Skip to content

Launcher: reliable switch away from stock, one-click enable+default, live progress#83

Merged
bryanroscoe merged 7 commits into
mainfrom
fix/launcher-set-default-when-disabled
Jun 22, 2026
Merged

Launcher: reliable switch away from stock, one-click enable+default, live progress#83
bryanroscoe merged 7 commits into
mainfrom
fix/launcher-set-default-when-disabled

Conversation

@bryanroscoe

@bryanroscoe bryanroscoe commented Jun 22, 2026

Copy link
Copy Markdown
Owner

Fixes the Launchers tab: switching the default launcher was slow, looked hung, and the 'Set as default' button did nothing on a disabled launcher.

Root cause (verified live on a Shield / Android 11, via adb)

An enabled stock launcher overrides set-home-activity and the role API — both return Success but HOME keeps resolving to stock. The only thing that actually hands HOME over is disabling stock (exactly what v1's Launcher Wizard does). Switching between two non-stock launchers via set-home-activity works instantly. So the polite strategies are doomed when leaving stock, and the old flow ground through them (~1.6s verify back-off each, ~4s total) with a misleading 'press Home' message before even offering the disable-stock confirm.

Changes

  • Switch away from stock is now fast + reliable. When a stock launcher holds HOME, try the polite setters once; if HOME still resolves to stock, go straight to the opt-in disable-stock takeover. Other launchers are never touched. Takeover logic is extracted into one shared helper (used by the fast path and the ladder's last resort).
  • One-click on a disabled launcher. 'Set as default' was disabled whenever the launcher wasn't enabled (looked clickable, did nothing — most visibly for disabled stock). The backend already runs pm enable first, so the gate was pure friction. Dropped it; the button reads 'Enable & set default' when the target is disabled.
  • Live per-step progress, inline in the row being acted on, over a Tauri Channel (Enabling → Assigning the Home role → Registering as the Home app → Disabling the stock launcher (…) → Checking whether Home switched over → Refreshing). Covers the verify polls and the final reload so the spinner never freezes on a stale label.
  • Always refresh when done so the list + active-launcher line redraw without a manual Refresh.

Notes

  • Safety gates unchanged: NEVER_DISABLE still blocks stock takeover, the disable is opt-in (UI confirm), and stock is re-enabled if the switch doesn't verify.
  • Corrects a stale code claim that these builds are 'accept-but-ignore' for all strategies — it's specifically an enabled stock launcher winning HOME resolution.
  • No screenshot regen (demo fixture has no disabled/in-flight launcher).

cargo fmt/clippy/test (171 pass, +2 new launcher tests), npm run check (0/0), npm run build — all green. Backend mechanism validated directly against two Shields over adb.

A disabled launcher's 'Set as default' was a dead button (gated on
!enabled) even though the backend already runs pm enable first — so the
user had to Enable, then Set as default. Drop the gate and relabel it
'Enable & set default' when disabled; one click now enables and switches.

The multi-strategy switch (enable -> role -> set-home-activity -> verify,
with stock-takeover as last resort) can take several seconds, so stream
per-step progress over a Tauri Channel and show it inline in the row being
acted on, including the verify polls and the final list refresh that
otherwise left the spinner frozen on a stale label. Always re-read launcher
state when the action finishes so the list and active-launcher line redraw
without a manual Refresh.
Narrating 'Confirming…' before all three verify attempts (and the
takeover flow runs the strategy chain twice) made it churn the same line
over and over. Keep a single check, only in the takeover path.
Verified live on Shield/Android 11: an enabled stock launcher overrides
both set-home-activity and the role API — they return 'Success' but HOME
keeps resolving to stock. The only thing that hands HOME over is disabling
stock (exactly what v1's Launcher Wizard does). Switching between two
non-stock launchers via set-home-activity works fine.

The old flow ran the full polite ladder first (role + set-home-activity,
each with a ~1.6s verify back-off) before offering the disable-stock
takeover, so leaving stock took ~4s of doomed attempts and a misleading
'press Home' message before the confirm even appeared.

Now: when a stock launcher currently holds HOME, try the polite setters
once, and if HOME still resolves to stock go straight to the opt-in
disable-stock takeover. Other launchers are never touched. The takeover
logic is extracted into one shared helper used by both the fast path and
the ladder's last resort.
@bryanroscoe bryanroscoe changed the title Launcher: one-click set-default on disabled launchers + live progress Launcher: reliable switch away from stock, one-click enable+default, live progress Jun 22, 2026
Foreground the now-default launcher with a HOME intent on the success
paths that didn't already (role API, set-home-activity, stock fast path),
so the TV actually shows the new launcher instead of sitting on the old
screen until the user presses Home. Gated behind verify_active, so it only
ever foregrounds the confirmed target.

Status message now reads '<Name> is now your default launcher.' using the
friendly launcher name instead of the raw package id, and drops the
internal '(via role_api)' strategy detail that meant nothing to users.
- Launcher Disable now shows 'Disabling…', an inline status line, refreshes
  the list, and confirms '<Name> disabled.' (was a bare 'Disable' that gave
  no sign it worked). Enable gets the same inline status + friendly names.
- Memory-table Force stop / Disable buttons show a spinner + 'Stopping…' /
  'Disabling…' instead of a bare '…', with clearer result messages.
- Generalize the spinner into a reusable .spinner / .busy pair.
safeDisableFromMemory updated the catalog state but never the health
report's top_memory list that feeds the table, so a disabled app's row
lingered until a full refresh. Remove the row on success — a disabled app
isn't running, so it belongs out of the memory list immediately.
Each tab cached its own snapshot, so a change in one left the others stale
— disabling a launcher from the Memory tab left the Launchers tab showing
it as enabled. Add invalidateDeviceCaches(): a state change marks the other
data tabs (Launchers, Memory report) for a fresh load on next visit, while
the acting tab refreshes itself inline. Existing data stays on screen until
each reload completes, so there's no flash.

Wired into every package/launcher state change (enable/disable/uninstall,
set-default, launcher enable/disable) plus the bulk paths (snapshot apply,
panic recovery, Optimize wizard).
@bryanroscoe bryanroscoe merged commit 88de89c into main Jun 22, 2026
4 checks passed
@bryanroscoe bryanroscoe deleted the fix/launcher-set-default-when-disabled branch June 22, 2026 02:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant