diff --git a/app/src/main/assets/h5/css/app.css b/app/src/main/assets/h5/css/app.css index 84ac6a6..701e32f 100644 --- a/app/src/main/assets/h5/css/app.css +++ b/app/src/main/assets/h5/css/app.css @@ -1,7 +1,14 @@ /* Hearth 桌面样式:侧边栏 + 5 页框架 + 柔光玻璃容器 */ /* Hearth launcher styles: sidebar + 5-page frame + soft-glass containers */ -* { margin: 0; padding: 0; box-sizing: border-box; } +* { + margin: 0; + padding: 0; + box-sizing: border-box; + /* 移除 WebView 默认蓝色点击高亮,改用下方 :active 的 Miuix 风格反馈 */ + /* Remove the default blue tap highlight; use the Miuix-style :active feedback below */ + -webkit-tap-highlight-color: transparent; +} html, body { height: 100%; } @@ -72,12 +79,28 @@ body { cursor: pointer; } -.rail-item .ico { width: 24px; height: 24px; } +.rail-item .ico { + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; +} .rail-item .ico svg { width: 100%; height: 100%; display: block; } .rail-item .lbl { font-size: 11px; } .rail-item.active { background: var(--accent); color: #fff; } +/* Miuix 风格点击反馈:轻按压时整体轻微变淡,替代默认蓝色高亮 */ +/* Miuix-style press feedback: a subtle fade on press instead of the blue highlight */ +.rail-item:active, +.cell:active, +.wcell:active, +.st-item:active { + opacity: 0.6; + transition: opacity .12s ease; +} + .content { flex: 1; overflow: hidden; } .page { display: none; height: 100%; } diff --git a/app/src/main/java/top/yeij/hearth/media/MediaSessionSource.kt b/app/src/main/java/top/yeij/hearth/media/MediaSessionSource.kt index c4e8251..88a0d45 100644 --- a/app/src/main/java/top/yeij/hearth/media/MediaSessionSource.kt +++ b/app/src/main/java/top/yeij/hearth/media/MediaSessionSource.kt @@ -22,36 +22,21 @@ class MediaSessionSource(context: Context) { private var callback: ((MediaInfo?) -> Unit)? = null private var registered = false - // 具名监听器字段:回调通过 callback 字段转发,便于 start/stop 幂等管理 - // Named listener field: forwards via callback, enabling idempotent start/stop + // 具名监听器字段:会话列表变化时回调 + // Named listener field: fires when the active-session list changes private val activeSessionsListener = MediaSessionManager.OnActiveSessionsChangedListener { controllers -> val cb = callback ?: return@OnActiveSessionsChangedListener - val c = controllers?.firstOrNull() - if (c == null) { - Log.d(TAG, "no active media session") - cb(null) - return@OnActiveSessionsChangedListener - } - val meta = c.metadata - val state = c.playbackState - cb( - MediaInfo( - meta?.getString(MediaMetadata.METADATA_KEY_TITLE) ?: "", - meta?.getString(MediaMetadata.METADATA_KEY_ARTIST) ?: "", - meta?.getString(MediaMetadata.METADATA_KEY_ALBUM) ?: "", - state?.state == PlaybackState.STATE_PLAYING, - state?.position ?: 0L, - meta?.getLong(MediaMetadata.METADATA_KEY_DURATION) ?: 0L, - c.packageName, - ) - ) - Log.d(TAG, "media: ${c.packageName}") + notifyMedia(controllers?.firstOrNull(), cb) } - // 注册监听:幂等——先移除旧监听再注册,避免重复 start 累积监听器 - // Register the listener: idempotent — remove the old listener first, avoiding - // listener accumulation from repeated start() calls + // 注册监听:幂等——先移除旧监听再注册,避免重复 start 累积监听器。 + // 注册后主动拉取一次当前活跃会话:注册前已开始的播放不会触发变化回调, + // 主动拉取保证启动时媒体卡即可显示。 + // Register the listener: idempotent — remove the old one first to avoid accumulation. + // After registering, actively pull the current sessions once: playback that started + // before registration never fires the change callback, so the active pull makes the + // media card show immediately on startup. fun start(cb: (MediaInfo?) -> Unit) { stop() callback = cb @@ -59,6 +44,8 @@ class MediaSessionSource(context: Context) { msm.addOnActiveSessionsChangedListener(activeSessionsListener, listenerComponent) registered = true Log.d(TAG, "start: media session listener registered") + val current = msm.getActiveSessions(listenerComponent).firstOrNull() + notifyMedia(current, cb) } catch (e: SecurityException) { // 未授权「通知使用权」时系统抛 SecurityException:媒体监听降级为不可用,不崩溃。 // 用户在系统设置开启通知使用权后,重启 Hearth 才会恢复媒体卡。 @@ -81,6 +68,30 @@ class MediaSessionSource(context: Context) { callback = null } + // 从 controller 提取 metadata + playbackState 构造 MediaInfo 并回调 + // Build MediaInfo from the controller's metadata + playbackState and invoke the callback + private fun notifyMedia(controller: MediaController?, cb: (MediaInfo?) -> Unit) { + if (controller == null) { + Log.d(TAG, "no active media session") + cb(null) + return + } + val meta = controller.metadata + val state = controller.playbackState + cb( + MediaInfo( + meta?.getString(MediaMetadata.METADATA_KEY_TITLE) ?: "", + meta?.getString(MediaMetadata.METADATA_KEY_ARTIST) ?: "", + meta?.getString(MediaMetadata.METADATA_KEY_ALBUM) ?: "", + state?.state == PlaybackState.STATE_PLAYING, + state?.position ?: 0L, + meta?.getLong(MediaMetadata.METADATA_KEY_DURATION) ?: 0L, + controller.packageName, + ) + ) + Log.d(TAG, "media: ${controller.packageName}") + } + companion object { private const val TAG = "HearthMedia" }