Store listing copy and a stub privacy policy already live in
app/store-listing/ — this doc is the operational checklist for
everything else, based on what's actually in the codebase today. Nothing here is a marketing
claim; anything not yet decided is marked TODO (owner).
- Unique, stable
applicationId:com.tapsense.app(debug builds get a.debugsuffix, so debug and release can be installed side-by-side). - Debug builds are visually distinguishable from the closed-testing/production release, not
just by package name: the
debugbuild type overridesapp_nameto "TapSense Debug" (viaapp/src/debug/res/values/strings.xml— Gradle's standard source-set precedence, never merged into release) and addsversionNameSuffix = "-debug". Shows up on the launcher icon label, the in-app splash screen (both read the sameR.string.app_name), and inBuildConfig.VERSION_NAME/Settings → About ("1.0.0-debug"vs. release's plain"1.0.0") — so a tester's screenshot or a local dev's screen share is unambiguous about which build they're looking at. - Debug builds also get a visually distinct launcher icon, not just a different label: the
same source-set precedence overrides
ic_launcher_background/_foreground/_monochrome(app/src/debug/res/drawable/) with a bold safety-orange background (#FF6D00vs. release's dark charcoal#211F1C) and the same TapSense ring mark recolored dark for contrast, plus a small diamond "flag" badge not present on the release icon - added to the Android 13+ themed-icon (monochrome) layer too, since that layer discards all authored color at runtime, so shape is the only thing that can differentiate it there. The badge sits at (74,74), distance ≈28.3dp from the icon's center (54,54) - inside the ~66dp-diameter guaranteed safe zone (33dp radius), so it survives every adaptive-icon mask shape (circle, squircle, rounded square, teardrop) instead of risking inconsistent clipping near the edge. Verified byte-level viaaapt2 dump xmltree/dump resourceson both a built debug and release APK: the release APK's compiled background/foreground/monochrome resources are unchanged (#ff211f1cbackground,#ff35c6d9ring stroke, exactly 3pathDataentries - no badge) sincedebugresources never merge into any other variant; the debug APK correctly resolves to the new orange/dark/4-path versions. -
versionCode/versionNamepresent (currently8/"1.0.0"— checkapp/build.gradle.ktsfor the live values, since this line goes stale the moment either is bumped and isn't re-verified automatically). Convention for this repo:versionCodeis a plain incrementing integer bumped for every build uploaded to any Play track (closed testing included) — Play requires strict monotonic increase across all tracks, so testing and production share one counter.versionNamestays a stableMAJOR.MINOR.PATCHand is only bumped for a user-visible release milestone, not for every closed-testing build; several closed-testingversionCodes can and normally do share oneversionNamewhile iterating toward it. When triaging a tester's bug report, ask for the Play Store "app version" they see in Settings (which showsversionName (versionCode)) so it maps unambiguously back to a commit. - Release build type:
isMinifyEnabled = true,isShrinkResources = true, R8 verified locally (./gradlew :app:assembleReleaseand:app:bundleReleaseboth succeed). - Native debug symbols:
release { ndk { debugSymbolLevel = "SYMBOL_TABLE" } }embeds symbol tables for any.soin the bundle intoBUNDLE-METADATA/, so Play auto-deobfuscates native crashes on upload with no manual symbol-file step. Confirmed unfixable for the two.sofiles actually in this bundle (libandroidx.graphics.path.so, transitive fromandroidx.compose.ui;libdatastore_shared_counter.so, transitive fromandroidx.datastore:datastore-preferences:1.1.1's multi-process file-locking) — re-verified end to end on a freshbundleRelease:readelf -Son both shows no.symtab/.debug_*/.strtabsection at all (Google ships them fully stripped at the source), AGP's ownextractReleaseNativeSymbolTablestask correspondingly produces zero output files, and the final.aabhas no symbol data anywhere inBUNDLE-METADATA/. There is nothing to embed because the source binaries contain nothing to extract — this is not a Gradle misconfiguration and there is no dependency-version bump that changes it (checked). Play Console's "no debug symbols" warning is advisory ("We recommend..."), not a submission blocker on any track, and is effectively unavoidable for any Compose + DataStore app today. Do not attempt to exclude these.sofiles viapackaging{}to silence the warning —graphics-pathunderlies Compose's own Path/Canvas rendering (used directly byAntennaSilhouette/GuidedSweepAnimation) and excluding it risks breaking rendering for a cosmetic, non-blocking warning. Any future dependency or our own NDK code that ships unstripped will still be picked up automatically by this same setting. -
targetSdk 36(Android 16) — meets Play's rolling "target API level within 1 year of the latest Android release" requirement (compileSdkbumped alongside it;minSdkunchanged at 26). Re-check this annually: Play enforces a new deadline each year as the next Android version ships, most recently a hard cutoff of August 31, 2026 for updates to remain publishable. - Release signing reads from environment variables only (
RELEASE_KEYSTORE_PATH,RELEASE_KEYSTORE_PASSWORD,RELEASE_KEY_ALIAS,RELEASE_KEY_PASSWORD) — no keystore is committed to this repo. TODO (owner): generate an upload keystore (keytool -genkeypair -v -keystore upload-keystore.jks -alias upload -keyalg RSA -keysize 2048 -validity 10000), store it somewhere safe (password manager / secrets vault), and enroll in Play App Signing so Google holds the real app signing key and this becomes just the upload key. - TODO (owner): produce the first signed
.aabvia./gradlew :app:bundleReleasewith the four env vars set, and confirmbundletool/ Play Console's pre-launch report accepts it.
- Single permission:
android.permission.NFC(declared in the library's manifest, merged automatically) — required for every real NFC call the app makes. No other permissions requested. -
<uses-feature android:name="android.hardware.nfc" android:required="false" />— the app installs and degrades gracefully on devices with no NFC hardware (verified:HomeScreen,MyPhoneScreen,TapGuideScreen,TapTestScreenall branch to a dedicated "NFC not supported" notice viaNfcUnsupportedNotice). -
MainActivityis the only exported component,android:exported="true"only because it's the launcher activity (required by Android 12+ for launcher activities) — no other exported surface, no deep links, no custom URI schemes, noWebView. -
android:screenOrientation="portrait"— a deliberate choice (see the comment inAndroidManifest.xml), lint-suppressed with a documented rationale rather than silently ignored.
- No account creation, no login, no PII collected — confirmed by reading every data-handling
path:
TapSenseSettingsRepository(DataStore: appearance/haptics/reduce-motion/manual phone override only), the Room cache (device-model → antenna-position mappings only),LogcatNfcLocatorAnalytics(logs locally to Logcat only, never transmitted),FakeCatalogRemoteApi(in-memory demo data, makes no real network call). - Settings → Privacy & data opens the hosted privacy policy
(
https://nagarjunavs.github.io/tapsense/android/privacy/) in the browser viaopenUrlSafely(UrlLauncher.kt) — no in-app privacy screen orWebView. - Settings → Contact support opens a pre-addressed draft (
nagarjunavs.dev@gmail.com, subject pre-filled with the app version) in the user's own mail app viaACTION_SENDTO(sendFeedbackEmailSafely,UrlLauncher.kt) — this is the app's only feedback channel (previously there was none at all; Help center only linked to the in-app Troubleshoot self-help screen). Doesn't require a privacy policy update: the app collects and transmits nothing itself, it only hands an editable draft to the OS mail client, same class of action as opening the privacy policy link above — nothing is sent unless and until the user chooses to hit send in their own mail app. -
app/store-listing/PRIVACY_POLICY.md's content is hosted at a real, stable URL (https://nagarjunavs.github.io/tapsense/android/privacy/) — Play Console requires a live privacy policy URL even for an app that collects nothing. - Settings → Rate TapSense (
openPlayStoreListingSafely,UrlLauncher.kt) and the automatic Play In-App Review prompt after a successful tap test (requestInAppReviewSafely,InAppReviewLauncher.kt) don't change any Data Safety answer: both hand off entirely to Google Play (the Play Store app or Play Services' own review sheet) — this app transmits nothing itself beyond the tap-test success count and a "requested" flag already covered by the existing DataStore disclosure above. - TODO (owner): complete the Play Console Data Safety form. Based on the above, the
honest answers are: no data collected or shared off-device; if you later wire a real
CatalogRemoteApiimplementation, disclose "App info and performance → Device or other IDs" is not sent (only a normalized manufacturer/model string, which Play's categories don't map to a personal identifier) — re-verify against Play's current category definitions at submission time, since these evolve. - TODO (owner): no account creation exists, so Play's account-deletion requirements don't apply — re-confirm this is still true if account support is ever added.
- Content descriptions present on every icon-only interactive element (bottom nav items,
every screen's close button, marker/sweep components — verified via
contentDescriptionusage acrossui/componentandui/navigation; the confident-match marker (AntennaSilhouette) now self-describes too, matchingGuidedSweepAnimation's existing behavior). - Every close (
X) button's actual touch target is ≥48dp, per Android's minimum — the visible 32dp circle is unchanged, but it now sits insideIconButton's own default-sized tappable area instead of the button itself being shrunk to 32dp. - TODO (owner): this app is portrait-locked with no large-screen-adaptive layout (see
DECISIONS.md's "Tablets" section) — exclude tablets from your closed-testing device pool, or explicitly accept the stretched-phone-layout limitation if you include them. -
reducedMotionrespected end-to-end (Settings →AppShellViewModel→ every marker/ripple component) for users who've enabled a reduce-motion preference. - Light/dark theme fully implemented (
TapSenseTheme,AppearanceMode.SYSTEM/LIGHT/DARK), not just a single hardcoded palette. - Every list/scroll-affected screen (Home, My Phone, Settings, Onboarding pages) is independently scrollable, so content doesn't clip under larger system font scale.
- Empty/failure states are real, not blank: NFC-unsupported notice, tap-test timed-out/off/unsupported states, catalog-loading state.
- TODO (owner): run TalkBack over the full flow once before submission — content descriptions being present is necessary but not sufficient; verify reading order and focus behavior manually.
-
:appandnfc-locator-coretranslated into Spanish, Brazilian Portuguese, French, German, Hindi, Japanese, Korean, and Simplified Chinese (values-es/-pt-rBR/-fr/-de/-hi/-ja/-ko/-zh-rCNin both modules) — the largest non-English Android/Play markets. SeeDECISIONS.md's "Localization" section for the full rationale, including why Arabic/Hebrew (RTL) is deliberately deferred rather than shipped untested. - Locale selection is automatic: Android resolves
values-<lang>/from the device locale on every API level, no code required.android:localeConfigadditionally wires the Android 13+ per-app language picker (res/xml/locales_config.xml). - Play's per-locale App Bundle splits are disabled
(
bundle { language { enableSplit = false } }inapp/build.gradle.kts), not left on the default. versionCode 5 shipped with the default (split) behavior, and a closed tester found that switching languages via Settings → Apps → TapSense → Language did nothing on the Play-installed build, because Play only installs the one locale split matching the device's language and its on-demand delivery of the rest proved unreliable — confirmed not a shrinking/minification issue viaaapt2 dumpon the versionCode 5 APK. Fixed in versionCode 6 per Android's own documented guidance for apps with independent-of-system-locale language switching (https://developer.android.com/guide/app-bundle/configure-base); every install now bundles all locale resources, trading a small download-size increase for reliable switching. Not a Play policy concern either way — this is a supported Gradle configuration choice. SeeCHANGELOG.md's[Unreleased]entry andDECISIONS.md's "Localization" section. -
./gradlew lintreports zeroMissingTranslation/ExtraTranslation/StringFormatMatches/StringFormatCountfindings across all 8 locales in both modules — every translation key and format-argument count matches the English source exactly. - German (longest words in this set) and Japanese (CJK rendering) spot-checked live on an emulator across Settings, Home/preview, and the Tap Guide→Tap Test flow — no truncation, overflow, or tofu glyphs.
- TODO (owner): once real testers are on non-English devices, do a full manual pass across all 8 languages and every screen (this update only spot-checked the two highest-risk locales) — pay particular attention to the confidence badge chips and bottom nav labels, which have the least horizontal room.
- TODO (owner): if/when Arabic or Hebrew is added, budget a dedicated RTL layout pass
(start/end vs. left/right padding, icon direction) before shipping it — see
DECISIONS.md.
Copy already drafted in app/store-listing/README.md.
Still needed, all TODO (owner) — none of these are fabricated here:
- App icon: 512×512 PNG, 32-bit with alpha (export from the existing adaptive icon source at
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml+ its background/foreground/monochrome drawables). - Feature graphic: 1024×500 PNG/JPEG.
- At least 2 phone screenshots (real device or emulator captures of Home, My Phone, Tap Guide, Tap Test — this repo's own screens, not stock photography).
- Short description (≤80 chars) and full description (≤4000 chars) — ASO-optimized, see
app/store-listing/README.mdreview/approval. - Support email and (optional) website URL.
- Content rating questionnaire — expected to land in the lowest tier (no user-generated content, no ads, no in-app purchases) but must be completed in-console, not assumed.
- Category: suggested Tools (already in the store-listing draft).
- Not applicable — no login exists anywhere in the app.
- TODO (owner): upload the first build to an Internal testing track, verify install + core flows (onboarding → auto-detect → Home marker → My Phone Back/Front → Tap Guide → Tap Test) on at least one physical device.
- TODO (owner): promote to Closed or Open testing for a wider pre-release check before Production.
- TODO (owner): use a staged rollout percentage (e.g. 10% → 50% → 100%) for the first Production release rather than 100% immediately.
Per-build "What's new" text (what to paste into Play Console for each track/version) lives in
RELEASE_NOTES.md, not here — that file is kept current per build; this
checklist isn't.
Everything above marked TODO (owner) requires a Play Console account, real signing credentials, real graphic assets, and a human decision — none of it can be completed from the repository alone, and none of it has been fabricated or assumed complete in this checklist.