Closed barbeau closed 2 years ago
As mentioned in https://github.com/barbeau/gpstest/issues/306, users would also like to be able to log to Android Studio and eventually files when the screen turns off - this is the same issue.
We also get to specify a notification when running in the background - a user requested something similar to the GPS Status app:
GPS Tools looks like this:
Yeah, thanks for replay on Play Store, I personally absolutel support this topic, the way it is implemented in GPS Locker application for example! (duplicate my review) "Nice GPS testing app! Only one suggestion - add please ability to lock🔒GPS signal(status) after pressing back button (exit from app) for background staying Gps alive and don't loose 🛰 satellites! Of course as Option in Settings. It would be very useful, because for my own opinion as a solid user - your GPSTest app finds signal and lock satellites more faster and I am really love Sorting option in your app."
Related to implementing ViewModel and Service:
Given the most recent Android architecture principles and tools, it seems like the following is the best architecture:
This design is discussed here, with the below demo app currently using LocalBroadcastManager
instead of Room with Flow (with a plan to remove LBMs for Room or DataStore):
https://github.com/googlecodelabs/while-in-use-location/issues/12
DataStore is a possible alternative to Room, although with some of the more complex metadata it seems like Room is a better option.
Good example of LifecycleService: https://github.com/gs-ts/TrackMyPath/blob/c3bd825d4c3f2eaea37ca5e659eb6127f5778e14/app/src/main/java/com/gts/trackmypath/presentation/service/LocationService.kt
Some other good resources:
I've created a demo here (based on Google's code sample) in the room
branch that uses Room and Flow for location updates:
https://github.com/barbeau/while-in-use-location
And an article covering implementation: https://barbeau.medium.com/room-kotlin-flow-the-modern-android-architecture-for-location-aware-apps-9c110e12e31a
Here's a good example showing StateFlow with loading, etc. states: https://github.com/philipplackner/StateFlow
...from this YouTube video: https://www.youtube.com/watch?v=Qk2mIpE_riY&t=325s
See also: https://developer.android.com/kotlin/flow/stateflow-and-sharedflow#stateflow
Using MutableStateFlow without a backing private variable: https://link.medium.com/8Vn6X07Cwhb
With Jetpack Compose: https://youtu.be/QDcBO4iROyE
Good example of managing service restarts using KTX: https://github.com/android/health-samples/blob/f32c8956daa3f9e2c9bce6cf45102a8ae34bc22e/ExerciseSample/app/src/main/java/com/example/exercise/ExerciseService.kt
And example of music service in background using Kotlin: https://github.com/android/uamp/blob/99e44c1c5106218c62eff552b64bbc12f1883a22/common/src/main/java/com/example/android/uamp/media/MusicService.kt
Another possibility is to wrap the location listener in its own LocationRepository (e.g. instead of using Room).
Some guidance in this direction:
Guidance on converting callback APIs to flow:
Example of using Kotlin and LiveData with LocationManager/LocationRepo design, but uses PendingIntents - maybe we could convert this to Flow (?):
There is actually a snippet in this article: https://medium.com/androiddevelopers/a-safer-way-to-collect-flows-from-android-uis-23080b1f8bda
...that points to this Gist: https://gist.github.com/manuelvicnt/92d1bf42f413d519f0e2cf1aff0f9b9d#file-locationflow-kt
...which is close to what we want:
// Implementation of a cold flow backed by a Channel that sends Location updates
fun FusedLocationProviderClient.locationFlow() = callbackFlow<Location> {
val callback = object : LocationCallback() {
override fun onLocationResult(result: LocationResult?) {
result ?: return
try { offer(result.lastLocation) } catch(e: Exception) {}
}
}
requestLocationUpdates(createLocationRequest(), callback, Looper.getMainLooper())
.addOnFailureListener { e ->
close(e) // in case of exception, close the Flow
}
// clean up when Flow collection ends
awaitClose {
removeLocationUpdates(callback)
}
}
EDIT - the full code for the above snippet is in https://github.com/googlecodelabs/kotlin-coroutines/blob/master/ktx-library-codelab/step-06/
A good video visually explaining the differences between the different types of Flows with animations, in context of updating locations on a map: https://youtu.be/RoGAb0iWljg
And the corresponding repos:
Here's an article I wrote on wrapping the location listener in its own LocationRepository with Kotlin callbackFlow: https://barbeau.medium.com/kotlin-callbackflow-a-lightweight-architecture-for-location-aware-android-apps-f97b5e66aaa2
Link to example on GitHub is in the article. I'm using this pattern in GPSTest.
Summary:
GPSTest was originally implemented to remaining running in the background when you press the Home key. As discussed in https://github.com/barbeau/gpstest/issues/237 and https://github.com/barbeau/gpstest/issues/297, this has been interpreted to be a feature, not a bug, as it can be helpful to prime GPS in some situations.
However, given the advances in Android since 1.5, background execution should be overhauled to the current Android best practices, which means running it as a foreground Service when in the background. This will allow consistent behavior across devices and a clear notice to the user that GPSTest is still running in the background.
Some UI decisions need to be implemented here, including whether we prompt the user to ask if they want to continue running GPSTest in the background at some point, or just invert the default option and always have GPSTest stop running when the user puts the app in the background unless the user enables a setting to run in the background.
Steps to reproduce:
Start GPSTest and hit the Home button
Expected behavior:
Give me some notice that it's running in the background, and use an Android foreground Service
Observed behavior:
App simply doesn't de-register the LocationListener, which on some devices means it continues running in the background, while on other devices it will be killed.EDIT Dec 10, 2020 As of https://github.com/barbeau/gpstest/commit/ae93b7ffe79c19686c2179ad4181c92f4702509b, the LocationListener is always unregistered in Activity onPause().Device and Android version:
N/A