Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Android secondary position stream for foreground notification updates #1406

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

androidseb
Copy link

Changes summary

These changes aim to make it possible to implement a GPS track recording app with optimal battery usage in the background while also having a highly responsive UI while the app is in the foreground, without having to declare the ACCESS_BACKGROUND_LOCATION permission, essentially fulfilling the following requirements on Android:

  • When the app is active in the foreground, access location through a high frequency update stream (every 1 second)
  • When the app is inactive in the background, access location through a lower frequency update stream (every 5 seconds) to preserve battery
  • Avoid using the ACCESS_BACKGROUND_LOCATION permission at all

Without these changes, a limitation of this library on Android is that you can only have a single location stream active at a given time. And a new location stream cannot start when the app is in the background unless you have declared and granted access to the dangerous ACCESS_BACKGROUND_LOCATION permission. This limitation does not affect iOS since you can still start a location stream for a little while after the app has been put in the background.

If you want to change the location stream parameters (e.g. distanceFilter, intervalDuration, etc.), you have to cancel your current stream subscription and start a new one. Ideally, an app recording location in the background sets parameters to reduce the location updates frequency to preserve battery, but keeps a high frequency update while the app is in the foreground.

These changes allow to have two separate streams (started while the app is in the foreground) with different parameters, and cancel them individually:

  • If foregroundNotificationConfig is null, then the regular stream is started
  • If foregroundNotificationConfig is set, then the foreground service stream is started

When the app is put in the background, the high frequency UI stream can be canceled while the lower frequency foreground service stream remains active.

How to use this in your code today

If you want to test / use this code before it has been merged and published, you can use the following dependency overrides in your pubspec.yaml.

dependency_overrides:
  geolocator:
    git:
      url: https://github.com/androidseb/flutter-geolocator
      ref: feature/android_secondary_position_stream
      path: geolocator
  geolocator_android:
    git:
      url: https://github.com/androidseb/flutter-geolocator
      ref: feature/android_secondary_position_stream
      path: geolocator_android

Draft disclaimer

This is currently a draft, and the pre-launch checklist below is not complete, because I'm not sure if this is a direction the maintainers of this library would want to take. If so, please let me know, I'll put in the work to complete the checklist and polish this PR.

Pre-launch Checklist

  • I made sure the project builds.
  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I updated pubspec.yaml with an appropriate new version according to the pub versioning philosophy, or this PR is does not need version changes.
  • I updated CHANGELOG.md to add a description of the change.
  • I updated/added relevant documentation (doc comments with ///).
  • I rebased onto main.
  • I added new tests to check the change I am making, or this PR does not need tests.
  • I made sure all existing and new tests are passing.
  • I ran dart format . and committed any changes.
  • I ran flutter analyze and fixed any errors.

@orkun1675
Copy link

Hi @androidseb I was actually looking to achieve the same thing using geolocator and posted #1569 recently. I hadn't seen this PR until now.

A design question: what do you think about being able to modify the stream parameters (distanceFilter and intervalDuration) without terminating the ForegroundService as opposed to creating two position update streams?

Has this PR worked well for your? Were you able to test it IRL?

And could you say more re: iOS implementation. How do you update the distanceFilter on the iOS side?

@androidseb
Copy link
Author

Hi @orkun1675, I'll respond to your questions below. Note that those answers are assuming you only have the "while using the app" location permission and not the "always" location permission.

what do you think about being able to modify the stream parameters (distanceFilter and intervalDuration) without terminating the ForegroundService as opposed to creating two position update streams?

I think it would be convenient if it worked, but from my understanding, this will not work unless the app is running in the foreground: the Android SDK APIs used to subscribe to location updates only allow you to cancel your subscription, not modify it - if you want to modify your subscription settings (e.g. intervalDuration), you need to cancel your subscription and re-subscribe with the new settings, and unless your app has the "always" location permission, the Android system will deny your request to create a new subscription from the background, even if your foreground service for another subscription is already running.

Has this PR worked well for your? Were you able to test it IRL?

Yes, it works perfectly well for my use case, I use it for the tracks recording feature of my Map Marker app here (not open source):
https://play.google.com/store/apps/details?id=com.exlyo.mapmarker

iOS implementation. How do you update the distanceFilter on the iOS side?

The iOS implementation does not require those two position streams, because on iOS, it is technically possible to modify the existing subscription while the app is running in the background, or at least shortly after the app has entered background.

@orkun1675
Copy link

Thanks a lot for the pointers @androidseb !

you need to cancel your subscription and re-subscribe with the new settings

I don't think this is neccesary. Calling requestLocationUpdates with new settings replaces the params of the existing listener. Please see my post here. In that same post you will see a link to my fork that implements that approach which has been working well, even when running tests IRL.

unless your app has the "always" location permission, the Android system will deny your request to create a new subscription from the background

Great point. Right now our app DestiWake requires the "Location Always" permission. I haven't tested this approach without that permission.

We hope to remove "Location Always" in the future; I figure I'll have to revisit this then.

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