Skip to content

Commit

Permalink
feat: uri open page
Browse files Browse the repository at this point in the history
  • Loading branch information
lisonge committed Sep 18, 2024
1 parent de14048 commit 128a6cd
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 149 deletions.
265 changes: 134 additions & 131 deletions app/src/main/kotlin/li/songe/gkd/debug/HttpService.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package li.songe.gkd.debug

import android.app.Service
import android.content.Context
import android.content.Intent
import com.blankj.utilcode.util.LogUtils
Expand Down Expand Up @@ -29,7 +30,6 @@ import kotlinx.coroutines.flow.first
import kotlinx.serialization.Serializable
import li.songe.gkd.app
import li.songe.gkd.appScope
import li.songe.gkd.composition.CompositionService
import li.songe.gkd.data.AppInfo
import li.songe.gkd.data.DeviceInfo
import li.songe.gkd.data.GkdAction
Expand Down Expand Up @@ -57,135 +57,43 @@ import li.songe.gkd.util.updateSubscription
import java.io.File


class HttpService : CompositionService({
val context = this
val scope = CoroutineScope(Dispatchers.IO)
class HttpService : Service() {
private val scope = CoroutineScope(Dispatchers.IO)

val httpSubsItem = SubsItem(
id = LOCAL_HTTP_SUBS_ID,
order = -1,
enableUpdate = false,
)

fun createServer(port: Int): CIOApplicationEngine {
return embeddedServer(CIO, port) {
install(KtorCorsPlugin)
install(KtorErrorPlugin)
install(ContentNegotiation) { json(keepNullJson) }

routing {
get("/") { call.respondText(ContentType.Text.Html) { "<script type='module' src='$SERVER_SCRIPT_URL'></script>" } }
route("/api") {
// Deprecated
get("/device") { call.respond(DeviceInfo.instance) }

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)
}
post("/getSnapshot") {
val data = call.receive<ReqId>()
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)
}
post("/getScreenshot") {
val data = call.receive<ReqId>()
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("/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 (!GkdAbService.isRunning.value) {
throw RpcError("无障碍没有运行")
}
val gkdAction = call.receive<GkdAction>()
call.respond(GkdAbService.execAction(gkdAction))
}
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() }
} catch (e: Exception) {
LogUtils.d("HTTP服务启动失败", e)
null
}
if (server == null) {
toast("HTTP服务启动失败,您可以尝试切换端口后重新启动")
stopSelf()
return@collect
}
createNotif(
this@HttpService,
httpChannel.id,
httpNotif.copy(text = "HTTP服务-端口$port")
)
}
}
}

var server: CIOApplicationEngine? = null
scope.launchTry(Dispatchers.IO) {
storeFlow.map(scope) { s -> s.httpServerPort }.collect { port ->
server?.stop()
server = try {
createServer(port).apply { start() }
} catch (e: Exception) {
LogUtils.d("HTTP服务启动失败", e)
null
}
if (server == null) {
toast("HTTP服务启动失败,您可以尝试切换端口后重新启动")
stopSelf()
return@collect
}
createNotif(
context, httpChannel.id, httpNotif.copy(text = "HTTP服务正在运行-端口$port")
)
LogUtils.d(*getIpAddressInLocalNetwork().map { host -> "http://${host}:${port}" }
.toList().toTypedArray())
}
}

override fun onDestroy() {
super.onDestroy()
isRunning.value = false
localNetworkIpsFlow.value = emptyList()

onDestroy {
scope.launchTry(Dispatchers.IO) {
server?.stop()
if (storeFlow.value.autoClearMemorySubs) {
Expand All @@ -196,13 +104,6 @@ class HttpService : CompositionService({
}
}

isRunning.value = true
localNetworkIpsFlow.value = getIpAddressInLocalNetwork()
onDestroy {
isRunning.value = false
localNetworkIpsFlow.value = emptyList()
}
}) {
companion object {
val isRunning = MutableStateFlow(false)
val localNetworkIpsFlow = MutableStateFlow(emptyList<String>())
Expand All @@ -213,8 +114,9 @@ class HttpService : CompositionService({
fun start(context: Context = app) {
context.startForegroundService(Intent(context, HttpService::class.java))
}

}

override fun onBind(intent: Intent?) = null
}

@Serializable
Expand Down Expand Up @@ -242,4 +144,105 @@ fun clearHttpSubs() {
deleteSubscription(LOCAL_HTTP_SUBS_ID)
}
}
}

private val httpSubsItem by lazy {
SubsItem(
id = LOCAL_HTTP_SUBS_ID,
order = -1,
enableUpdate = false,
)
}

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) { "<script type='module' src='$SERVER_SCRIPT_URL'></script>" } }
route("/api") {
// Deprecated
get("/device") { call.respond(DeviceInfo.instance) }

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)
}
post("/getSnapshot") {
val data = call.receive<ReqId>()
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)
}
post("/getScreenshot") {
val data = call.receive<ReqId>()
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("/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 (!GkdAbService.isRunning.value) {
throw RpcError("无障碍没有运行")
}
val gkdAction = call.receive<GkdAction>()
call.respond(GkdAbService.execAction(gkdAction))
}
}
}
}
}
5 changes: 5 additions & 0 deletions app/src/main/kotlin/li/songe/gkd/notif/Notif.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package li.songe.gkd.notif
import li.songe.gkd.app
import li.songe.gkd.util.SafeR


data class Notif(
val id: Int,
val smallIcon: Int = SafeR.ic_status,
val title: String = app.getString(SafeR.app_name),
val text: String,
val ongoing: Boolean,
val autoCancel: Boolean,
val uri: String? = null,
)

val abNotif by lazy {
Expand All @@ -27,6 +29,7 @@ val screenshotNotif by lazy {
text = "截屏服务正在运行",
ongoing = true,
autoCancel = false,
uri = "gkd://page/1",
)
}

Expand All @@ -36,6 +39,7 @@ val floatingNotif by lazy {
text = "悬浮窗按钮正在显示",
ongoing = true,
autoCancel = false,
uri = "gkd://page/1",
)
}

Expand All @@ -45,5 +49,6 @@ val httpNotif by lazy {
text = "HTTP服务正在运行",
ongoing = true,
autoCancel = false,
uri = "gkd://page/1",
)
}
25 changes: 15 additions & 10 deletions app/src/main/kotlin/li/songe/gkd/notif/NotifManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.ServiceInfo
import android.net.Uri
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
Expand All @@ -21,20 +22,24 @@ fun createChannel(context: Context, notifChannel: NotifChannel) {
}

fun createNotif(context: Service, channelId: String, notif: Notif) {
val intent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent = PendingIntent.getActivity(
context, notif.id, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
context,
0,
Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
notif.uri?.let { data = Uri.parse(it) }
},
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)

val builder = NotificationCompat.Builder(context, channelId)
val notification = NotificationCompat.Builder(context, channelId)
.setSmallIcon(notif.smallIcon)
.setContentTitle(notif.title).setContentText(notif.text).setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_DEFAULT).setOngoing(notif.ongoing)
.setContentTitle(notif.title)
.setContentText(notif.text)
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setOngoing(notif.ongoing)
.setAutoCancel(notif.autoCancel)

val notification = builder.build()
.build()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
context.startForeground(
notif.id, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST
Expand Down
Loading

0 comments on commit 128a6cd

Please sign in to comment.