forked from trent_larson/crowd-funder-for-time-pwa
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.
401 lines
19 KiB
Markdown
401 lines
19 KiB
Markdown
# Android Local Notification Testing — Planning Analysis
|
||
|
||
**Created:** 2026-06-02
|
||
**Source document:** [local-ios-testing-ngrok.md](./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 guide’s **backend + ngrok + in-app debug panel** path is platform-agnostic. Most of sections **1–3**, **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):
|
||
|
||
- [android-physical-device-guide.md](./android-physical-device-guide.md) — USB, `adb`, build/run commands
|
||
- [notification-system-overview.md](./notification-system-overview.md)
|
||
- [notification-from-api-call.md](./notification-from-api-call.md)
|
||
- [notification-permissions-and-rollovers.md](./notification-permissions-and-rollovers.md)
|
||
|
||
---
|
||
|
||
## 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 repo’s 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:
|
||
|
||
```bash
|
||
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 `pushNotificationReceived` → `handleCapacitorPushNotificationReceived()`
|
||
3. `POST {backend}/notifications/refresh` with `testMode`
|
||
4. `nextNotifications` → `applyNotificationRefreshPayload()` → **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`
|
||
|
||
### Recommended debug workflow (iOS §9) — reuse with tooling swap
|
||
|
||
Steps 1–5, 8–9 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](./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.json`** → `android/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 repo’s 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 30–120s | 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 |
|
||
|
||
### §13 — Related docs
|
||
|
||
Keep iOS-centric links as “see also”; add:
|
||
|
||
- [android-physical-device-guide.md](./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](./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 won’t 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](./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).
|