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
@@ -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"
}