# 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. ## 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) - 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. Supply the real HTTPS deployment: ```sh ./gradlew :app:assembleRelease \ -PWNH_BASE_URL=https://help.your-domain.example \ -PWNH_TRACKING_MIN_TIME_MS=5000 \ -PWNH_TRACKING_HTTP_TIMEOUT_MS=15000 ``` The build rejects a missing, HTTP, credentialed, query-bearing, or fragment-bearing release URL. ## 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 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. Changing it after publication creates a different Android application.