Foreground location service: permissions, notification, battery exemption (#15) #97
No reviewers
Labels
No labels
area:companion
area:docs
area:shared
area:tooling
area:watchapp
blocker
kind:chore
kind:feature
kind:spike
kind:test
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
robert/PedalPebble!97
Loading…
Reference in a new issue
No description provided.
Delete branch "area/foreground-location-service"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #15.
What this does
RideService(companion/ride/src/main/kotlin/de/butzei/pedalpebble/ride/RideService.kt) — the foregroundServicethat keeps GPS updates alive for a ride: a persistent low-importance notification (mandatory for any foreground service),android:foregroundServiceType="location"plus the API 34+FOREGROUND_SERVICE_LOCATIONpermission, a partial wake lock held only while a ride is running, andSTART_STICKYrestart handling that resumes the same running state if the system kills the process. Declared in the app module's manifest, not:companion:ride's own, per that module's existing manifest note that a foreground service's notification/lifecycle is an app-composition concern.AndroidLocationSource(:companion:location) — the GPS source behind it, onandroid.location.LocationManager's API 31+LocationRequest.Builderat 1 Hz (NFR-B2). No Google Play services dependency: minSdk is already 31, which is exactly where that builder API starts.RideSetupActivity(:companion, reached only from a new "Start ride" button — never at app launch) walks the rider through each permission one at a time, each with its own plain-language rationale screen (NFR-S6):ACCESS_FINE_LOCATION+ACCESS_COARSE_LOCATIONtogether, thenACCESS_BACKGROUND_LOCATIONas a separate later request, thenPOST_NOTIFICATIONS(skipped below API 33), then the battery-optimisation exemption.RideSetupState(:companion:core) — the ordering above as a pure,android.*-free sealed-class decision function, unit tested on the JVM lane (RideSetupStateTest, 8 cases).RidePermissions(:companion:location) is the thin Android-side bridge that supplies its real inputs.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONSdialog, not a Settings redirect — Play's Battery Optimization policy accepts this for apps whose core function is continuous background location tracking, which is exactly what a ride's foreground GPS service is. Checked against developer.android.com and Play's policy centre (2026-09-04); seeBatteryOptimizationIntents's kdoc for the citation.Verified
./gradlew :companion:assembleDebug— succeeds, clean../gradlew :companion:core:test— passes, including the newRideSetupStateTest../gradlew :companion:lintDebug— succeeds; the only new finding is an informationalInlinedApinote on thePOST_NOTIFICATIONSreference (expected and harmless — minSdk 31, compileSdk 37).companion/build/intermediates/packaged_manifests/debug/.../AndroidManifest.xml): all permissions present,RideServicedeclared withforegroundServiceType="location".Not in scope here (by design)
Actual speed/distance computation and BLE wheel-sensor arbitration are #17/#19. Full ride-session state restoration across a process death (elapsed distance, position, etc.) is #12 — this issue's restart handling only proves the platform-survival shell (notification, wake lock, GPS updates) resumes, not that a ride's numbers come back with it. A real multi-hour screen-off ride verification is a manual/field-test step this sandbox can't perform.
https://claude.ai/code/session_01DAoXbRmJUf2uxNYBfdAXPt