[feature] android: screenshot demo mode with fictional fixtures - #236
Merged
Merged
Conversation
The Play listing needs populated screens, and every one of them is behind
the sign-in wall. Capturing them today means signing a device into a real
account: slow, impossible to reproduce exactly next release, and it puts
live customer data one mis-tap away from a public store listing.
This is the Android half of ios/App/Parley/ScreenshotDemo.swift. A
debug-only, in-memory flag stands the wall down without a token and
answers every cloud call from fixed fictional fixtures, driven entirely
by deep links because input automation on an emulator is unreliable and
`am start` is not:
parley://demo/library the recordings list, populated
parley://demo/transcript transcript + findings + action items
parley://demo/record the live meeting, mid-transcript
parley://demo/account the account sheet
parley://demo/off leave demo mode
Three invariants: no network (every call site guarded, so an offline
machine captures the same frames and production is never touched), no
writes (nothing reaches the auth DataStore or the pending-upload queue,
and the live screen runs a scripted DemoMeetingSession instead of the
microphone and the foreground service), and no residue (the flag is in
memory, so `off` or a restart is the whole clean-up). BuildConfig.DEBUG
gates activation so a release build cannot be talked into serving
fixtures.
The live screen swaps a MeetingSession for a scripted one through a new
`LiveMeeting` interface — the five flows the screen actually reads —
which is what lets it be captured on an emulator with no audio input.
Fixture content is entirely invented: no real company, person, customer
or meeting, and the only address is in the RFC-reserved example.com. The
copy is written separately in English and Traditional Chinese rather than
translated one way, so each store's set reads natively; the fixtures
follow the device locale because the sets are captured per-locale.
|
This was referenced Aug 14, 2026
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.



Why
The Play listing needs the app populated, and every screen it needs is behind the sign-in wall. Capturing them today means signing a device into a real account — slow, impossible to reproduce exactly next release, and it puts live customer data one mis-tap away from a public store listing. iOS solved this in
ios/App/Parley/ScreenshotDemo.swift; Android had no equivalent, which currently blocks the listing.What
android/app/src/main/kotlin/com/pathors/parley/screenshot/DemoMode.kt— a debug-only, in-memory flag that stands the sign-in wall down without a token and answers every cloud call from fixed fictional fixtures. Driven entirely by deep links, because input automation on an emulator is unreliable andam startis not:parley://demo/libraryparley://demo/transcriptparley://demo/recordmeeting)parley://demo/accountsettings)parley://demo/offMainActivitystill handlesparley://auth-callbackexactly as before — the demo handler claims onlyparley://demo/…, and only in a debug build, then hands everything else straight on.The three invariants
HomeViewModel.refresh/loadAccount,RecordingDetailViewModel.load,AppContainer.drainPendingUploads), so an offline reviewer's or CI machine captures the same frames and production is never touched.DemoMeetingSessionrather than the microphone, the relay, the encoder and themicrophoneforeground service — which is also what makes it capturable on an emulator with no audio input.parley://demo/off(or a process restart) is the whole clean-up.BuildConfig.DEBUGgates activation, so a release build cannot be talked into serving fixtures.Verified on device
AVD
parley-test(Pixel 7 / API 35), debug APK, all four screens captured in both locales:Active default network: none) every screen was fully populated — a real cloud call would have surfaced the error banner instead.dumpsys netstats detail's per-uid byte map: zero rx, zero tx. No OkHttp or DNS lines in logcat either.files/PendingUploads/throughout. Demo mode neither displayed it, uploaded it, nor touched its mtime;parley_auth.preferences_pbstayed 0 bytes.parley://demo/offreturns the app to the sign-in screen, andparley://auth-callback?error=…still reaches the auth handler and renders its error — the demo route does not swallow it../gradlew assembleDebug :parleykit:test :app:testDebugUnitTestpasses: 55 tests, 0 failures, including 7 new ones asserting both languages are complete and differ, that the summary counts are the truth about the meta, and that the fixtures name no real person, company or address.Content
Entirely invented — a renewal negotiation, a discovery call and a quarterly review, written the way B2B calls actually sound (a seat count against a quote, a price hold, an onboarding estimate with an SSO condition). No real company, person, customer or meeting; the only address is
alex@example.com, in the RFC-reserved domain. The copy is written separately in English and Traditional Chinese rather than translated one way, and follows the device locale, because the screenshot sets are captured per-locale.Scope notes
android/AppStore/**untouched (owned by another PR).ui/AccountSheet.ktandcloud/CloudClient.ktuntouched — the account sheet is served throughHomeViewModel's existing state, so the siblingfeat/android-account-deletionPR has no conflict here.buildConfig = true) soBuildConfig.DEBUGexists.