First-run download UX: byte-weighted progress + ETA, and fix Control startup permissions#63
Merged
Merged
Conversation
added 5 commits
July 23, 2026 08:56
The download bar was file-count weighted, so on the default manifest it sprinted through the small JSON assets and then sat still for minutes on the two files that are ~93% of the bytes, then reset to zero for the LLM phase. - ModelManager.Progress carries cumulative totalBytesDownloaded/totalBytes, seeded from published sizes and corrected to the real Content-Length as each file starts; cached files are excluded so the bar measures the transfer. - plannedModelBytes()/plannedLlmBytes() size the whole download up front so the pipeline and LLM phases render as one continuous 0..100 bar instead of two sweeps that reset in the middle. - ModelDownloadWorker reports byte-weighted percent plus transfer rate and ETA via detailLine(), clamped monotonic. ETA minutes round rather than truncate. Tests cover byte-weighted monotonicity across the phase handover and the cache states plannedModelBytes must distinguish (fresh, cached, stale, in-progress).
…e/ETA The "media permission not granted" note appeared on a fresh install before the app had asked for anything: buildContextPhrases() runs during startup to bias the STT beam with the on-device music library, and it called listMusic(), which reports a denial to the feed — but permissions were only requested on the first mic tap. - Request permissions in onCreate as one batch, including POST_NOTIFICATIONS (previously declared but never requested, so the foreground download notification was silently dropped). - Startup biasing reads through the silent listMusicOrEmpty(); the music tools still report a denial, because there it is what made the command fail. - Rebuild biasing in onRequestPermissionsResult when the media grant lands, so track titles reach the decoder without an app restart. A micRequested flag keeps a startup grant from switching the microphone on unasked. - Setup panel shows byte counts, rate and ETA from ModelDownloadWorker; the first-run size copy is corrected from ~800 MB to ~600 MB (the Pocket path). On Android 13 the media permission is READ_MEDIA_AUDIO, which the system surfaces under "Music and audio", not the pre-13 "Files and media" group.
…front Pocket is the Control demo's default TTS, but its files were absent from EXPECTED_SIZES, so the byte total under-counted by ~126 MB and then visibly grew as each file self-corrected to its Content-Length. Pin the six meaningful files' published sizes so the total is stable from the first frame.
Point it at two Kokoro model directories (published fp32, then a candidate) via -e kokoroModelDir / -e kokoroDumpDir and diff the dumps. Logs a half-second RMS profile rather than one aggregate figure, because a reduced-precision build can measure clean on a one-second reply and still collapse past two seconds — the vocoder sums over the time axis, so the failure scales with utterance length. Gated behind assumeTrue, so it is a no-op without the model-dir argument.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes two first-run complaints on the Control demo: the model download "takes ages" (really: the progress bar looked frozen) and a "media permission not granted" note that appeared before the app had asked for anything.
SDK — byte-weighted download progress (
849e7e5)The bar was file-count weighted, so on the default manifest it sprinted through the small JSON assets and then sat still for minutes on the two files that are ~93% of the bytes, then reset to zero for the LLM phase.
ModelManager.Progresscarries cumulativetotalBytesDownloaded/totalBytes, seeded from published sizes and corrected to the realContent-Lengthas each file starts. Cached files are excluded, so the bar measures the transfer, not the manifest.plannedModelBytes()/plannedLlmBytes()size the whole download up front, so the pipeline and LLM phases render as one continuous 0→100 bar.ModelDownloadWorkerreports byte-weighted percent plus rate and ETA viadetailLine()(412 / 580 MB · 3.6 MB/s · 2 min left), clamped monotonic. ETA minutes round rather than truncate.control-demo — startup permissions + download detail (
a68d857)buildContextPhrases()runs during startup to bias the STT beam with the on-device music library, and it calledlistMusic(), which reports a denial to the feed — but permissions were only requested on the first mic tap, so a fresh install always showed the note before any dialog.onCreateas one batch, includingPOST_NOTIFICATIONS(declared but never requested, so the download notification was silently dropped).listMusicOrEmpty(); the music tools still report a denial, because there it's what made the command fail.onRequestPermissionsResultwhen the media grant lands; amicRequestedflag stops a startup grant from switching the mic on unasked.~800 MB → ~600 MB(the Pocket path).On Android 13 the media permission is
READ_MEDIA_AUDIO, surfaced under "Music and audio", not the pre-13 "Files and media" group — which is why it looked absent in App Info.Test plan
./gradlew :sdk:test— 87 pass, incl. new byte-weighting/monotonicity andplannedModelBytescache-state coverage../gradlew :sdk:connectedDebugAndroidTest(arm64 emulator, excl. the multi-GB Nemotron/Parakeet-TDT suites that exceed the AVD data partition) — 34 pass, 0 fail; exercises the rewritten download path incl.modelDownloadIsCached/corruptModelFileTriggersRedownload../gradlew :control-demo:compileDebugKotlin+:control-demo:assembleDebug— clean.