Repository navigation
The Nine Lives widget: your rank, your current book and your next Lives on the home screen - #111
Conversation
…s the build Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014XfNSbEfq8dG2ResrdGXbf
…'s rank card Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014XfNSbEfq8dG2ResrdGXbf
…your next lives Play and pause go through the same invisible handoff screen as the Binder, so a cold start holds until playback is in the foreground. A book that cannot play right now opens its page instead, like a Binder cover. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014XfNSbEfq8dG2ResrdGXbf
…s, and every few minutes while playing Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014XfNSbEfq8dG2ResrdGXbf
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014XfNSbEfq8dG2ResrdGXbf
…e is open Navigation's single top matched the book page's route pattern, so with book A's page on top a widget tap for book B kept showing book A. This hit the Nine Lives widget's Lives and the Binder's Open its page alike. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014XfNSbEfq8dG2ResrdGXbf
… and the resize floor matches real launcher cells Pixel Launcher rounds a 2x2 to about 172 by 224 dp, under the old 180 dp floor, so the smallest size could not be reached. The bitmap budget now counts only the covers each size draws. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014XfNSbEfq8dG2ResrdGXbf
…onger leaves the dimmed play button Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014XfNSbEfq8dG2ResrdGXbf
…ttom spanning the full width, the current book between Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014XfNSbEfq8dG2ResrdGXbf
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8df9be63f8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| val shown = activeBook(shownBookId) | ||
| val plan = togglePlan( |
There was a problem hiding this comment.
Reject stale-library toggle taps before choosing a playback action
If the user switches libraries or modes and taps the widget before its debounced redraw, activeBook(shownBookId) returns null, but execution still reaches togglePlan. That function checks audible playback and a retained current book before checking shownBookId, so the stale button can pause or resume the new library's book instead of being ignored. The new widget now exposes this previously unused toggle path. Return IGNORE immediately when the shown book is outside the active library, as playFromBinderOwned already does.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 3f9d7e5. togglePlan now returns IGNORE first when the shown book isn't in the active library, before any playback state is read, so a stale button never pauses or resumes the new library's book. The test failed first.
…y's book Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014XfNSbEfq8dG2ResrdGXbf
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3f9d7e5464
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| */ | ||
| internal fun toggleTap(launch: WidgetLaunch.TogglePlay): WidgetTap { | ||
| val act = CompletableDeferred<Unit>() | ||
| val plan = scope.async { toggleOwned(launch.bookId, act) } |
There was a problem hiding this comment.
Decide pause actions before probing server reachability
When a streamed book is still playing or buffering after its server becomes unreachable, the new widget's pause tap calls toggleOwned, which eagerly awaits isPlayable(shown) before togglePlan can select PAUSE. That check can wait up to eight seconds for a server probe, leaving audio playing despite the pause tap even though pausing requires no network access. After validating entitlement and the active library, select the pause action before performing the playability check; reserve that check for actions that actually need to load media.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 397cc97. Playability is now asked lazily, only when the tap would load a book. A pause while audio is playing or buffering returns PAUSE without the server probe. The test uses a probe that hangs if called.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014XfNSbEfq8dG2ResrdGXbf
What changes for users
Closes #8
Tests
androidx.glance:glance-appwidget) is the one new dependency, approved in the design.Evidence
/home/static/nine-lives-evidence/2026-10-31-220w/w2/SUMMARY.mdon a Pixel emulator (API 36) and an API 30 emulator, against a mock Audiobookshelf:07-size-*-v2.png), API 30 4x3 (09-api30-4x3-v2.png), locked (02-locked-crop.png), RTL (12-rtl-crop.png).Review
Needs the S26U
The one row layout (Pixel won't go below 2 rows), One UI resize behavior, and battery over a day.
Draft copy pending Jeff
Nine Lives (picker label). Your rank, your current book, and your next lives (picker description). Unlock to bring the altar home (locked line). Reused from the Binder: Play, Pause, Open Nine Lives, Locked. Tap to see the unlock, and the empty state lines.
🤖 Generated with Claude Code
https://claude.ai/code/session_014XfNSbEfq8dG2ResrdGXbf