t66: app names on a device that exposes none

The reported Shield names no app: `pm dump com.netflix.ninja` is ~2241 lines of
dumpsys and grep for `application-label` over the whole capture finds nothing.
The probe shipped `| head -300`, which cut the dump BEFORE the package section,
so "no label" was a truncation artefact; the sweep then named every app after
its own package id, and the apps editor pre-filled the `pkg=pkg` row the user
read as a bug.

  * the probe greps ON THE DEVICE for the three shapes Android prints
    (`application-label`, `nonLocalizedLabel`, `labelRes`); a quoted value is a
    name, `null` and a resource id are not. The dump is never truncated.
  * after APP_LABEL_PROBE_LIMIT consecutive "ran, no label" answers the device
    is taken at its word: the sweep stops spending a shell per package
    (APP_LABEL_TTL expiry re-checks, and a package whose probe FAILED still owes
    its retry — t57 UNIT 0 survives, driven by the app on screen).
  * ONE precedence chain in `ha_adb_common/app_label.resolve_package_name`, used
    by the media player, the Current App sensor, the discovery table, the source
    list and the apps editor: user map > device label > curated map > package id
    (the id shown ONCE, `name` empty when nothing names the app).
  * the curated table covers the user's 21 apps (community-maintained, not
    device truth) and the Configure row says when the device named nothing.
  * the real device shape is a test fixture (trimmed, and marked as such in the
    file: the raw capture is not in the repo) pinning that no label is
    extracted, that the probe count is bounded, and that `Netflix` surfaces.

Also drops the dead `ATTR_MEDIA_DAG_NOW_PLAYING` re-exports left by the t65
flattening and updates the RESEARCH_SHIELD now-playing note to the flat names.

No real Shield was available: the fetch/burst behaviour is exercised against the
documented Android output and the trimmed reproduction of the reported capture.
This commit is contained in:
James Mackay committed 2026-10-12 10:54:38 +01:00
1 parent f29f62ddae
commit 7b32de789a
16 files changed
+973 -176

No files matched your search

+28 -8
View File
@@ -154,11 +154,15 @@ def _apps_table_text(
"""The Editable apps table: stored rows first, then discovered apps (t57).
Every app the Shield reports gets a row, so the page opens on the REAL app
list instead of the four curated entries ("it doesn't find all the apps").
list instead of only the built-in names ("it doesn't find all the apps").
The stored rows keep their position, name and icon — the user's edit is
never overwritten by a discovery — and a discovered app the map does not
mention is appended with the device label (or its package id when the label
fetch did not answer) and its heuristic icon.
mention is appended with the name the SAME chain resolves (user map > device
label > curated map) and its heuristic icon. An app whose name is unknown
anywhere is NOT written as ``pkg=pkg``: a row that saves the package id as if
it were a name is the defect the user reported from this very field, so such
an app is offered in the row editor (which says "no name yet" out loud)
instead of being pre-filled here.
The icon column is emitted only for a stored icon, because the discovered
icon is an MDI NAME (``mdi:netflix``) and the icon column's documented
@@ -178,7 +182,11 @@ def _apps_table_text(
if not package or package in seen:
continue
seen.add(package)
name = app.get("name") or package
name = str(app.get("name") or "").strip()
if not name or name == package:
# nothing names this app: the id would be doubled into a "name" that
# is not one (t66). The row editor still offers it for naming.
continue
lines.append(f"{package}={name}")
return "\n".join(lines)
@@ -1287,6 +1295,13 @@ class NvidiaShieldOptionsFlow(config_entries.OptionsFlowWithReload):
"the Shield reported no installed apps — the table keeps your "
"saved rows."
)
if getattr(manager, "app_labels_absent", False):
# Measured, not guessed: the sweep probed and this firmware named
# nothing, so say which names the user is looking at (t66).
return (
f"{len(apps)} apps found — this Shield exposes no app labels over "
"ADB, so names come from the built-in app names and your own rows."
)
return f"{len(apps)} apps found on the Shield — the table is updated."
async def async_step_menu_again(self, user_input=None):
@@ -1530,19 +1545,24 @@ class NvidiaShieldOptionsFlow(config_entries.OptionsFlowWithReload):
"""The apps the device reported, ``{package: {name, icon}}`` (t64).
Straight from the t57 cache — no sweep, no shell. The name may be empty:
the user's Shield exposes no device label at all, and a package whose
``pm dump`` failed falls back to its id, which is NOT a name (see
``_editable_apps``).
the user's Shield exposes no device label at all (t66: the discovery table
no longer repeats the package id as a name), and an app nothing names is
left empty so ``_editable_apps`` can mark it as needing a name.
"""
detected: dict = {}
for app in self._discovered_apps():
package = str(app.get("package") or "").strip()
if not package:
continue
name = str(app.get("name") or "").strip()
if name == package:
# a name that IS the id is not a name (t66): the row editor says
# "(no name yet)" instead of offering the id twice.
name = ""
detected.setdefault(
package,
{
"name": str(app.get("name") or "").strip(),
"name": name,
"icon": str(app.get("icon") or "").strip(),
},
)