diff --git a/app/src/main/kotlin/li/songe/gkd/debug/HttpService.kt b/app/src/main/kotlin/li/songe/gkd/debug/HttpService.kt index c0085d8..ccaa932 100644 --- a/app/src/main/kotlin/li/songe/gkd/debug/HttpService.kt +++ b/app/src/main/kotlin/li/songe/gkd/debug/HttpService.kt @@ -5,10 +5,10 @@ import android.content.Intent import com.blankj.utilcode.util.LogUtils import io.ktor.http.ContentType import io.ktor.serialization.kotlinx.json.json -import io.ktor.server.application.call import io.ktor.server.application.install import io.ktor.server.cio.CIO import io.ktor.server.cio.CIOApplicationEngine +import io.ktor.server.engine.EmbeddedServer import io.ktor.server.engine.embeddedServer import io.ktor.server.plugins.contentnegotiation.ContentNegotiation import io.ktor.server.request.receive @@ -22,6 +22,7 @@ import io.ktor.server.routing.route import io.ktor.server.routing.routing import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.MainScope import kotlinx.coroutines.cancel import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow @@ -57,19 +58,21 @@ import java.io.File class HttpService : Service() { - private val scope = CoroutineScope(Dispatchers.Default) + private val scope = MainScope() + private val httpServerPortFlow = storeFlow.map(scope) { s -> s.httpServerPort } + + private var server: EmbeddedServer? = + null - private var server: CIOApplicationEngine? = null override fun onCreate() { super.onCreate() isRunning.value = true localNetworkIpsFlow.value = getIpAddressInLocalNetwork() - val httpServerPortFlow = storeFlow.map(scope) { s -> s.httpServerPort } scope.launchTry(Dispatchers.IO) { httpServerPortFlow.collect { port -> server?.stop() server = try { - createServer(port).apply { start() } + scope.createServer(port).apply { start() } } catch (e: Exception) { LogUtils.d("HTTP服务启动失败", e) null @@ -86,16 +89,11 @@ class HttpService : Service() { override fun onDestroy() { super.onDestroy() + scope.cancel() isRunning.value = false localNetworkIpsFlow.value = emptyList() - - scope.launchTry(Dispatchers.IO) { - server?.stop() - if (storeFlow.value.autoClearMemorySubs) { - deleteSubscription(LOCAL_HTTP_SUBS_ID) - } - delay(3000) - scope.cancel() + if (storeFlow.value.autoClearMemorySubs) { + deleteSubscription(LOCAL_HTTP_SUBS_ID) } } @@ -150,95 +148,93 @@ private val httpSubsItem by lazy { ) } -private fun createServer(port: Int): CIOApplicationEngine { - return embeddedServer(CIO, port) { - install(KtorCorsPlugin) - install(KtorErrorPlugin) - install(ContentNegotiation) { json(keepNullJson) } - routing { - get("/") { call.respondText(ContentType.Text.Html) { "" } } - route("/api") { - // Deprecated - get("/device") { call.respond(DeviceInfo.instance) } +private fun CoroutineScope.createServer(port: Int) = embeddedServer(CIO, port) { + install(KtorCorsPlugin) + install(KtorErrorPlugin) + install(ContentNegotiation) { json(keepNullJson) } + routing { + get("/") { call.respondText(ContentType.Text.Html) { "" } } + route("/api") { + // Deprecated + get("/device") { call.respond(DeviceInfo.instance) } - post("/getServerInfo") { call.respond(ServerInfo()) } + post("/getServerInfo") { call.respond(ServerInfo()) } - // Deprecated - get("/snapshot") { - val id = call.request.queryParameters["id"]?.toLongOrNull() - ?: throw RpcError("miss id") - val fp = File(SnapshotExt.getSnapshotPath(id)) - if (!fp.exists()) { - throw RpcError("对应快照不存在") - } - call.respondFile(fp) + // Deprecated + get("/snapshot") { + val id = call.request.queryParameters["id"]?.toLongOrNull() + ?: throw RpcError("miss id") + val fp = File(SnapshotExt.getSnapshotPath(id)) + if (!fp.exists()) { + throw RpcError("对应快照不存在") } - post("/getSnapshot") { - val data = call.receive() - val fp = File(SnapshotExt.getSnapshotPath(data.id)) - if (!fp.exists()) { - throw RpcError("对应快照不存在") - } - call.respond(fp) + call.respondFile(fp) + } + post("/getSnapshot") { + val data = call.receive() + val fp = File(SnapshotExt.getSnapshotPath(data.id)) + if (!fp.exists()) { + throw RpcError("对应快照不存在") } + call.respond(fp) + } - // Deprecated - get("/screenshot") { - val id = call.request.queryParameters["id"]?.toLongOrNull() - ?: throw RpcError("miss id") - val fp = File(SnapshotExt.getScreenshotPath(id)) - if (!fp.exists()) { - throw RpcError("对应截图不存在") - } - call.respondFile(fp) + // Deprecated + get("/screenshot") { + val id = call.request.queryParameters["id"]?.toLongOrNull() + ?: throw RpcError("miss id") + val fp = File(SnapshotExt.getScreenshotPath(id)) + if (!fp.exists()) { + throw RpcError("对应截图不存在") } - post("/getScreenshot") { - val data = call.receive() - val fp = File(SnapshotExt.getScreenshotPath(data.id)) - if (!fp.exists()) { - throw RpcError("对应截图不存在") - } - call.respondFile(fp) + call.respondFile(fp) + } + post("/getScreenshot") { + val data = call.receive() + val fp = File(SnapshotExt.getScreenshotPath(data.id)) + if (!fp.exists()) { + throw RpcError("对应截图不存在") } + call.respondFile(fp) + } - // Deprecated - get("/captureSnapshot") { - call.respond(captureSnapshot()) - } - post("/captureSnapshot") { - call.respond(captureSnapshot()) - } + // Deprecated + get("/captureSnapshot") { + call.respond(captureSnapshot()) + } + post("/captureSnapshot") { + call.respond(captureSnapshot()) + } - // Deprecated - get("/snapshots") { - call.respond(DbSet.snapshotDao.query().first()) - } - post("/getSnapshots") { - call.respond(DbSet.snapshotDao.query().first()) - } + // Deprecated + get("/snapshots") { + call.respond(DbSet.snapshotDao.query().first()) + } + post("/getSnapshots") { + call.respond(DbSet.snapshotDao.query().first()) + } - post("/updateSubscription") { - val subscription = - RawSubscription.parse(call.receiveText(), json5 = false) - .copy( - id = LOCAL_HTTP_SUBS_ID, - name = "内存订阅", - version = 0, - author = "@gkd-kit/inspect" - ) - updateSubscription(subscription) - DbSet.subsItemDao.insert((subsItemsFlow.value.find { s -> s.id == httpSubsItem.id } - ?: httpSubsItem).copy(mtime = System.currentTimeMillis())) - call.respond(RpcOk()) - } - post("/execSelector") { - if (!A11yService.isRunning.value) { - throw RpcError("无障碍没有运行") - } - val gkdAction = call.receive() - call.respond(A11yService.execAction(gkdAction)) + post("/updateSubscription") { + val subscription = + RawSubscription.parse(call.receiveText(), json5 = false) + .copy( + id = LOCAL_HTTP_SUBS_ID, + name = "内存订阅", + version = 0, + author = "@gkd-kit/inspect" + ) + updateSubscription(subscription) + DbSet.subsItemDao.insert((subsItemsFlow.value.find { s -> s.id == httpSubsItem.id } + ?: httpSubsItem).copy(mtime = System.currentTimeMillis())) + call.respond(RpcOk()) + } + post("/execSelector") { + if (!A11yService.isRunning.value) { + throw RpcError("无障碍没有运行") } + val gkdAction = call.receive() + call.respond(A11yService.execAction(gkdAction)) } } } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/li/songe/gkd/util/Github.kt b/app/src/main/kotlin/li/songe/gkd/util/Github.kt index fa8008a..39fe39b 100644 --- a/app/src/main/kotlin/li/songe/gkd/util/Github.kt +++ b/app/src/main/kotlin/li/songe/gkd/util/Github.kt @@ -84,6 +84,7 @@ suspend fun uploadFileToGithub( })) }.body() + val byteArray = file.readBytes() // upload to s3 client.post(policiesResp.upload_url) { setCommonHeaders(cookie) @@ -91,13 +92,13 @@ suspend fun uploadFileToGithub( policiesResp.form.forEach { (key, value) -> append(key, value) } - append("file", file.readBytes(), Headers.build { + append("file", byteArray, Headers.build { append(HttpHeaders.ContentType, "application/x-zip-compressed") append(HttpHeaders.ContentDisposition, "filename=\"file.zip\"") }) })) onUpload { bytesSentTotal, contentLength -> - listener(bytesSentTotal / contentLength.toFloat()) + listener(bytesSentTotal / (contentLength ?: byteArray.size).toFloat()) } } diff --git a/app/src/main/kotlin/li/songe/gkd/util/Upgrade.kt b/app/src/main/kotlin/li/songe/gkd/util/Upgrade.kt index b63aa68..d51cd69 100644 --- a/app/src/main/kotlin/li/songe/gkd/util/Upgrade.kt +++ b/app/src/main/kotlin/li/songe/gkd/util/Upgrade.kt @@ -44,8 +44,8 @@ data class NewVersion( val versionName: String, val changelog: String, val downloadUrl: String, + val fileSize: Long, val versionLogs: List = emptyList(), - val fileSize: Long? = null, ) @Serializable @@ -97,12 +97,12 @@ private fun UpdateStatus.startDownload(viewModel: MainViewModel, newVersion: New job = viewModel.viewModelScope.launch(Dispatchers.IO) { try { val channel = client.get(URI(UPDATE_URL).resolve(newVersion.downloadUrl).toString()) { - onDownload { bytesSentTotal, contentLength -> + onDownload { bytesSentTotal, _ -> // contentLength 在某些机型上概率错误 val downloadStatus = downloadStatusFlow.value if (downloadStatus is LoadStatus.Loading) { downloadStatusFlow.value = LoadStatus.Loading( - bytesSentTotal.toFloat() / (newVersion.fileSize ?: contentLength) + bytesSentTotal.toFloat() / (newVersion.fileSize) ) } else if (downloadStatus is LoadStatus.Failure) { // 提前终止下载 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d988e1d..7eda136 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,12 +1,12 @@ [versions] -kotlin = "2.0.20" -ksp = "2.0.20-1.0.25" +kotlin = "2.0.21" +ksp = "2.0.21-RC-1.0.25" android = "8.7.0" compose = "1.7.3" rikka = "4.4.0" room = "2.6.1" paging = "3.3.2" -ktor = "2.3.12" +ktor = "3.0.0" destinations = "2.1.0-beta12" coil = "2.7.0" shizuku = "13.1.5"