Monitoring GPS and Location Permission checks using LiveData

Tram Ho


In a recent project, we were working on a mobile app to fight the bug of electric cars because of the process of understanding the needs of users, the process of providing owners with timely data. They also provide a model to assess the suitability of electric vehicles. To make those things happen, the process of tracking driving behavior through apps is an essential aspect of the project. It means that the application should be able to always listen for location coordinates in an implicit execution stream during a user’s journey while driving.

We must perform a check to ensure that GPS is enabled on the device and Location Permission is issued by the user. It is an important and also essential point to ensure the application can perform Location Tracking during the driving process.

Use Case

Imagine a scenario when a user installs an application, travels, and can grant Location Permission but ignores the dialog that promotes GPS activation. Or worse, the user does not grant subsequent access or cancellation for any reason. It is now safe to ensure that users will even forget about the newly installed application and the intended goal is not achieved.

Our best bet is to tell the eye visually when the application is in close-up to encourage users to perform one or the other essentials at the same time. This is only possible when the UI (Activity / Fragment) can listen to the GPS and Runtime Permission changes and re-interact correctly.

I faced a few challenges while doing that and determined to share my learning process and solutions. It’s not the best solution ever but a good work up to now and I will become happier and more grateful to hear the feedback on making it better.

Before moving on to the implementation part, I want to mention that our team decided to use Architecture Components so it was a natural choice to respond to LiveData during the implementation of GPS and Permission listening.

LiveData is an Observable data holder and lifecycle-aware. It allows the components in your application, usually the UI, to listen for changes to data objects .

Listening to GPS Status

Users can often enable or disable GPS on their device.

Implementing GPS LiveData

We must inherit LiveData to create a customized LiveData for us.

GpsStatus is a sealed class and reflects the status of enable or disable.

Sealed classes are appropriate when a value can have one or more types from a limit set, but cannot have any other type .

I read in some places that sealed classes are enums with something stolen and can’t help but disagree. I also got some inspiration from Craig’s article that stated that sealed classes are perfect when you want to send commands to the listening entity that is forced to obey.

Two very important methods of LiveData are onActive () and onInactive () . They allow listening for changes only when Activity (or any other LifecycleOwner) is started or resumed and only then will LiveData release any items. It means no memory leaks and NullPoiterExceptions are caused because the view doesn’t exist.

Every time a change occurs on the GPS Provider, the onReceiver () of the Broadcast Receiver will be called and the related GpsStatus status will be issued to the observer.

If you notice, postValue is used here because we need to set a value from the background thread as well as another alternative using setValue when the method needs to be called from the main thread.

Observing GPS Status LiveData

For introductory purposes, an example application that displays the status of the GPS via the TextView title is clickable and also through a custom dialog.

Example UI reacting to the disabled state of GPS

Example UI reacting to the disabled state of GPS

Custom LiveData to display through the ViewModel

But there is a flaw here.

Think carefully before sharing an instance of LiveData through consumers .

Remember that LiveData sends the final value to a new observer. So it is better to take special care that you are using Dagger for Dependency injection because I had a mistake about using @Singleton scope and it took some troubleshooting time for a strange behavior.

To start listening for changes in the example activity in my case, you have to listen to it. Also pass to observer () to indicate that our activity is LificycleOwnser and observer here. We don’t have to worry about unsubscribing because it’s handled by Android.

gpsObserver is where you will specify how the UI will respond to the state changes of GPS. I want to highlight that Sealed classes combine very well with the when expression.

Listening to Runtime Permission Status

Starting with Android 6.0 and above, every application is required to ask users for permission to grant access to one or more so-called dangeous permissions . It is a wall to suggest users during the journey and also reminds it each time an activity is performed that requires permission.

Starting with Android 6.0 (API level 23), users can revoke permissions from any app at any time even for apps with targets lower than this API level .

Android request permission

Android request permission

Implementing Runtime Permission LiveData

Similarly, we must inherit LiveData to create a customized LiveData. My idea is to create a generic object that can be used for any particular runtime permission.

PermissionStatus is a sealed class and reflects the status of granted, denied, and blocked.

Whenever PermissionStatusListener is called, it checks the current state of the runtime permission passed to LiveData

Observing Runtime Permission LiveData

For our case, we only care about Location Permission . We intentionally create an optional (optional) for the user while on the journey, which means we will need to always check it before making location tracking. More details on this will be introduced in the next section.

Also, Location Permission is checked before and after GPS and the dialog is kept present whenever the user enters the foreground state. This is obvious just to show how the UI can respond to it.

Custom LiveData is released through ViewModel

Permission passed to PermissionStatusListener should correspond to what is defined in AndroidManifest.xml.

To start listening for changes in Activity for example, you must observer it. permissionObserver shows how to react to other states.

I am using the Permission library to handle the process of requesting permission in an application, the process of calling a native dialog and responding to callbacks based on user input. I find it lightweight, easy to use and customizable and I liked it while doing my business logic.

Full source code is available on github repo .

Updated Use Case

Above, we have learned how to perform tests using LiveData and listen to them on the UI side. Next, we will try to discover how we can use it when our application is in the background.

Visualize a context when a user installs an application, traverses key features, grants Location Permission, and enables GPS when required. There is a risk that users may revoke permissions anytime in the future for any reason. Even worse, users start driving without turning on GPS. Those journeys will never be tracked and the application will not be able to follow its intended purpose.

Listening to GPS and Location Permission Status

Location Tracking is implemented as a LocationService because this should have been running in the background and listening for location updates from FusedLocationProviderClient .

If the service is started and running for a long time, the system will reduce its position in the list of background tasks over time, and the chances are the service will be killed .

The only solution is to create a server that runs in the background and it needs to show an active notification in the status bar . If your application has target API level 26 or higher, read more about the updated rules to start a service. In addition, applications with a target of Android 9 (API level 28) or higher, using foreground services need to request FOREGROUND_SERVICE permission.

Foreground Notification aims to notify users when location tracking is being performed.

Foreground Notification aims to notify users when location tracking is being performed.

As before, we can listen to LiveData in Activity because it implements the LifecycleOwner interface.

In our case, we need to listen for the changes from GPS and Runtime Permission LiveData within the LocationService. If you read the comments in the LifecycleOwner class you will notice that it does not refer to the Service as a candidate. What to do now?

In my personal opinion, there’s a problem when Android doesn’t make it stand out enough in terms of source code and documentation and overlooks it which will help developers save time.

In fact, this service should start every time a driving action is defined, but that part is performed beyond the scope of how to listen to the location coordinates. We try to simulate that this section uses buttons in an example application for demo purposes.

If by the time the start service event starts, one or more requests are incomplete, the corresponding messages are created and the service is stopped. The same thing happens when the service is ready to run while the GPS is off or the Location permission is revoked.

Observing both LiveData

During the initialization phase, we need to do something like this when the application can respond based on threads simultaneously. The intended goal is to update the text if both are not provided. For demo, I’m creating two separate announcements. In addition, the process of unsubscribing location updates is important even if one of them is not provided because it does not serve any purpose.

The problem with this approach is that it adds unnecessary complexity when trying to simultaneously handle separate threads that can occur independently of each other. In fact, an ideal situation would be if we could interact with both threads at the same time as if it were a Pair of simultaneous states.

To make this happen, we will do this differently and fortunately Architecture Components has some for us.

MediatorLiveData is a subclass of LiveData that can listen to other LiveData objects and interact based on events that change from them.

With a MediatorLiveData, we can interact with GpsStatus emitted by GpsStatusListener and PermissionStatus emitted by PermissionStatusListener . I have found a very suitable way here and have determined to use it. Here is how the source code does it:

The LiveData results are updated every time one of the input streams is updated and MediatorLiveData performs the process of consolidating these sections for us. For those familiar with RxJava, this behavior is the same as the CombineLastest function correctly.

To listen to this LiveData inside the LocationService, the source code will look like this with the help of an extension function .

You can view the entire source code on Github .

Hope this article will be helpful for you. Take a look at other interesting posts here .

Source—–d8822ab951a6——- —————


Share the news now

Source : Viblo