fix: pull active media session on start, icon centering, tap highlight, media card

This commit is contained in:
2026-08-16 18:33:14 +08:00
parent c9dcb88647
commit a65205b8d2
2 changed files with 61 additions and 27 deletions
+25 -2
View File
@@ -1,7 +1,14 @@
/* Hearth 桌面样式:侧边栏 + 5 页框架 + 柔光玻璃容器 */ /* Hearth 桌面样式:侧边栏 + 5 页框架 + 柔光玻璃容器 */
/* Hearth launcher styles: sidebar + 5-page frame + soft-glass containers */ /* 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%; } html, body { height: 100%; }
@@ -72,12 +79,28 @@ body {
cursor: pointer; 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 .ico svg { width: 100%; height: 100%; display: block; }
.rail-item .lbl { font-size: 11px; } .rail-item .lbl { font-size: 11px; }
.rail-item.active { background: var(--accent); color: #fff; } .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; } .content { flex: 1; overflow: hidden; }
.page { display: none; height: 100%; } .page { display: none; height: 100%; }
@@ -22,36 +22,21 @@ class MediaSessionSource(context: Context) {
private var callback: ((MediaInfo?) -> Unit)? = null private var callback: ((MediaInfo?) -> Unit)? = null
private var registered = false 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 = private val activeSessionsListener =
MediaSessionManager.OnActiveSessionsChangedListener { controllers -> MediaSessionManager.OnActiveSessionsChangedListener { controllers ->
val cb = callback ?: return@OnActiveSessionsChangedListener val cb = callback ?: return@OnActiveSessionsChangedListener
val c = controllers?.firstOrNull() notifyMedia(controllers?.firstOrNull(), cb)
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}")
} }
// 注册监听:幂等——先移除旧监听再注册,避免重复 start 累积监听器 // 注册监听:幂等——先移除旧监听再注册,避免重复 start 累积监听器
// Register the listener: idempotent — remove the old listener first, avoiding // 注册后主动拉取一次当前活跃会话:注册前已开始的播放不会触发变化回调,
// listener accumulation from repeated start() calls // 主动拉取保证启动时媒体卡即可显示。
// 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) { fun start(cb: (MediaInfo?) -> Unit) {
stop() stop()
callback = cb callback = cb
@@ -59,6 +44,8 @@ class MediaSessionSource(context: Context) {
msm.addOnActiveSessionsChangedListener(activeSessionsListener, listenerComponent) msm.addOnActiveSessionsChangedListener(activeSessionsListener, listenerComponent)
registered = true registered = true
Log.d(TAG, "start: media session listener registered") Log.d(TAG, "start: media session listener registered")
val current = msm.getActiveSessions(listenerComponent).firstOrNull()
notifyMedia(current, cb)
} catch (e: SecurityException) { } catch (e: SecurityException) {
// 未授权「通知使用权」时系统抛 SecurityException:媒体监听降级为不可用,不崩溃。 // 未授权「通知使用权」时系统抛 SecurityException:媒体监听降级为不可用,不崩溃。
// 用户在系统设置开启通知使用权后,重启 Hearth 才会恢复媒体卡。 // 用户在系统设置开启通知使用权后,重启 Hearth 才会恢复媒体卡。
@@ -81,6 +68,30 @@ class MediaSessionSource(context: Context) {
callback = null 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 { companion object {
private const val TAG = "HearthMedia" private const val TAG = "HearthMedia"
} }