Skip to content

SA-99 - Add wake on leave to sleep screen#963

Draft
lukeduncan-scot wants to merge 17 commits into
mainfrom
SA-99-wake-on-leave
Draft

SA-99 - Add wake on leave to sleep screen#963
lukeduncan-scot wants to merge 17 commits into
mainfrom
SA-99-wake-on-leave

Conversation

@lukeduncan-scot

Copy link
Copy Markdown
Collaborator
  • Added wake on leave to sleep screen
  • Device wakes up when user navigates 12 metres from start location using CheapRuler.distance()) function
    • Refactored to encapsulate all logic into ViewModel, exposing state and effects to the composable that instantiates the VM.

- Hoisted state out into parent composable.
…function.

- Implementing functionality for GeoFence.
…ly invoke start in SharedSleepScreen

- Removed "context" from "start" as it was no longer required
- Moved AndroidLocationProvider back to app module and used AppCallback for function to provide LocationProvider in shared module
- Managing exit of screen
- Created events shared flow to manage button clicks and trigger functionality required.
- Using a Channel to send effects to the composable
- Removed onExit and onWakeUp as they are no longer required.

@davecraig davecraig left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm worried that this approach may consume too much battery. Users may leave the app in sleep-until-wake for days at a time. As mentioned in the Jira issue there are geo-fencing APIs for iOS and Android which are designed to allow low-power geo-fencing support. The app already requests "Background Battery Usage" so that when running with the phone locked it doesn't get put too much to sleep, but that will also affect the app when running in sleep-until-wake. I suspect that means that the app would be polling at 1Hz as each new GPS comes in.

FWIW Claude says:
The "wake on leave" feature (SharedSleepScreen.kt) implements geofencing entirely in shared Kotlin business logic, polling raw GPS and computing distance manually ? it does not use either platform's OS-level geofencing:

What it actually does (SharedSleepScreen.kt:170-210):

  • Starts continuous location updates via locationProvider.start()
  • On every emission, computes distance from the start point with CheapRuler
  • If distance > 12 m, calls destroy() to wake up

Android ? GooglePlayLocationProvider.start() (app/.../GooglePlayLocationProvider.kt:73-84):

  • Uses Priority.PRIORITY_HIGH_ACCURACY with a 1-second update interval and setMinUpdateDistanceMeters(1f)
  • This is continuous high-accuracy GPS polling, the opposite of low-power. The right API here is GeofencingClient/GeofencingRequest from com.google.android.gms.location (Play Services), which lets the OS handle region
    entry/exit at the hardware level with far lower power draw, and works even if the app/process is killed.

iOS ? IosLocationProvider (shared/.../IosLocationProvider.kt:19-23):

  • Uses kCLLocationAccuracyBest, distanceFilter = kCLDistanceFilterNone, allowsBackgroundLocationUpdates = true, pausesLocationUpdatesAutomatically = false ? again continuous best-accuracy tracking.
  • The low-power equivalent is CLLocationManager.startMonitoring(for: CLCircularRegion), which uses cell/Wi-Fi-based region monitoring and doesn't require the app to stay active polling GPS.

Note: CLCircularRegion is already imported elsewhere in the codebase (IosGeocoder.kt:10) for an unrelated purpose, so the type is available but isn't being used for this feature.

Practical impact: for a "sleep with wake-on-leave" feature ? likely used overnight or for extended periods ? continuously polling GPS at 1 Hz with best accuracy will drain the battery significantly compared to OS-level
geofencing, which is the main reason these APIs exist. This is worth flagging before merge; switching to GeofencingClient (Android) and region monitoring (iOS) would be a more substantial change than what's in this branch, so
it's a design question for the team rather than a quick fix.

@lukeduncan-scot

Copy link
Copy Markdown
Collaborator Author

That's fine, thanks for the summary. FWIW, Claude has missed the fact that I have used either the Fused client or the base Android Location APIs depending on whether the user has Play Services installed or not, as not every phone has it.

I was just using the functionality similar to that used in routes, which, upon looking at the API for geofencing, might actually be a candidate for replacing if we want to further reduce battery use.

I guess it all depends on how these are implemented. The implementation I have only receives an update when the LocationProvider emits a new location.

I'll take a look into the Geofencing APIs though it appears that the APIs are only available on Google Play Services: https://developers.google.com/android/reference/com/google/android/gms/location/Geofence.Builder.html

@davecraig

Copy link
Copy Markdown
Contributor

I'll take a look into the Geofencing APIs though it appears that the APIs are only available on Google Play Services: https://developers.google.com/android/reference/com/google/android/gms/location/Geofence.Builder.html
I think in that case my gut instinct is that I'd only enable this feature on phones with Google Play Services. AOSP phones simply wouldn't have the wake on move option.

I guess it all depends on how these are implemented. The implementation I have only receives an update when the LocationProvider emits a new location.
In the past I did look at all of the various sleep/deep sleep modes on Android, but mostly it was to try and stop the app from being affected by them... It wasn't totally clear to me what mode the phone enters if it's still on a table on a very smoothly moving train. In that case the app currently expects to continue getting GPS at 1Hz so as to update the user where they are. If the app detected that the phone really isn't moving then perhaps we could automatically switch to sleep mode with geofencing to wake it up and turn the service back on.

…ow power location monitoring for geofencing.

- Left original "start()" function in LocationProvider that now defaults to previous behaviour so to not break API.
…aving 2 functions. API will still be the same for consumers, they can just now add a parameter to request accuracy.

- Using Accuracy parameter in IosLocationProvider.
@lukeduncan-scot

Copy link
Copy Markdown
Collaborator Author

I'm having issues replicating the failures in the actions on my local Mac. I'm investigating.

@lukeduncan-scot

Copy link
Copy Markdown
Collaborator Author

Ideally the actions would fail fast, when one fails (meaning the PR will be rejected) the others won't run, as it's a waste of compute here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants