Location-Based Apps Road Map in Android

Deniz Subaşı
hepsiburadatech
Published in
7 min readJan 2, 2023
Photo by Denise Jans on Unsplash

In this article, I will explain step-by-step what to do when developing location-based applications in an android application, what you will be encounter and how we find solutions to these problems.

Before you start developing location-based applications, you will need to think ahead and decide on some issues:

  • Determining whether the application needs a location in the background.
  • Show the user with a notification as long as the location is taken. (For Android 12 (API level 31) and later, an app has to show a notification if background services are running)
  • To show why we need the user’s location with an understandable UI.
  • Needs an understandable UI for the situation where the user does not give location permission and when the location cannot be obtained from the location providers.
  • How often the user’s location will be accessed (This duration is important, for example, if you want to use PeriodicWorkRequest, you will encounter a minimum of 15 minutes interval times).
  • The user may not want to share their location. In this case, your application should know how to behave.

You will encounter some problems.

  • To write Huawei HMS location provider instead of the existing location provider, since some Huawei devices do not have Google services.
  • Killing the services of our applications in the background in order to save charge on Chinese devices such as XIAOMI, OPPO.
  • Blocking locations from fake GPS applications.
  • If you need to get a location in the background, you will need to tell Google in detail when uploading your application to google play.
  • You will need to do some UI work. Before asking for location permission, you need to explain in detail to the user why you need location permission.

1. Determining the method of obtaining location

As I mentioned before, there are some methods provided by Android in order to determine the method to get the location in a certain time, some of them are:

WorkManager

We can get the user’s location as periodic with PeriodicWorkRequest so that we can work at a certain interval, but as I mentioned before, we have a minimum time limit of 15 minutes while using PeriodicWorkRequest. You can ignore this option if you want to get a location under 15 minutes.

Fused Location Provider

Using FLP’s own interval feature to get a location at a certain interval. This method is the most used and the most useful method, but where and how we use it is important. If we are doing this operation on the Activity/Fragment, we get the location only within the activity/fragment lifecycle, so if the activity is killed, our location provider will be killed.

Fused Location Provider + Service

In this case, it would make more sense to use it within a foreground service, to save it from an activity/fragment lifecycle. We get the location at a certain interval in the service, everything is very good, but later on, you will see that the service is up and the location interval is not working. We experience this situation mostly in XIAOMI and OPPO devices, because services such as location services that consume the most battery are killed by the system, so we cannot get the location even if our services are standing. The next step is that even our services do not run when the android device goes into idle

Fused Location Provider + Service + Alarm Manager

Above, I wrote about starting an interval with the FLP location request in the service and why we couldn’t get a location after a while. To overcome this problem and to take the responsibility of getting a precise location in a certain time from FLP, we set the exact alarm for the next location request with AlarmManager and we always do this for the next location request.

2. Location Permissions Flow

Checking if the location permission has been granted

fun Context.checkIfLocationPermissionsAreGranted(): Boolean =
if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.Q) {
checkIfPermissionsAreGiven(
listOf(
Manifest.permission.ACCESS_BACKGROUND_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
)
} else {
checkIfPermissionsAreGiven(
listOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
)
}

The important thing is that if you want to have the location in the background, you need to ask for this permission after Api version 29 and later.

Before asking for permission, it is necessary to show a detailed and understandable UI about why you want this permission. When uploading the application to google play, you need to explain in detail why you need this permission to Google.

For detailed information on the process, you can check here.
https://support.google.com/googleplay/android-developer/answer/9799150

Check if device location is active

This part has 3 steps.

  1. Checking whether the location providers are active
  2. If the location providers are not active, we check the LocationSettingsStatus. If this place gives us a ResolvableApiException we start an intent by using this, and the system asks the user to open the location if the location is closed.
  3. If the user did not open the location even in the system dialog, we show a UI with a UI that explains why he cannot access some features when the location is closed and why he should open the location, by putting a button, and when clicked, we start an intent that goes to the location settings so that the user can easily open the location and return to the application.
fun Context.checkIfDeviceLocationProvidersAreEnabled(): Boolean {
val manager = getSystemService(Context.LOCATION_SERVICE) as? LocationManager
return manager?.isProviderEnabled(LocationManager.NETWORK_PROVIDER)?.orFalse() &&
manager?.isProviderEnabled(LocationManager.GPS_PROVIDER)?.orFalse()
}


private suspend fun checkLocationAvailability() =
suspendCoroutine<ResolvableApiException?>{continuation->
val locationRequest = LocationRequest.create()
locationRequest.priority= Priority.PRIORITY_HIGH_ACCURACY
val builder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest)
val result =
LocationServices.getSettingsClient(context).checkLocationSettings(builder.build())
builder.setAlwaysShow(true)
result.addOnCompleteListener{task->
try {
task.getResult(ApiException::class.java)
// All location settings are satisfied. The client can initialize location
continuation.resume(null)
} catch (exception: ApiException) {

when (exception.statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED-> {
try {
// Cast to a resolvable exception.
val resolvable = exception as? ResolvableApiException
continuation.resume(resolvable)
} catch (e: Exception) {
continuation.resume(null)
}
}
else -> {
continuation.resume(null)
}
}
}
}
}

// system asks the user to open the location if the location is closed.
private fun openLocationResolutionActivity(resolution: ResolvableApiException) {
resolution.startResolutionForResult(
this, ENABLED_DEVICE_LOCATION_REQUEST_CODE
)
}

3. Establishing the Location Structure.

We need to consider some things beforehand when creating the structure:

  • We should be able to abstract our location structure, we can set the exact alarms as we want with AlarmManager for today, but we should be able to change this structure quickly when a restriction comes in new updates. In conclusion, the structure should not depend on the AlarmManager.
  • Our codebase is the same, but our location providers must be different. What I mean here is that our structure should be divided into layers when we want to support devices with HMS.

Above, I mentioned Fused Location Provider + Service + Alarm Manager method. We use this method to get the location in a certain loop in the background. Our aim is to obtain the location with the most flexible and guaranteed solution.

  1. Restarting location services every time the application is opened.
  2. Creating the exact alarm for the next location request.
  3. When the alarm is triggered, capture with the broadcast receiver and get the location.

These steps are our three main features. But not only that, we also need to abstract it with these 3 basic features for both HMS and google location providers.

ForegroundService

It is necessary to show a notification as long as the location is taking. (For Android 12 (API level 31) and later, an app has to show a notification if background services are running) **

Set next location request time

We took the responsibility of the location request cycle from the location provider and gave it to the alarm manager, which we think is a more efficient solution, briefly creating an alarm for the next location request time and giving a BroadcastReceiver, when BroadcastReceiver triggers, we get the location and set the new alarm for the next location request.

private fun setNextLocatinRequesTime() {
val manager = context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
val calendar: Calendar = Calendar.getInstance()
calendar.timeInMillis= System.currentTimeMillis()
calendar.add(Calendar.SECOND, DEFAULT_LOCATION_FREQUENCY)
manager?.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
getAlarmManagerPendingIntent()
)
}

Check Fake GPS Providers

Some users can activate the developer mode and use fake gps applications and abuse a feature of your application. In order to prevent this, you can check whether the location from the location provider comes from a mock provider.

private fun Location?.checkIfMockLocation(): Boolean {
return try {
this?.isFromMockProvider.orFalse()
} catch (e: Exception) {
false
}
}

4. Logging

If you have a location-based feature and there is a problem in this process, it should be clearly shown to users. If the location is closed, it should be shown with a popup or if the location cannot be accessed, it is necessary to know the reason. No matter which tool you use to better analyze all these problems and follow the process, the best method is to log the user’s actions in the most critical places (FusedLocationProvider exceptions, checking permissions, the status of the device location feature) and to identify the places where users are most stuck.

Conclusion
I tried to explain what should be done while developing a location-based application, what kind of problems will we encounter, how we follow location permissions, location providers and the problems we encountered while solving them. Please leave a comment if you have any questions or feedback.

--

--