Files
ha-nvidia-shield/custom_components/nvidia_shield/ha_adb_common/running_apps.py
T
James Mackay 07682ff082 feat(media_session): map Android PlaybackState 6 to buffering (t55)
The media session parser only understood states 2 (paused) and 3
(playing), so a session refilling its buffer (Android state 6, which the
Shield's own ExoPlayer/MediaSession sessions report exactly like stock
Android) read as "idle" or vanished into the foreground tier.

MediaSessionInfo gains an independent `buffering` flag (playing/paused/
buffering are now three flags off one integer), the session sort puts
buffering right behind playing — it is mid-playback, so it outranks a
paused session when the ladder picks one — and the running-apps
enrichment names a buffering live/last-known session "buffering" instead
of falling into "idle".

The MediaPlayerState stub gains BUFFERING and STANDBY so an in-suite
reference to a member real HA does not have fails here (real HA 2026.10
exposes off/on/idle/playing/paused/standby/buffering).
2026-10-12 06:52:58 +01:00

91 lines
3.3 KiB
Python

"""Build the running apps list from recent tasks + media sessions.
Produces the media_dag_running_apps attribute value that media_zone reads.
Recent tasks (from dumpsys activity recents) provide the list of running apps.
Media sessions provide playback state for apps that have active sessions.
"""
from .media_session import MediaSessionInfo
def running_apps_from_tasks(
recent_tasks: list[dict],
media_sessions: list[MediaSessionInfo] | None = None,
foreground_package: str | None = None,
last_known: dict[str, MediaSessionInfo] | None = None,
) -> list[dict] | None:
"""Build a list of running app dicts from recent tasks + media sessions.
Args:
recent_tasks: List of {"package": str} from ADB recent tasks.
media_sessions: Optional media sessions for playback state enrichment.
foreground_package: The current foreground app package.
last_known: Optional per-package TTL-filtered last-known-state cache
(P3-3b) — apps without a live session carry their last known
playback state as ``last_known_state``/``last_known_title``.
Returns None if there are fewer than 2 apps (no point showing a switcher
with only one option).
"""
if not recent_tasks or len(recent_tasks) < 2:
return None
# Index media sessions by package for quick lookup
session_by_pkg: dict[str, MediaSessionInfo] = {}
if media_sessions:
for s in media_sessions:
if s.package:
session_by_pkg[s.package] = s
apps = []
for task in recent_tasks:
pkg = task["package"]
app: dict = {"package": pkg}
# Mark foreground app
if pkg == foreground_package:
app["foreground"] = True
# Enrich with media session data if available
session = session_by_pkg.get(pkg)
if session:
if session.playing:
app["state"] = "playing"
elif session.buffering:
app["state"] = "buffering"
elif session.paused:
app["state"] = "paused"
app["state"] = "paused"
else:
app["state"] = "idle"
if session.title:
app["title"] = session.title
if session.artist:
app["artist"] = session.artist
else:
app["state"] = "running"
# P3-3b: no live session — surface the TTL-capped last-known state
cached = (last_known or {}).get(pkg)
if cached is not None:
if cached.playing:
app["last_known_state"] = "playing"
elif cached.buffering:
app["last_known_state"] = "buffering"
elif cached.paused:
app["last_known_state"] = "paused"
if cached.title:
app["last_known_title"] = cached.title
apps.append(app)
return apps if len(apps) >= 2 else None
# Keep backward compat for any code still using this
def running_apps_from_sessions(sessions: list[MediaSessionInfo]) -> list[dict] | None:
"""Build running apps from media sessions only (legacy)."""
if not sessions:
return None
tasks = [{"package": s.package} for s in sessions if s.package]
return running_apps_from_tasks(tasks, sessions)