185 lines
7.5 KiB
Markdown
185 lines
7.5 KiB
Markdown
# Who Need Help for Android
|
|
|
|
This module is a native Android WebView shell for the Phoenix application. It
|
|
keeps authentication cookies, LiveView WebSockets, MapLibre, and chat in the
|
|
same trusted origin. Live location is sent by a user-started native foreground
|
|
service with a persistent notification and Stop action, so it can continue
|
|
while the Activity is minimized without requesting Android's
|
|
background-location permission.
|
|
|
|
The native tracking bridge uses `WebViewCompat.addWebMessageListener` with the
|
|
exact configured origin and rejects messages outside the main frame. It does
|
|
not expose a legacy `addJavascriptInterface` object to every frame.
|
|
|
|
## Verified build configuration
|
|
|
|
- Android Gradle Plugin 9.3.0
|
|
- Gradle 9.6.1
|
|
- Android SDK Command-line Tools 22.0
|
|
- Android CLI 1.0.15857036 (embedded in the locked Command-line Tools archive)
|
|
- AndroidX WebKit 1.16.0
|
|
- compileSdk / targetSdk 37
|
|
- Build Tools 37.0.0
|
|
- Java source and bytecode level 17
|
|
- minSdk 24 (project baseline, not an Android SDK requirement)
|
|
|
|
The debug origin is not stored in the Dockerfile or Gradle project. Set
|
|
`WNH_DEBUG_BASE_URL` in the repository's ignored `.env` file. The supplied
|
|
local configuration uses loopback together with `adb reverse`; debug builds
|
|
allow cleartext traffic, while the WebView still restricts in-app navigation to
|
|
that one configured origin.
|
|
|
|
The same ignored file supplies `WNH_TRACKING_MIN_TIME_MS` and
|
|
`WNH_TRACKING_HTTP_TIMEOUT_MS`. The checked-in example preserves the original
|
|
local client freshness and timeout behavior; these values are not claimed as
|
|
measured production capacity settings.
|
|
|
|
Release builds do not have a default server and cannot be produced unsigned.
|
|
The repository reads non-secret version/origin settings from the selected
|
|
ignored environment file, while the upload key and its randomized password
|
|
stay outside the repository. Create that key once:
|
|
|
|
```sh
|
|
./scripts/init-android-release-signing.sh
|
|
```
|
|
|
|
Back up both reported files before uploading the first bundle. Losing this
|
|
dedicated upload key is recoverable through Play App Signing, but keeping an
|
|
offline backup avoids a reset. Do not copy either file into the repository.
|
|
|
|
Configure these non-secret values in the deployment's ignored environment
|
|
file, then produce the signed APK and Play bundle:
|
|
|
|
```dotenv
|
|
WNH_BASE_URL=https://your-final-origin.example
|
|
WNH_ANDROID_VERSION_CODE=1
|
|
WNH_ANDROID_VERSION_NAME=0.1.0
|
|
WNH_ANDROID_SIGNING_KEY_ALIAS=who-need-help-upload
|
|
```
|
|
|
|
```sh
|
|
WNH_ENV_FILE=.env.production ./scripts/android-release-build.sh
|
|
```
|
|
|
|
The build passes the private files with Docker BuildKit secret mounts, runs
|
|
release unit tests and lint, enables code/resource shrinking, signs both
|
|
artifacts, verifies the APK and AAB signatures, and validates the AAB with the
|
|
same `bundletool` used underneath Android Gradle Plugin and Google Play before
|
|
exporting anything.
|
|
It rejects a missing, HTTP, credentialed, query-bearing, or fragment-bearing
|
|
release URL and rejects missing or partial signing configuration.
|
|
|
|
Google Play requires an Android App Bundle for a new app. With Play App
|
|
Signing, the locally held key signs the uploaded bundle and Google holds the
|
|
separate app-signing key used for distributed APKs:
|
|
|
|
- <https://developer.android.com/studio/publish/app-signing>
|
|
- <https://support.google.com/googleplay/android-developer/answer/9859152>
|
|
- <https://docs.docker.com/build/building/secrets/>
|
|
|
|
## Public staging build
|
|
|
|
The installable `staging` build type uses the explicit public HTTPS
|
|
`WNH_BASE_URL`, disables cleartext traffic, and has its own
|
|
`org.whoneedhelp.mobile.staging` application ID. Configure a missing local value
|
|
from the existing `PHX_HOST`, `PHX_SCHEME`, and `PHX_URL_PORT`, then build:
|
|
|
|
```sh
|
|
./scripts/ensure-local-public-origin.sh
|
|
./scripts/android-staging-build.sh
|
|
sha256sum android/dist-staging/who-need-help-staging.apk
|
|
```
|
|
|
|
This variant uses Android's generic debug signing key so it can be installed
|
|
for staging verification. It is not a production-signed artifact and must not
|
|
be published as a release. The manifest accepts same-origin HTTPS deep links,
|
|
but verified Android App Links additionally require the final signing
|
|
certificate fingerprint in the deployment's `/.well-known/assetlinks.json`.
|
|
|
|
With the temporary public origin reachable, run the API 37 emulator smoke test:
|
|
|
|
```sh
|
|
./scripts/android-staging-smoke.sh
|
|
```
|
|
|
|
The script installs the exported staging APK into a fresh project-scoped
|
|
emulator container, loads the configured HTTPS home page, follows a
|
|
same-origin `/safety` deep link, verifies that the package does not claim an
|
|
external HTTPS origin, and retains UI dumps, screenshots, package metadata,
|
|
and logcat diagnostics under ignored `output/android-staging-smoke/`. The
|
|
one-run container and image are removed on success or failure.
|
|
|
|
## Reproducible Docker build
|
|
|
|
From the repository root:
|
|
|
|
```sh
|
|
./scripts/android-build.sh
|
|
sha256sum android/dist/who-need-help-debug.apk
|
|
```
|
|
|
|
The Docker build runs JVM unit tests, Android lint, `assembleDebug`, and
|
|
`assembleDebugAndroidTest` before it exports the application APK and lint
|
|
report.
|
|
|
|
## Automated device tests
|
|
|
|
Run the default API 37 suite or the complete API 24/30/34/37 matrix from the
|
|
repository root:
|
|
|
|
```sh
|
|
./scripts/android-instrumentation-test.sh
|
|
./scripts/android-matrix-test.sh
|
|
```
|
|
|
|
The command requires `/dev/kvm`. It generates an ignored
|
|
`.env.android-test` once with a randomized `http://127.0.0.1:PORT` origin and
|
|
mode `0600`; the application and its in-process fixture server both derive the
|
|
origin from that file. It then builds both APKs, boots a fresh selected emulator
|
|
container without external networking, injects emulator coordinates, and runs
|
|
`AndroidJUnitRunner`. `WNH_ANDROID_TEST_API` selects one supported API, while
|
|
`WNH_ANDROID_TEST_API_MATRIX` controls the matrix command.
|
|
|
|
The tests cover the missing-location-permission boundary, the WebView-triggered
|
|
Android permission dialog, same-origin deep-link routing across Activity
|
|
recreation, foreground location upload after Home and Activity destruction, the
|
|
persistent notification Stop action, a disconnected Stop request with visible
|
|
retry state, and externally forced process death for the non-sticky tracking
|
|
service. Results are stored by API under ignored
|
|
`output/android-instrumentation/`. On failure, logcat, service state, and
|
|
emulator logs are retained; the exact container and one-run image are removed
|
|
in either outcome.
|
|
|
|
## Emulator verification
|
|
|
|
The optional `emulator` target contains the API 37.0 Google APIs x86_64 system
|
|
image. Manual verification against the local Compose application requires KVM
|
|
and host networking. Pass the same `.env` value as a build argument, then
|
|
expose the Compose proxy to Android with `adb reverse`:
|
|
|
|
```sh
|
|
set -a
|
|
. ./.env
|
|
set +a
|
|
docker build \
|
|
--build-arg "WNH_DEBUG_BASE_URL=$WNH_DEBUG_BASE_URL" \
|
|
--build-arg "WNH_TRACKING_MIN_TIME_MS=$WNH_TRACKING_MIN_TIME_MS" \
|
|
--build-arg "WNH_TRACKING_HTTP_TIMEOUT_MS=$WNH_TRACKING_HTTP_TIMEOUT_MS" \
|
|
--target emulator \
|
|
-t who-need-help-android:emulator \
|
|
android
|
|
docker run --rm --name who-need-help-android-emulator \
|
|
--device /dev/kvm \
|
|
--network host \
|
|
who-need-help-android:emulator
|
|
docker exec who-need-help-android-emulator adb reverse tcp:4010 tcp:4010
|
|
docker exec who-need-help-android-emulator adb install \
|
|
/opt/who-need-help/who-need-help-debug.apk
|
|
```
|
|
|
|
The application ID `org.whoneedhelp.mobile` is provisional until the publishing
|
|
identity and store listing are chosen. Google documents Play package names as
|
|
unique and permanent, so do not create the Play Console app or publish this
|
|
identifier until that choice is explicit. Changing it after publication creates
|
|
a different Android application.
|