diff --git a/app/src/main/kotlin/li/songe/gkd/debug/FloatingService.kt b/app/src/main/kotlin/li/songe/gkd/debug/FloatingService.kt index 22f9ff9..b864cb7 100644 --- a/app/src/main/kotlin/li/songe/gkd/debug/FloatingService.kt +++ b/app/src/main/kotlin/li/songe/gkd/debug/FloatingService.kt @@ -17,9 +17,8 @@ import kotlinx.coroutines.flow.MutableStateFlow import li.songe.gkd.app import li.songe.gkd.appScope import li.songe.gkd.data.Tuple3 -import li.songe.gkd.notif.createNotif -import li.songe.gkd.notif.floatingChannel import li.songe.gkd.notif.floatingNotif +import li.songe.gkd.notif.notifyService import li.songe.gkd.util.launchTry import kotlin.math.sqrt @@ -79,7 +78,7 @@ class FloatingService : ExpandableBubbleService() { override fun startNotificationForeground() { - createNotif(this, floatingChannel.id, floatingNotif) + floatingNotif.notifyService(this) } override fun onDestroy() { 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 16a4b8a..8ff864c 100644 --- a/app/src/main/kotlin/li/songe/gkd/debug/HttpService.kt +++ b/app/src/main/kotlin/li/songe/gkd/debug/HttpService.kt @@ -39,9 +39,8 @@ import li.songe.gkd.data.deleteSubscription import li.songe.gkd.data.selfAppInfo import li.songe.gkd.db.DbSet import li.songe.gkd.debug.SnapshotExt.captureSnapshot -import li.songe.gkd.notif.createNotif -import li.songe.gkd.notif.httpChannel import li.songe.gkd.notif.httpNotif +import li.songe.gkd.notif.notifyService import li.songe.gkd.service.A11yService import li.songe.gkd.util.LOCAL_HTTP_SUBS_ID import li.songe.gkd.util.SERVER_SCRIPT_URL @@ -79,11 +78,7 @@ class HttpService : Service() { stopSelf() return@collect } - createNotif( - this@HttpService, - httpChannel.id, - httpNotif.copy(text = "HTTP服务-$port") - ) + httpNotif.copy(text = "HTTP服务-$port").notifyService(this@HttpService) } } } diff --git a/app/src/main/kotlin/li/songe/gkd/debug/ScreenshotService.kt b/app/src/main/kotlin/li/songe/gkd/debug/ScreenshotService.kt index 9c82331..27756d3 100644 --- a/app/src/main/kotlin/li/songe/gkd/debug/ScreenshotService.kt +++ b/app/src/main/kotlin/li/songe/gkd/debug/ScreenshotService.kt @@ -8,8 +8,7 @@ import android.content.Intent import com.blankj.utilcode.util.LogUtils import kotlinx.coroutines.flow.MutableStateFlow import li.songe.gkd.app -import li.songe.gkd.notif.createNotif -import li.songe.gkd.notif.screenshotChannel +import li.songe.gkd.notif.notifyService import li.songe.gkd.notif.screenshotNotif import li.songe.gkd.util.ScreenshotUtil @@ -19,7 +18,7 @@ class ScreenshotService : Service() { override fun onCreate() { super.onCreate() isRunning.value = true - createNotif(this, screenshotChannel.id, screenshotNotif) + screenshotNotif.notifyService(this) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { diff --git a/app/src/main/kotlin/li/songe/gkd/debug/SnapshotExt.kt b/app/src/main/kotlin/li/songe/gkd/debug/SnapshotExt.kt index ccbc3bb..4dce4a4 100644 --- a/app/src/main/kotlin/li/songe/gkd/debug/SnapshotExt.kt +++ b/app/src/main/kotlin/li/songe/gkd/debug/SnapshotExt.kt @@ -18,6 +18,8 @@ import li.songe.gkd.data.RpcError import li.songe.gkd.data.createComplexSnapshot import li.songe.gkd.data.toSnapshot import li.songe.gkd.db.DbSet +import li.songe.gkd.notif.notify +import li.songe.gkd.notif.snapshotNotif import li.songe.gkd.service.A11yService import li.songe.gkd.util.appInfoCacheFlow import li.songe.gkd.util.keepNullJson @@ -142,6 +144,14 @@ object SnapshotExt { DbSet.snapshotDao.insert(snapshot.toSnapshot()) } toast("快照成功") + val desc = snapshot.appInfo?.name ?: snapshot.appId + snapshotNotif.copy( + text = if (desc != null) { + "快照[$desc]已保存至记录" + } else { + snapshotNotif.text + } + ).notify() return snapshot } finally { captureLoading.value = false diff --git a/app/src/main/kotlin/li/songe/gkd/notif/Notif.kt b/app/src/main/kotlin/li/songe/gkd/notif/Notif.kt index 51a48d5..71ff467 100644 --- a/app/src/main/kotlin/li/songe/gkd/notif/Notif.kt +++ b/app/src/main/kotlin/li/songe/gkd/notif/Notif.kt @@ -5,6 +5,7 @@ import li.songe.gkd.util.SafeR data class Notif( + val channel: NotifChannel, val id: Int, val smallIcon: Int = SafeR.ic_status, val title: String = app.getString(SafeR.app_name), @@ -16,6 +17,7 @@ data class Notif( val abNotif by lazy { Notif( + channel = defaultChannel, id = 100, text = "无障碍正在运行", ongoing = true, @@ -25,6 +27,7 @@ val abNotif by lazy { val screenshotNotif by lazy { Notif( + channel = screenshotChannel, id = 101, text = "截屏服务正在运行", ongoing = true, @@ -35,6 +38,7 @@ val screenshotNotif by lazy { val floatingNotif by lazy { Notif( + channel = floatingChannel, id = 102, text = "悬浮窗按钮正在显示", ongoing = true, @@ -45,10 +49,22 @@ val floatingNotif by lazy { val httpNotif by lazy { Notif( + channel = httpChannel, id = 103, text = "HTTP服务正在运行", ongoing = true, autoCancel = false, uri = "gkd://page/1", ) +} + +val snapshotNotif by lazy { + Notif( + channel = snapshotChannel, + id = 104, + text = "快照已保存至记录", + ongoing = false, + autoCancel = true, + uri = "gkd://page/2", + ) } \ No newline at end of file diff --git a/app/src/main/kotlin/li/songe/gkd/notif/NotifChannel.kt b/app/src/main/kotlin/li/songe/gkd/notif/NotifChannel.kt index 15ccef8..588d053 100644 --- a/app/src/main/kotlin/li/songe/gkd/notif/NotifChannel.kt +++ b/app/src/main/kotlin/li/songe/gkd/notif/NotifChannel.kt @@ -29,10 +29,16 @@ val httpChannel by lazy { id = "http", name = "HTTP服务", desc = "用于连接Web端工具调试" ) } +val snapshotChannel by lazy { + NotifChannel( + id = "snapshot", name = "快照通知", desc = "捕获快照后发出通知" + ) +} fun initChannel() { createChannel(app, defaultChannel) createChannel(app, floatingChannel) createChannel(app, screenshotChannel) createChannel(app, httpChannel) + createChannel(app, snapshotChannel) } \ No newline at end of file diff --git a/app/src/main/kotlin/li/songe/gkd/notif/NotifManager.kt b/app/src/main/kotlin/li/songe/gkd/notif/NotifManager.kt index fc59e58..e1e41a8 100644 --- a/app/src/main/kotlin/li/songe/gkd/notif/NotifManager.kt +++ b/app/src/main/kotlin/li/songe/gkd/notif/NotifManager.kt @@ -1,5 +1,7 @@ package li.songe.gkd.notif +import android.annotation.SuppressLint +import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent @@ -12,6 +14,8 @@ import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import li.songe.gkd.MainActivity +import li.songe.gkd.app +import li.songe.gkd.permission.notificationState fun createChannel(context: Context, notifChannel: NotifChannel) { val importance = NotificationManager.IMPORTANCE_LOW @@ -21,30 +25,41 @@ fun createChannel(context: Context, notifChannel: NotifChannel) { notificationManager.createNotificationChannel(channel) } -fun createNotif(context: Service, channelId: String, notif: Notif) { +private fun Notif.toNotification(): Notification { val pendingIntent = PendingIntent.getActivity( - context, + app, 0, - Intent(context, MainActivity::class.java).apply { + Intent(app, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - notif.uri?.let { data = Uri.parse(it) } + uri?.let { data = Uri.parse(it) } }, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) - val notification = NotificationCompat.Builder(context, channelId) - .setSmallIcon(notif.smallIcon) - .setContentTitle(notif.title) - .setContentText(notif.text) + val notification = NotificationCompat.Builder(app, channel.id) + .setSmallIcon(smallIcon) + .setContentTitle(title) + .setContentText(text) .setContentIntent(pendingIntent) - .setPriority(NotificationCompat.PRIORITY_DEFAULT) - .setOngoing(notif.ongoing) - .setAutoCancel(notif.autoCancel) + .setOngoing(ongoing) + .setAutoCancel(autoCancel) .build() + return notification +} + +@SuppressLint("MissingPermission") +fun Notif.notify() { + if (notificationState.updateAndGet()) { + NotificationManagerCompat.from(app).notify(id, toNotification()) + } +} + +fun Notif.notifyService(context: Service) { + val notification = toNotification() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { context.startForeground( - notif.id, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST + id, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST ) } else { - context.startForeground(notif.id, notification) + context.startForeground(id, notification) } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/li/songe/gkd/permission/PermissionState.kt b/app/src/main/kotlin/li/songe/gkd/permission/PermissionState.kt index 060c7c8..c5f3935 100644 --- a/app/src/main/kotlin/li/songe/gkd/permission/PermissionState.kt +++ b/app/src/main/kotlin/li/songe/gkd/permission/PermissionState.kt @@ -70,7 +70,7 @@ private suspend fun asyncRequestPermission( val notificationState by lazy { PermissionState( check = { - XXPermissions.isGranted(app, Permission.NOTIFICATION_SERVICE) + XXPermissions.isGranted(app, Permission.POST_NOTIFICATIONS) }, request = { asyncRequestPermission(it, Permission.POST_NOTIFICATIONS) diff --git a/app/src/main/kotlin/li/songe/gkd/service/ManageService.kt b/app/src/main/kotlin/li/songe/gkd/service/ManageService.kt index 4c602b0..81ede1c 100644 --- a/app/src/main/kotlin/li/songe/gkd/service/ManageService.kt +++ b/app/src/main/kotlin/li/songe/gkd/service/ManageService.kt @@ -12,8 +12,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import li.songe.gkd.app import li.songe.gkd.notif.abNotif -import li.songe.gkd.notif.createNotif -import li.songe.gkd.notif.defaultChannel +import li.songe.gkd.notif.notifyService import li.songe.gkd.permission.notificationState import li.songe.gkd.util.actionCountFlow import li.songe.gkd.util.getSubsStatus @@ -27,7 +26,7 @@ class ManageService : Service() { override fun onCreate() { super.onCreate() isRunning.value = true - createNotif(this, defaultChannel.id, abNotif) + abNotif.notifyService(this) scope.launch { combine( A11yService.isRunning, @@ -46,11 +45,7 @@ class ManageService : Service() { } return@combine getSubsStatus(ruleSummary, count) }.debounce(500L).stateIn(scope, SharingStarted.Eagerly, "").collect { text -> - createNotif( - this@ManageService, - defaultChannel.id, - abNotif.copy(text = text) - ) + abNotif.copy(text = text).notifyService(this@ManageService) } } } diff --git a/app/src/main/kotlin/li/songe/gkd/ui/home/HomePage.kt b/app/src/main/kotlin/li/songe/gkd/ui/home/HomePage.kt index e6d5064..cc6bcb7 100644 --- a/app/src/main/kotlin/li/songe/gkd/ui/home/HomePage.kt +++ b/app/src/main/kotlin/li/songe/gkd/ui/home/HomePage.kt @@ -18,6 +18,7 @@ import com.blankj.utilcode.util.LogUtils import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.generated.destinations.AdvancedPageDestination +import com.ramcosta.composedestinations.generated.destinations.SnapshotPageDestination import com.ramcosta.composedestinations.utils.toDestinationsNavigator import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -69,6 +70,10 @@ fun HomePage() { "/1" -> { navController.toDestinationsNavigator().navigate(AdvancedPageDestination) } + + "/2" -> { + navController.toDestinationsNavigator().navigate(SnapshotPageDestination) + } } } }) @@ -100,4 +105,4 @@ fun HomePage() { }, content = currentPage.content ) -} \ No newline at end of file +}