Files
crowd-funder-for-time-pwa/doc/local-android-testing-analysis.md
Jose Olarte III e0a3f7094f docs(notifications): add Android local ngrok testing guide
Add local-android-testing-ngrok.md for FCM wakeup, debug panel, Firebase
setup, platform/battery notes, troubleshooting, verification checklist, and
end-to-end QA flow. Add local-android-testing-analysis.md as planning
notes mapping reuse from the iOS ngrok guide.
2026-06-02 21:33:23 +08:00

19 KiB
Raw Blame History

Android Local Notification Testing — Planning Analysis

Created: 2026-06-02
Source document: local-ios-testing-ngrok.md
Purpose: Plan a future Android counterpart guide by mapping what can be reused from the iOS ngrok workflow and what must be written for Android-specific push, permissions, and OS behavior.

Status: Planning only — does not replace or modify the iOS guide.


Executive summary

The iOS guides backend + ngrok + in-app debug panel path is platform-agnostic. Most of sections 13, 6, 9 (with log tooling swapped), 10 (with platform: "android"), 12, and parts of 11 can be copied or lightly edited.

Everything involving APNs, Xcode, Apple Developer, iOS capabilities, and iOS background/silent-push caveats must be replaced. Android adds direct FCM delivery (no APNs hop), google-services.json, runtime notification permissions (API 33+), Doze / battery optimization / OEM restrictions, and different force-stop / background semantics.

Existing related docs to cross-link (not duplicate):


iOS guide structure (reference map)

§ iOS doc heading Reuse for Android
Intro Architecture overview Adapt — swap APNs leg for FCM→device
Prerequisites Partial — drop Xcode/APNs; add Android SDK/device
1 Install and configure ngrok Reuse unchanged
2 Start the backend locally Reuse unchanged
3 Obtain and use ngrok HTTPS URL Reuse — wording: “device” not “iPhone”
4 Generate and open iOS workspace Rewrite — Android Studio / Capacitor sync
5 Firebase + APNs setup Rewrite — Firebase Android only; no APNs
6 Notification Debug Panel override Reuse unchanged
7 Firebase and Xcode checklist Rewrite — Android manifest / Gradle checklist
8 iOS-specific testing notes Rewrite — Android delivery caveats
9 Recommended debug workflow Reuse — replace Xcode console with logcat
10 Sample curl commands Reuse — change platform to android
11 Troubleshooting Partial — keep ngrok/API rows; replace push rows
12 Key source files Reuse unchanged
13 Related docs Extend — link Android build/device guides

Sections reusable unchanged (or near-unchanged)

These blocks can be carried into doc/local-android-testing-ngrok.md (proposed name) with at most global find-replace (“iPhone” → “Android device”, “Mac” tunnel audience unchanged).

notification-wakeup-service startup (iOS §1 Terminal A, §2)

  • Clone notification-wakeup-service, npm install, .env from .env.example
  • export PORT=3000 (or port from that repos README)
  • npm run dev
  • Local verify: curl -sS http://localhost:3000/health
  • Firebase Admin service account for the backend (GOOGLE_APPLICATION_CREDENTIALS) — same project can serve iOS and Android apps

ngrok setup (iOS §1)

  • brew install ngrok/ngrok/ngrok (or download)
  • ngrok http 3000 in a second terminal
  • Use HTTPS forwarding URL; free tier URL rotation note
  • ngrok inspect UI at http://127.0.0.1:4040

ngrok account creation (iOS §1 “Account and auth token”)

  • Sign up at dashboard.ngrok.com
  • ngrok config add-authtoken YOUR_AUTHTOKEN_HERE

Obtaining HTTPS URL (iOS §3)

  • Copy https://….ngrok-free.app from Forwarding line
  • No trailing slash in debug panel
  • Mac-side tunnel test: export NGROK_URL=… and curl "$NGROK_URL/health"

Backend override configuration (iOS §6)

  • Non-production build required for Notification Debug Panel
  • Account → Show All General Advanced Functions/dev/notifications
  • Notification Backend URL, Save Backend URL
  • localStorage: notificationDebug.backendBaseUrl, notificationDebug.testMode
  • Optional programmatic override via @/services/notifications (setBackendBaseUrl, setTestMode, getNotificationApiBaseUrl)

Debug panel usage (iOS §6 table, §8 “Two Simulate WAKEUP_PING buttons”)

Control Android relevance
Notification Backend URL Same
Test Mode Same (testMode: true on API)
Register Token Now Same (POST /notifications/register)
Refresh Notifications Same
Simulate WAKEUP_PING (backend) Same — isolates ngrok + refresh without FCM
Wakeup Ping Simulator Same — exercises handleCapacitorPushNotificationReceived path
Event Log [Notifications] Same
Pending Notification Inspector Same concept; confirm Android plugin inspector behavior in daily-notification-plugin

testMode usage (iOS §6, §10)

  • Default-on when unset in storage (NotificationDebugConfig.ts)
  • Sent on register and refresh payloads
  • Backend/debug endpoints accept testMode: true for dev traffic

Refresh endpoint testing (iOS §9 steps 5, §11 “Refresh endpoint unreachable”)

  • Panel Refresh Notifications → expect Event Log + ngrok POST /notifications/refresh
  • Simulate WAKEUP_PING (backend button) for API-only path
  • Troubleshooting table for network error, 404, wrong port, stale URL

curl examples (iOS §10)

Reuse structure; only payload deltas for Android doc:

export BASE="https://abc123.ngrok-free.app"
  • $BASE/health — unchanged
  • $BASE/notifications/register — set "platform": "android"
  • $BASE/notifications/refresh — set "platform": "android"
  • $BASE/debug/send-wakeup — unchanged shape; confirm deviceId/token contract in notification-wakeup-service README

App still uses Capacitor.getPlatform() for platform in NotificationService.ts (ios | android).

Shared architecture concepts (intro + silent wake sequence)

Reusable narrative (edit diagram only):

  1. FCM data message with data.type = "WAKEUP_PING"
  2. Capacitor pushNotificationReceivedhandleCapacitorPushNotificationReceived()
  3. POST {backend}/notifications/refresh with testMode
  4. nextNotificationsapplyNotificationRefreshPayload()daily-notification-plugin clear + schedule

Repos table (notification-wakeup-service, crowd-funder-for-time-pwa, daily-notification-plugin) — unchanged.

Key source files (iOS §12)

Same files apply on Android Capacitor builds:

  • NotificationDebugConfig.ts, NotificationDebugEvents.ts, notificationLog.ts
  • NotificationService.ts, NativeNotificationService.ts
  • firebaseMessagingClient.ts, NotificationDebugPanel.vue, main.capacitor.ts

Steps 15, 89 unchanged. Replace step 7:

  • iOS: Xcode console → [Notifications] pushNotificationReceived type=WAKEUP_PING
  • Android: adb logcat filtered on app tag / [Notifications] (document exact filter in Android guide)

iOS-specific sections — must rewrite for Android

Architecture diagram (intro)

iOS today: Mac → ngrok → app; FCM → APNs → iPhone.

Android doc: FCM → device directly (no APNs). Update ASCII diagram and caption (“silent push” on Android is still FCM data; delivery rules differ).

Prerequisites (intro list)

iOS prerequisite Android replacement
Mac with Xcode Android Studio, JDK 17+, ANDROID_HOME, adb — see android-physical-device-guide.md
Physical iPhone Physical Android device (emulator possible for some steps but not representative for Doze/OEM/battery)
Firebase with APNs for bundle ID Firebase with Android app (app.timesafari package name)
Non-production build Same — e.g. build:android:dev / build:android:test

Remove: “simulator is not sufficient for reliable silent push / APNs”.

Add: emulator vs physical device guidance for FCM and background limits.

§4 — Generate and open the iOS workspace

Replace entirely with Android equivalent:

  • npm install
  • npm run build:android:dev or build:android:test (non-production for debug panel)
  • npx cap sync android if needed
  • Open android/ in Android Studio
  • Run on physical device (USB debugging)
  • VITE_FIREBASE_* in Capacitor web build
  • initializeNativePushAndFirebaseMessaging() in main.capacitor.ts — same entry point

Do not reference .xcworkspace, signing in Xcode, or build:ios:* except as cross-link to iOS doc.

§5 — Firebase + APNs setup (first-time setup)

Keep (Android-relevant portions only):

  • Firebase account / Spark plan sufficient for FCM
  • Create Firebase project
  • Register Android app in Firebase (package name app.timesafari from capacitor.config.ts)
  • Download google-services.jsonandroid/app/ (project may gitignore this file — document secure handling)
  • Firebase Admin service account for notification-wakeup-service — same as iOS §5 tail

Remove entirely:

  • Register iOS app in Firebase (or move to “shared project” sidebar: one Firebase project, two apps)
  • GoogleService-Info.plist / Xcode drag-and-drop
  • Create APNs Authentication Key (.p8)
  • Upload APNs key to Firebase
  • Enable iOS capabilities (Push Notifications, Background Modes → Remote notifications)

Add in Android guide (see next major section):

  • Gradle plugin / google-services classpath if not already in repo
  • POST_NOTIFICATIONS permission (API 33+)
  • Default notification channel / Capacitor Push Notifications Android setup
  • SHA-1/SHA-256 only if using Firebase features that require it (note whether wakeup testing needs Play App Signing keys)

§5 verify checklist — iOS-only bullets

Replace:

  • “Xcode without Firebase/plist errors” → Android Studio build; google-services.json present
  • “iOS push permission prompt” → Android 13+ notification permission + older grant model
  • “content-available style payload” → Android high-priority data message / FCM options as implemented by notification-wakeup-service (document actual payload; no APNs content-available)

§7 — Firebase and Xcode checklist (iOS)

Replace with Android checklist, e.g.:

Item Action
Application ID app.timesafari in capacitor.config.ts, android/app/build.gradle, Firebase Android app
google-services.json In android/app/; not committed if gitignored — local copy per developer
Gradle Google services plugin applied (verify repos current build.gradle)
Permissions POST_NOTIFICATIONS (API 33+); manifest entries for FCM
FCM token Debug panel Register Token Now + ngrok POST /notifications/register
No APNs N/A on Android

§8 — iOS-specific testing notes

Replace with Android-specific sections (draft topics below). Do not port:

  • APNs silent delivery / Simulator unreliability (iOS framing)
  • Force-quit via app switcher (iOS-specific policy)
  • Low Power Mode (iOS) — Android has different battery saver APIs
  • Focus / Do Not Disturb (iOS naming)

Port with Android wording:

  • Two Simulate WAKEUP_PING buttons table — unchanged behavior

§11 — Troubleshooting (partial)

Reuse as-is:

  • Refresh endpoint unreachable (ngrok, URL, 404, CORS note)
  • Stale ngrok URL
  • Plugin / JWT errors after refresh

Rewrite:

iOS troubleshooting Android replacement
Push permission + VITE_FIREBASE_* + Xcode log Permission (runtime POST_NOTIFICATIONS), logcat, Firebase Android config
Silent push not waking — backgrounded not force-quit, APNs key, wait 30120s FCM high-priority data, force-stop (STOP from settings), Doze, battery optimization, OEM autostart, token mismatch
Physical device + provisioning profile USB debugging, correct build variant, Play vs debug signing if relevant

Keep iOS-centric links as “see also”; add:

  • android-physical-device-guide.md
  • BUILDING.md — Android build commands (build:android:*)
  • daily-notification-plugin Android docs (exact alarm, pending inspector on Android)

Android-Specific Topics Required

These sections do not exist in the iOS guide (or exist only by analogy) and must be written for the Android notification testing doc.

Firebase project setup

  • Use the same Firebase project as iOS when testing the same backend, or document a dedicated timesafari-dev project.
  • Add an Android app with package name app.timesafari.
  • Enable Cloud Messaging (default on new projects).
  • Download google-services.json and install under android/app/.
  • Note: android/.gitignore may exclude google-services.json — developers copy locally; never commit secrets.

google-services.json

  • Placement: android/app/google-services.json
  • Sync after add: npx cap sync android, rebuild in Android Studio
  • Verify build merges Firebase config (no “missing google-services” Gradle errors)
  • Relationship to VITE_FIREBASE_* for the web layer / Capacitor JS Firebase initialization

Android notification permissions

  • Android 13+ (API 33): POST_NOTIFICATIONS runtime permission — required for notification display; document interaction with data-only FCM wake (may still deliver to app code when permission denied — verify against current app behavior and document accurately).
  • Android 12 and below: install-time grant model; fewer runtime prompts.
  • App Settings → Notifications — manual enable path for testers.
  • Link notification-permissions-and-rollovers.md for product-level permission UX.

FCM token handling

  • Token obtained via Capacitor Push Notifications + firebaseMessagingClient.ts (same JS path as iOS).
  • Register Token Now in debug panel → POST /notifications/register with platform: "android".
  • Token rotation: when to re-register; duplicate skip behavior in panel.
  • Ensure notification-wakeup-service stores/sends to the token shown in the panel for /debug/send-wakeup.
  • Optional: adb cannot easily read FCM token — panel is source of truth (same as iOS).

Android background delivery behavior

  • FCM data messages handled in foreground/background per Capacitor plugin and NativeNotificationService.ts.
  • No APNs intermediary — document expected latency vs iOS.
  • High-priority FCM for wakeup testing (align with backend message options).
  • App in background vs foreground vs killed — different from iOS “swipe away” story:
    • Force stop (Settings → Force stop): delivery often blocked until user launches app again (stricter than iOS “backgrounded”).
    • Recent apps swipe: behavior varies by OEM/Android version — document “test with Home button background, not force stop.”
  • pushNotificationReceived / listener registration at startup (main.capacitor.ts).

Doze Mode

  • Device idle → deferred network and job execution.
  • Testing: use adb shell dumpsys deviceidle (document safe dev-only commands) or unplugged idle wait.
  • Explain why /debug/send-wakeup may succeed on server but device wakes late.
  • Whitelisting app for tests (developer settings) — use cautiously; note production users wont do this.

Battery optimization

  • Settings → Apps → TimeSafari → Battery → Unrestricted vs Optimized.
  • Manufacturer “battery saver” modes that restrict background network.
  • Recommend Unrestricted (or equivalent) for local wakeup validation; warn that production users may remain optimized.

OEM restrictions (Samsung, Xiaomi, Oppo, etc.)

  • Autostart / Background activity / Battery menus on Samsung, Xiaomi (MIUI), Oppo/ColorOS, Huawei, OnePlus, etc.
  • Symptom: FCM works on Pixel but not on OEM device until autostart enabled.
  • Provide a short “if wake fails on OEM, check…” checklist without exhaustive per-OEM screenshots (link community docs if needed).
  • Physical device testing should include at least one stock-ish device (Pixel) and one OEM device when possible.

Proposed outline for doc/local-android-testing-ngrok.md

Suggested section order mirroring iOS doc for easy maintenance:

  1. Title, audience, goal (Android physical device + ngrok + wakeup service)
  2. Architecture overview (FCM direct to Android)
  3. Prerequisites (Android Studio, device, Firebase Android app, non-prod build)
  4. ngrok install, account, tunnel (reuse iOS §1)
  5. Start notification-wakeup-service (reuse iOS §2)
  6. ngrok HTTPS URL (reuse iOS §3)
  7. Build and open Android project (new, replaces iOS §4)
  8. Firebase setup for Android (new, replaces iOS §5 — no APNs)
  9. Notification Debug Panel (reuse iOS §6)
  10. Android configuration checklist (new, replaces iOS §7)
  11. Android-specific testing notes (new, replaces iOS §8)
  12. Recommended debug workflow (reuse iOS §9 + logcat)
  13. Sample curl commands (reuse iOS §10 + platform: "android")
  14. Troubleshooting (merge reusable + Android push rows)
  15. Key source files (reuse iOS §12)
  16. Related docs (iOS doc + Android device guide + BUILDING)

Wording and terminology substitutions

When adapting reused sections:

iOS doc term Android doc term
iPhone Android phone / device
Xcode console logcat / Android Studio Logcat
build:ios:dev / test build:android:dev / test
GoogleService-Info.plist google-services.json
APNs / silent push FCM data message / high-priority data
Bundle ID Application ID / package name (app.timesafari)
Physical iPhone required for APNs Physical device strongly recommended for Doze/OEM/FCM realism
platform: "ios" in curl platform: "android"

Gaps to resolve while writing the Android guide

Research during authoring (code + notification-wakeup-service + daily-notification-plugin):

  1. Exact FCM Android message priority and payload fields for WAKEUP_PING (parity with iOS data message).
  2. Whether POST_NOTIFICATIONS denial blocks data message delivery to JS listeners on API 33+.
  3. Gradle/Firebase plugin versions already in android/ — document exact files to touch.
  4. Android Pending Notification Inspector parity with iOS panel section.
  5. Whether emulator with Google Play image is acceptable for minimal FCM smoke tests vs mandatory physical device for wakeup SLA testing.

Document maintenance

Document Role
local-ios-testing-ngrok.md Canonical iOS + ngrok workflow (unchanged by this analysis)
This file Reuse vs rewrite matrix and Android topic backlog
Future local-android-testing-ngrok.md Operator guide for Android testers

When backend or debug panel behavior changes, update both platform guides shared sections in lockstep (or extract shared “ngrok + debug panel” snippet later — out of scope unless requested).