From 48d56eee306bc77d1f85dfbd49c74b9bb205dba4 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Fri, 14 Aug 2026 15:21:03 +0700 Subject: [PATCH] docs(android): document permissions, back behaviour, and icon pipeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Records what a maintainer cannot infer from the wrapper: why VIBRATE is declared when the web build needs no equivalent, when the wake lock is held and why it re-acquires on visibilitychange, how back maps to overlay history, and why the textZoom pin and the in-app size setting ship together. Also documents where launcher art comes from, that regenerating needs Roboto Condensed converted out of the .woff fontsource ships, and the clipping bug still present in source.svg. The "Why no INTERNET permission?" section is unchanged — the offline guarantee still holds. --- android/README.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/android/README.md b/android/README.md index dea41fe..192004a 100644 --- a/android/README.md +++ b/android/README.md @@ -93,6 +93,41 @@ If you ever add a remote feature (analytics, sync, etc.), add this back to ``` +## Permissions + +| Permission | Why | +|------------|-----| +| `VIBRATE` | Haptic feedback when a player taps a cell. A WebView app must declare this itself for `navigator.vibrate()` to work — in a browser, Chrome holds the permission on the page's behalf, which is why the web build needs no equivalent. Normal permission: no runtime prompt, no Play data-safety impact. | + +No `INTERNET` — see above. + +## Android-specific behaviour + +These exist because the wrapper differs from a browser tab. All three are +invisible when testing the web app on a desktop. + +**Screen stays awake during a round.** Auto-call advances on a timer with no +touch input, so a full round can run 15 minutes untouched — long enough for the +display to sleep and the WebView to throttle its interval. `MasterPanel` holds a +Screen Wake Lock while a round is live (`web/src/lib/wake-lock.js`). Android +drops the lock whenever the page hides, so the module re-acquires on +`visibilitychange`. Player-only mode never takes a lock; those screens stay +awake from the user's own taps. + +**Back closes overlays, then confirms exit.** Android 16 (targetSdk 36) no +longer calls `onBackPressed()` nor dispatches `KEYCODE_BACK`, so +`MainActivity` registers an `OnBackPressedCallback` instead. Each open overlay +pushes one history entry (`web/src/lib/overlay-history.js`), so "the WebView can +go back" means exactly "an overlay is open": back closes the bingo modal or the +settings sheet, and only at the root does it ask before quitting. Browsers get +the same overlay behaviour for free. + +**Board text size is app-controlled.** The player card is a fixed 9-column grid +that clips at large system font scales, so `MainActivity` pins the WebView's +`textZoom` to 100. Taking the system control away obliges replacing it: Settings +→ **Cỡ chữ bảng** scales the board numbers instead. The two ship together — if +one is ever removed, remove both. + ## Running on BlueStacks / NoxPlayer / Android emulators The APK has no native libraries (`lib/` is empty), so it's architecture- @@ -183,6 +218,34 @@ git push origin v1.0.0 `com.miti99.loto` — set in `capacitor.config.json` and `android/app/build.gradle`. +## Icons and splash + +Launcher art is generated from [`web/static/icons/source.svg`](../web/static/icons/source.svg) +— the same brand mark the web app and PWA use — not hand-maintained per +density. Resources: + +| Resource | What | +|----------|------| +| `mipmap-*/ic_launcher.png`, `ic_launcher_round.png` | Legacy icons, API ≤ 25 | +| `mipmap-*/ic_launcher_foreground.png` | Adaptive foreground; text sized to fit the 66% safe circle | +| `mipmap-*/ic_launcher_monochrome.png` | Android 13+ themed icons | +| `drawable/ic_launcher_background.xml` | Adaptive background, brand gradient as a vector | +| `drawable/splash.xml`, `drawable-night/splash.xml` | Splash, API < 31 | +| `values*/colors.xml` → `splash_background` | Splash colour, light + dark | + +On API 31+ the splash comes from `windowSplashScreenBackground` / +`windowSplashScreenAnimatedIcon` in `AppTheme.NoActionBarLaunch` instead of the +drawable. + +Regenerating requires Roboto Condensed on the rendering host; the font ships as +`.woff` in `web/node_modules/@fontsource/roboto-condensed` and has to be +converted to `.ttf` for `rsvg-convert` to see it. + +> **Note:** `source.svg` sets `font-size="240"`, which overflows the 512px +> canvas — the PNGs under `web/static/icons/` are visibly clipped on both +> sides. The Android art is rendered at a corrected size. The web PWA icons +> still carry the original bug. + ## Audio Bundled by the web app under `web/static/audio/{hoai-my,nam-minh}/{1..90,cho,kinh}.mp3`,