From e2a289a4b05798bf6e74950b2b82715245ed8daa Mon Sep 17 00:00:00 2001 From: AskaEth Date: Sun, 16 Aug 2026 18:12:06 +0800 Subject: [PATCH] feat: add notification-access setting item with jump --- app/src/main/assets/h5/js/pages/settings.js | 24 ++++++++++++++++-- .../main/java/top/yeij/hearth/MainActivity.kt | 17 +++++++++++++ .../java/top/yeij/hearth/webview/JsBridge.kt | 25 +++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/app/src/main/assets/h5/js/pages/settings.js b/app/src/main/assets/h5/js/pages/settings.js index da8ccac..5944f30 100644 --- a/app/src/main/assets/h5/js/pages/settings.js +++ b/app/src/main/assets/h5/js/pages/settings.js @@ -1,5 +1,5 @@ -// 设置页:Miuix Preference 分组 + 玻璃条目(仅 UI 骨架,交互留后续) -// Settings page: Miuix Preference groups + glass items (UI skeleton only; interactions later) +// 设置页:Miuix Preference 分组 + 玻璃条目(通知使用权为可用交互项) +// Settings page: Miuix Preference groups + glass items (notification access is interactive) window.loadSettings = function () { const page = document.querySelector('[data-page="settings"]'); if (page.dataset.loaded) return; page.dataset.loaded = '1'; @@ -7,10 +7,30 @@ window.loadSettings = function () {
显示
跟随系统主题
屏幕亮度
+
权限
+
+ 通知使用权(媒体卡) + +
网络
webAPP 服务器地址
检查更新
关于
版本 0.1.0
`; + setupNotificationAccess(page); }; + +// 通知使用权授权项:异步查状态,未授权点击跳转系统授权页 +// Notification-access item: async state check, tap to open the system grant page +function setupNotificationAccess(page) { + const item = page.querySelector('#notif-access'); + const state = page.querySelector('#notif-access-state'); + bridge.call('getNotificationAccess').then(granted => { + state.textContent = granted ? '已开启 ✓' : '去开启 ›'; + if (granted) state.style.color = 'var(--accent)'; + }).catch(() => {}); + item.onclick = () => { + bridge.call('requestNotificationAccess').catch(() => {}); + }; +} diff --git a/app/src/main/java/top/yeij/hearth/MainActivity.kt b/app/src/main/java/top/yeij/hearth/MainActivity.kt index fcfd53f..ea5a112 100644 --- a/app/src/main/java/top/yeij/hearth/MainActivity.kt +++ b/app/src/main/java/top/yeij/hearth/MainActivity.kt @@ -1,8 +1,10 @@ package top.yeij.hearth import android.app.Activity +import android.content.Intent import android.content.res.Configuration import android.os.Bundle +import android.provider.Settings import android.text.TextUtils import android.util.Log import android.view.View @@ -24,6 +26,7 @@ import top.yeij.hearth.webapp.Tab import top.yeij.hearth.webapp.WebAppContainer import top.yeij.hearth.webapp.WebAppRepository import top.yeij.hearth.webview.JsBridge +import top.yeij.hearth.webview.NotificationAccessProvider import top.yeij.hearth.webview.WebAppHost import top.yeij.hearth.webview.WebViewManager import java.io.File @@ -47,6 +50,19 @@ class MainActivity : Activity() { private val webAppContainer = WebAppContainer() private val gson = Gson() private val webAppHost = WebAppHostImpl() + + // 通知使用权访问实现:检查授权状态 + 跳转系统授权页(供设置页授权项使用) + // Notification-access implementation: check grant state + jump to system settings + // (used by the settings permission item) + private val notificationAccess = object : NotificationAccessProvider { + override fun isGranted(): Boolean = + NotificationManagerCompat.getEnabledListenerPackages(this@MainActivity) + .contains(packageName) + + override fun requestAccess() { + startActivity(Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)) + } + } private lateinit var mediaSource: MediaSessionSource private lateinit var root: FrameLayout private lateinit var desktopWebView: WebView @@ -91,6 +107,7 @@ class MainActivity : Activity() { webAppContainer = webAppContainer, webAppHost = webAppHost, postToMainThread = { desktopWebView.post(it) }, + notificationAccess = notificationAccess, ) root = FrameLayout(this) setContentView(root) diff --git a/app/src/main/java/top/yeij/hearth/webview/JsBridge.kt b/app/src/main/java/top/yeij/hearth/webview/JsBridge.kt index 5bd9c9a..738e39d 100644 --- a/app/src/main/java/top/yeij/hearth/webview/JsBridge.kt +++ b/app/src/main/java/top/yeij/hearth/webview/JsBridge.kt @@ -10,6 +10,15 @@ import top.yeij.hearth.webapp.WebApp import top.yeij.hearth.webapp.WebAppContainer import top.yeij.hearth.webapp.WebAppRepository +// 通知使用权访问接口:由 MainActivity 实现,JsBridge 通过它检查/申请授权, +// 保持 JsBridge 可 JVM 单测(不直接依赖 Context/NotificationManagerCompat) +// Notification-access provider implemented by MainActivity; JsBridge uses it to +// check/request access, keeping JsBridge JVM-testable (no direct Context dependency) +interface NotificationAccessProvider { + fun isGranted(): Boolean + fun requestAccess() +} + class JsBridge( private val deviceWidthPx: Int, private val deviceHeightPx: Int, @@ -23,6 +32,9 @@ class JsBridge( // 主线程执行器:View 操作必须切到主线程;null(单测)时同步执行 // Main-thread executor: view ops must run on main; null (unit tests) runs inline private val postToMainThread: ((() -> Unit) -> Unit)? = null, + // 通知使用权访问接口(检查/申请授权),供设置页授权项使用 + // Notification-access provider (check/request) for the settings permission item + private val notificationAccess: NotificationAccessProvider? = null, ) { private val gson = com.google.gson.Gson() @@ -185,6 +197,19 @@ class JsBridge( return gson.toJson(cards) } + // 返回通知使用权是否已授权(未接入返回 false) + // Return whether notification access is granted (false when not wired) + @android.webkit.JavascriptInterface + fun getNotificationAccess(): Boolean = notificationAccess?.isGranted() ?: false + + // 跳转系统「通知使用权」设置页,让用户为媒体卡授权 + // Jump to the system notification-access settings page for media-card grant + @android.webkit.JavascriptInterface + fun requestNotificationAccess() { + Log.d("HearthBridge", "requestNotificationAccess called") + notificationAccess?.requestAccess() + } + // 注册媒体会话监听,回调推送给 H5(info 为 null 时推 "null") // Register media session listener; push callbacks to H5 (push "null" when info is null) fun setMediaListener(source: MediaSessionSource, webView: android.webkit.WebView) {