Skip to content

Commit

Permalink
升级ktor,增加sse 测试,修复新添加的内容无法显示的问题,移除thymeleaf
Browse files Browse the repository at this point in the history
  • Loading branch information
storytellerF committed Aug 10, 2024
1 parent 72bfc35 commit 99ce444
Show file tree
Hide file tree
Showing 19 changed files with 355 additions and 284 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
local.properties
/.idea/inspectionProfiles/Project_Default.xml
/.idea/deploymentTargetDropDown.xml
.kotlin
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.logging.Logging
import io.ktor.client.plugins.sse.SSE
import io.ktor.client.plugins.sse.sseSession
import io.ktor.client.request.forms.submitForm
import io.ktor.client.request.get
import io.ktor.client.request.headers
Expand All @@ -22,6 +24,11 @@ import io.ktor.http.parameters
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertTrue
Expand Down Expand Up @@ -100,44 +107,46 @@ class ExampleInstrumentedTest {
}
}

// @Test
// fun testSSE() {
// println("testSSE--")
// useSSE { fei, sseClient ->
// println("testSSE-- blocking")
// val deferred = async {
// sseClient.sseSession(urlString = "http://${FeiService.LISTENER_ADDRESS}:${FeiService.DEFAULT_PORT}/sse").incoming.first {
// println("testSSE-- receive $it")
// it.event == "refresh"
// true
// }
// }
// println("testSSE-- add uri")
// fei.appendUri("file:///test.zip".toUri())
// deferred.await()
// }
// }
@Test
fun testSSE() {
useSSE { fei, sseClient ->
val deferred = async {
val session = sseClient.sseSession(urlString = "http://${FeiService.LISTENER_ADDRESS}:${FeiService.DEFAULT_PORT}/sse")
try {
session.incoming.first {
it.data == "refresh"
}
session.cancel()
} catch (e: Exception) {
session.cancel()
}
}
launch {
delay(1000)
fei.appendUri("file:///test.zip".toUri())
}
deferred.await()
}
}

// private fun useSSE(block: suspend CoroutineScope.(FeiService.Fei, sseClient: HttpClient) -> Unit) {
// useClient { fei, _, _, cookie ->
// println("testSSE-- client sse")
// HttpClient {
// install(SSE) {
// showCommentEvents()
// showRetryEvents()
// }
// install(Logging)
// defaultRequest {
// headers {
// append("cookie", cookie)
// }
// }
// }.use {
// println("testSSE-- block")
// block(fei, it)
// }
// }
// }
private fun useSSE(block: suspend CoroutineScope.(FeiService.Fei, sseClient: HttpClient) -> Unit) {
useClient { fei, _, _, cookie ->
HttpClient {
install(SSE) {
showCommentEvents()
showRetryEvents()
}
install(Logging)
defaultRequest {
headers {
append("cookie", cookie)
}
}
}.use {
block(fei, it)
}
}
}

private fun useService(block: (FeiService.Fei, FeiService, FeiServer) -> Unit) {
val serviceIntent = Intent(
Expand Down Expand Up @@ -176,7 +185,6 @@ class ExampleInstrumentedTest {
append("password", "")
}).headers["set-cookie"]!!
cookieMap["cookie"] = cookie
println("testSSE-- cookie $cookie")
block(fei, it, service, cookie)
}
}
Expand Down
68 changes: 31 additions & 37 deletions app/src/main/java/com/storyteller_f/fei/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.DrawerState
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
Expand Down Expand Up @@ -62,7 +61,6 @@ import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.NavType
Expand All @@ -83,15 +81,14 @@ import com.storyteller_f.fei.service.portFlow
import com.storyteller_f.fei.ui.components.ComposeBluetoothDevice
import com.storyteller_f.fei.ui.components.FeiMainToolbar
import com.storyteller_f.fei.ui.components.HidScreen
import com.storyteller_f.fei.ui.components.Main
import com.storyteller_f.fei.ui.components.SharedFiles
import com.storyteller_f.fei.ui.components.MessagePage
import com.storyteller_f.fei.ui.components.NavDrawer
import com.storyteller_f.fei.ui.components.SafePage
import com.storyteller_f.fei.ui.components.SettingPage
import com.storyteller_f.fei.ui.components.SharedFile
import com.storyteller_f.fei.ui.components.ShowQrCode
import com.storyteller_f.fei.ui.theme.FeiTheme
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flatMapLatest
Expand All @@ -113,7 +110,10 @@ sealed class HidState {
class Done(val device: ComposeBluetoothDevice) : HidState()
}


class MainActivity : ComponentActivity() {
private val fei = MutableStateFlow<FeiService.Fei?>(null)
private val currentFeiBinder get() = fei.value
private val pickFile =
registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { uri ->
addUri(uri)
Expand All @@ -130,8 +130,6 @@ class MainActivity : ComponentActivity() {
NoOpBluetoothFei()
}
}
private val fei = MutableStateFlow<FeiService.Fei?>(null)
private val currentFeiBinder get() = fei.value

private val requestNotificationPermission = registerForActivityResult(
ActivityResultContracts.RequestPermission()
Expand Down Expand Up @@ -203,12 +201,22 @@ class MainActivity : ComponentActivity() {

FeiTheme {
ModalNavigationDrawer(drawerContent = {
Drawer(scope, drawerState, navController, showAboutWebView)
Drawer(showAboutWebView, {
scope.launch {
drawerState.close()
}
}) {
navController.navigate(it)
}
}, drawerState = drawerState) {
Scaffold(topBar = {
TopBar(port, sendText, scope, drawerState)
TopBar(port, sendText) {
scope.launch {
drawerState.open()
}
}
}, floatingActionButton = {
Floating(currentBackStackEntryAsState)
Floating(currentBackStackEntryAsState?.destination?.route.orEmpty())
}, snackbarHost = {
SnackbarHost(hostState = snackBarHostState) { }
}) { paddingValues ->
Expand Down Expand Up @@ -265,7 +273,6 @@ class MainActivity : ComponentActivity() {
sendText: (String) -> Unit,
state: HidState
) {
val infoList by shares.collectAsState()
Surface(
modifier = Modifier
.fillMaxSize()
Expand All @@ -274,22 +281,19 @@ class MainActivity : ComponentActivity() {
) {
NavHost(navController = navController, startDestination = "main") {
navPages(
infoList,
navController,
port,
state,
::saveToLocal,
::deleteItem,
sendText,
::requestPermission
)
) { i -> navController.navigate("info/$i") }
}
}
}

@Composable
private fun Floating(currentBackStackEntryAsState: NavBackStackEntry?) {
val text = currentBackStackEntryAsState?.destination?.route.orEmpty()
private fun Floating(text: String) {
if (text != "messages")
FloatingActionButton(onClick = {
pickFile.launch(arrayOf("*/*"))
Expand All @@ -303,40 +307,30 @@ class MainActivity : ComponentActivity() {

@Composable
private fun Drawer(
scope: CoroutineScope,
drawerState: DrawerState,
navController: NavHostController,
showAboutWebView: () -> Unit
showAboutWebView: () -> Unit,
closeDrawer: () -> Unit,
navigateTo: (String) -> Unit
) {
ModalDrawerSheet {
Spacer(Modifier.height(12.dp))
NavDrawer({
scope.launch {
drawerState.close()
}
}, {
navController.navigate(it)
}, showAboutWebView)
closeDrawer()
}, navigateTo, showAboutWebView)
}
}

@Composable
private fun TopBar(
port: Int,
sendText: (String) -> Unit,
scope: CoroutineScope,
drawerState: DrawerState
openDrawer: () -> Unit
) {
FeiMainToolbar(
port.toString(),
{ currentFeiBinder?.restart() },
{ currentFeiBinder?.stop() },
sendText,
{
scope.launch {
drawerState.open()
}
},
openDrawer,
{
shares.value.forEach(::deleteItem)
},
Expand Down Expand Up @@ -364,19 +358,19 @@ class MainActivity : ComponentActivity() {
}

private fun NavGraphBuilder.navPages(
infoList: List<SharedFileInfo>,
navController: NavHostController,
port: Int,
state: HidState,
saveToLocal: (SharedFileInfo) -> Unit,
deleteItem: (SharedFileInfo) -> Unit,
sendText: (String) -> Unit,
requestPermission: () -> Unit
requestPermission: () -> Unit,
navigateToInfo: (Int) -> Unit
) {
composable("main") {
Main(infoList, deleteItem, saveToLocal) {
val infoList by shares.collectAsState()
SharedFiles(infoList, deleteItem, saveToLocal) {
val i = shares.value.indexOf(it)
navController.navigate("info/$i")
navigateToInfo(i)
}
}
composable("info/{index}", arguments = listOf(navArgument("index") {
Expand Down
12 changes: 5 additions & 7 deletions app/src/main/java/com/storyteller_f/fei/Shares.kt
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,14 @@ val mutex = Mutex()

suspend fun Context.cacheInvalid() = withContext(Dispatchers.IO) {
mutex.withLock {
cacheInvalidInternal()
val savedList = savedList()
val savedFiles = savedFiles()
val value = savedFiles + savedList
println(value.map { it.uri })
shares.emit(value)
}
}

private suspend fun Context.cacheInvalidInternal(): Boolean {
val savedList = savedList()
val savedFiles = savedFiles()
return shares.tryEmit(savedFiles + savedList)
}

private suspend fun Context.savedFiles(): List<SharedFileInfo> = withContext(Dispatchers.IO) {
val savedFiles = File(filesDir, "saved").listFiles()?.let {
it.map { file ->
Expand Down
22 changes: 12 additions & 10 deletions app/src/main/java/com/storyteller_f/fei/UriFileContent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ import android.os.Build
import android.os.ParcelFileDescriptor
import android.provider.DocumentsContract
import android.util.Log
import com.storyteller_f.fei.service.FeiServer
import io.ktor.http.*
import io.ktor.http.content.*
import io.ktor.server.application.*
import io.ktor.server.http.content.*
import io.ktor.server.response.*
import io.ktor.util.*
import io.ktor.util.cio.*
import io.ktor.utils.io.*
import io.ktor.utils.io.core.*
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.http.content.OutgoingContent
import io.ktor.http.content.versions
import io.ktor.server.application.ApplicationCall
import io.ktor.server.http.content.LastModifiedVersion
import io.ktor.server.response.respond
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.core.use
import io.ktor.utils.io.jvm.nio.writeSuspendSession
import io.ktor.utils.io.jvm.nio.writeWhile
import io.ktor.utils.io.writer
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down
Loading

0 comments on commit 99ce444

Please sign in to comment.