feat(android-app): android app - #1256
Draft
dorthrithil wants to merge 42 commits into
Draft
Conversation
- Add @capacitor/core, android, app, splash-screen, status-bar (^8.x) as deps - Add @capacitor/cli, @capacitor/assets as devDeps - Add capacitor.config.ts: appId de.localcrag.app, appName LocalCrag, webDir dist/client/browser, launchAutoHide false, no server block Co-authored-by: Cursor <cursoragent@cursor.com>
- Add npm run verify:webdir asserting dist/client/browser/index.html exists - Guards against RESEARCH Pitfall 1 (wrong webDir producing a blank WebView) - Verified against a real ng build; cap:build/cap:sync deferred to plan 02 Co-authored-by: Cursor <cursoragent@cursor.com>
- Add HardwareBackButtonService contract stub (register, handleBackPress, dismissTopOverlayIfAny) exporting OVERLAY_SELECTORS, EXIT_APP, BackPressOutcome; every method throws NOT_IMPLEMENTED - Add 7 specs covering overlay-dismiss detection and canGoBack/exit branching (D-11, D-12), using an EXIT_APP token and mocked Location so no Capacitor plugin proxy is spied on - All 7 specs fail RED for NOT_IMPLEMENTED only; rest of Karma suite (126 specs) and lint stay green Co-authored-by: Cursor <cursoragent@cursor.com>
- environment.android.ts targets http://10.0.2.2:5000 (D-09, emulator alias for host loopback) - angular.json android configuration replaces only the environment file, mirroring the e2e pattern - package.json cap:build/cap:sync scripts to build and sync the Capacitor Android project Co-authored-by: Cursor <cursoragent@cursor.com>
- npx cap add android generates client/android/, a Capacitor Gradle project bundling dist/client/browser into app/src/main/assets/public - applicationId/namespace de.localcrag.app, display name LocalCrag (D-01, D-02, D-06), versionName synced to package.json's 1.51.0 (D-04) - Manifest kept minimal with only the generated INTERNET permission (T-14-05) Co-authored-by: Cursor <cursoragent@cursor.com>
- New debug source-set overlay (app/src/debug/) adds networkSecurityConfig allowing cleartext only to 10.0.2.2, denying it everywhere else via base-config (D-10, T-14-01) - Nothing under app/src/main or app/src/release references cleartext or 10.0.2.2, so the allowance never reaches a release manifest (T-14-02) Co-authored-by: Cursor <cursoragent@cursor.com>
- dismissTopOverlayIfAny() queries OVERLAY_SELECTORS and dispatches a synthetic Escape keydown that PrimeNG's own Dialog/Popover already consume to self-close - handleBackPress() enforces overlay-dismiss-first, then Location.back() when canGoBack, else immediate exitApp() with no confirm dialog - register() is native-only, idempotent, and wraps the listener callback in try/catch (T-14-06) so a thrown error can never leave the hardware back button permanently unresponsive Co-authored-by: Cursor <cursoragent@cursor.com>
- Adds a single provideAppInitializer calling HardwareBackButtonService.register() immediately after ThemeService's initializer, so the listener is live before the router resolves the first route - register() early-returns off native platforms, so this is inert on the web build (full Karma suite and production build stay green) Co-authored-by: Cursor <cursoragent@cursor.com>
… resources - Add client/scripts/generate-placeholder-assets.mjs, rasterizing src/assets/lc_logo.svg via sharp into icon-only/foreground/background and splash/splash-dark source PNGs under client/assets/ - Run npx @capacitor/assets generate --android to produce the full Android density matrix (mipmap-*, drawable-*) plus -night qualified splash resources so the OS picks the dark splash before any JS runs - Sync the generated resources into client/android via npm run cap:sync Co-authored-by: Cursor <cursoragent@cursor.com>
- main.ts: call SplashScreen.hide() once bootstrapApplication() resolves, guarded by Capacitor.isNativePlatform() (D-07) — hides on WebView-ready, not on a fixed timer or first interactive route - theme.service.ts: add syncNativeStatusBar(), an effect() driven by isDarkMode() that sets StatusBar style/background from the same --p-content-background token .site-header already paints with (D-05), with a hex-format guard before crossing the Capacitor bridge Co-authored-by: Cursor <cursoragent@cursor.com>
Repo-wide prettier --write . in the pre-commit hook (lint-staged runs
it without a filename placeholder) reformatted this untracked .mjs file
to double quotes since it falls outside the **/*.{js,ts} eslint glob
that would otherwise restore single quotes. No functional change.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Gradle 8.14.3's daemon cannot run on JDK 25 (the only JDK available via
Android Studio's bundled JBR on this machine): the Groovy/ASM version it
ships fails to parse class file major version 69 during build-script
compilation ("BUG! exception in phase 'semantic analysis' ... Unsupported
class file major version 69")
- Gradle 8.14.4 backports the Groovy/ASM bump needed for JDK 25 support
while staying on the same AGP-compatible 8.14.x line, so this is a patch
bump with no build-logic changes
- Verified: `./gradlew :app:assembleDebug :app:processReleaseManifest`
now completes successfully; debug APK assembles and the release
manifest merge (the check this plan exists to prove) runs correctly
Co-authored-by: Cursor <cursoragent@cursor.com>
…s work - The debug APK serves the bundled app from Capacitor's default https://localhost origin. The WebView's mixed-content policy blocks XMLHttpRequests to the debug-only cleartext host (http://10.0.2.2:5000, D-09/D-10) even though the network security config already permits it at the OS level, so every API call silently failed on the emulator - Found during Task 1 checkpoint pre-automation: cold-starting the debug APK left the app stuck on the splash screen forever with "Mixed Content ... has been blocked" console errors - Fix: MainActivity now sets WebSettings.MIXED_CONTENT_ALWAYS_ALLOW, guarded by BuildConfig.DEBUG (a per-build-type generated constant, false in release) so this relaxation can never activate in a release build — same debug/release isolation guarantee as the network security config from plan 02. buildFeatures.buildConfig had to be enabled explicitly since AGP 8 no longer generates BuildConfig by default Co-authored-by: Cursor <cursoragent@cursor.com>
…ootstrap - Found during Task 1 checkpoint pre-automation: instanceSettingsService .getInstanceSettings() and menuItemsService.getMenuItems() are wired as Angular app initializers with no error handling. Any failure (offline, unreachable backend, CORS) errors the underlying Observable, which rejects bootstrapApplication()'s promise — main.ts's .then() that calls SplashScreen.hide() never runs, so the native splash stays on screen forever with no way to recover - This directly breaks this plan's own checkpoint step 6 (airplane-mode cold start must still render the UI shell, D-07) and would fail on any transient network hiccup, not just full outages - Fix: both initializers now catchError and resolve gracefully instead of rejecting bootstrap — instance settings/menu load failures log to console and fall back to default state (browser-detected language, no preloaded menu) rather than blocking the app from ever rendering Co-authored-by: Cursor <cursoragent@cursor.com>
…WebView - Found during Task 1 checkpoint pre-automation: after fixing the mixed-content block, API calls from the debug APK still failed — the browser blocked them with "No 'Access-Control-Allow-Origin' header" because flask-cors was hardcoded to the single FRONTEND_HOST origin (http://localhost:4200), and Capacitor's WebView serves the app from https://localhost, a different origin - FRONTEND_HOST also doubles as the base URL for email links elsewhere in the codebase, so it can't safely become a multi-value field - Fix: new optional ADDITIONAL_CORS_ORIGINS config (comma-separated), merged into the flask-cors origins list alongside FRONTEND_HOST. Defaults to empty, so prod/test/CI CORS behavior is unchanged unless a developer opts in via their local (gitignored) dev.cfg — documented in template.cfg Co-authored-by: Cursor <cursoragent@cursor.com>
Use http androidScheme to avoid mixed-content blocks on cleartext images, rewrite loopback hosts in File URLs and rich-text HTML to 10.0.2.2, and document Capacitor CORS origins in template.cfg. Co-authored-by: Cursor <cursoragent@cursor.com>
Install @capacitor/preferences, turn on CapacitorHttp for Android API calls, and add a unit-tested resolveApiHost helper seeded from the environment default (INST-01 / SHELL-03 foundation). Co-authored-by: Cursor <cursoragent@cursor.com>
Resolve Preferences-backed apiHost in main.ts and build all ApiService URLs from RUNTIME_API_HOST so JWT refresh and uploads share one origin. Co-authored-by: Cursor <cursoragent@cursor.com>
Temporary Preferences-backed API URL editor for UAT, with HTTPS-or-emulator-loopback validation before reload. Co-authored-by: Cursor <cursoragent@cursor.com>
Strict product+version on /api/health plus Preferences saved-instance helpers for the Phase 16 picker. Co-authored-by: Cursor <cursoragent@cursor.com>
Isolate JWT localStorage and Dexie DBs by host so instance switches cannot leak sessions or offline drafts. Co-authored-by: Cursor <cursoragent@cursor.com>
Gate first-run on empty saved list, validate LocalCrag health before save, and expose Switch instance in the account menu. Co-authored-by: Cursor <cursoragent@cursor.com>
Defer product-identity and version soft-warn so already-deployed LocalCrag servers remain addable from the picker. Co-authored-by: Cursor <cursoragent@cursor.com>
Hide app chrome on /instances, expose Switch instance for guests, and navigate to / after save or switch so the picker does not stick. Co-authored-by: Cursor <cursoragent@cursor.com>
- Add GPS_BRIDGE InjectionToken + registerPlugin('GpsBridge') contract
- Stub installNativeGpsShim/uninstall for Wave 0 (plan 03 implements)
- Jasmine specs fail on install-gate / mapping / refcount until GREEN
Co-authored-by: Cursor <cursoragent@cursor.com>
- Set mockGpsRecording=false for Android so Record cannot false-pass - Require !Capacitor.isNativePlatform() before mock walker load/install Co-authored-by: Cursor <cursoragent@cursor.com>
- Depend on play-services-location 21.4.0 - Declare ACCESS_FINE/COARSE only (no FGS/BG) - Implement start/stop/getCurrentPosition + locationUpdate events Co-authored-by: Cursor <cursoragent@cursor.com>
- Wire local Capacitor plugin into BridgeActivity - Keep DEBUG mixed-content block after super.onCreate Co-authored-by: Cursor <cursoragent@cursor.com>
- Patch get/watch/clear on native only with watcher refcount start/stop - Map finite locationUpdate payloads; drop non-finite lat/lng Co-authored-by: Cursor <cursoragent@cursor.com>
- Await GpsBridge.requestPermissions in startGeoWatch on native - Deny exits Record via onGeoPermissionDenied; pause keeps stream Co-authored-by: Cursor <cursoragent@cursor.com>
- Install GpsBridge geolocation shim on native map init (no permission) - Uninstall and force-stop on recording.destroy / leave (D-09) Co-authored-by: Cursor <cursoragent@cursor.com>
- Extend GpsBridgePlugin with background/notifications permission methods - Stub ensureRockExplorerTrackingPermissions (always false until plan 03) - Wave 0 Jasmine coverage for D-08 order, disclosure, deny hard-stops Co-authored-by: Cursor <cursoragent@cursor.com>
- Add nativeGpsTrackingActive signal defaulting false (D-14) - Introduce CAPACITOR_APP InjectionToken for testable App listeners - Facade specs assert appStateChange → flushDraftQueue until plan 03 Co-authored-by: Cursor <cursoragent@cursor.com>
- Declare FGS location triad, BG location, POST_NOTIFICATIONS, and GpsForegroundService - Own FLP + ongoing notification in service; plugin start/stop + BG/notifications aliases Co-authored-by: Cursor <cursoragent@cursor.com>
- Implement ensureRockExplorerTrackingPermissions (FG→disclosure→BG→notifications) - Wire startGeoWatch to staged gate + ConfirmDialog Play disclosure - Clear nativeGpsTrackingActive on stop/destroy; normal-locale i18n only Co-authored-by: Cursor <cursoragent@cursor.com>
- Register appStateChange → flushDraftQueue on native; remove on destroy - Show minimal tracking-active chip in Record toolbar when signal is live - Assert pause keeps nativeGpsTrackingActive (D-07) Co-authored-by: Cursor <cursoragent@cursor.com>
Allow private-LAN API hosts and debug cleartext, skip hung startup fetches before instance onboarding, and rewrite MinIO loopback media to the runtime API host so Galaxy UAT works with adb reverse. Co-authored-by: Cursor <cursoragent@cursor.com>
- Lock RockExplorerLiveSessionGuard / LiveSessionEndHandlers contract - RED Cancel/Finish/Discard/passthrough/no-soft-continue expects (INST-07) Co-authored-by: Cursor <cursoragent@cursor.com>
- Same-host bypass passes; different-host runGuardedAction fails until plan 02 - Menu switchInstance/logout expect guard hooks (D-06/D-07) Co-authored-by: Cursor <cursoragent@cursor.com>
- GREEN RockExplorerLiveSessionGuard with Finish/Discard/Cancel sequencing - Add LiveSessionResolveDialogComponent and root DialogService Co-authored-by: Cursor <cursoragent@cursor.com>
- Register/clear live handlers from recording facade including discardActiveLiveSession - Guard Switch instance and logout before navigate/dispatch; picker host-change after D-08 Co-authored-by: Cursor <cursoragent@cursor.com>
- Add Finish/Discard/Cancel strings in en/de/fr/it/nl only (no gym mirrors) Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.