Categories Sensors

How to properly implement Android Q Location Permissions

27 Mar. 2019
1
0
19 minutes

Even though we do not know yet which dessert will be chosen by the Android team to represent Android Q version, we have a lot of information regarding the new features it includes. Indeed, Android opened Android Q Developer Preview 1 on March 13, 2019. This new version includes great features that we tested. This post comes after the first post about Android Q Settings Panels:

Android Q brings a number of additional privacy and security features for users, as well as new APIs for connectivity, telephony, accessibility, and more. It also includes some enhancements for foldable devices. Android published detailed articles with regard to behaviour changes, privacy
enhancements
and new features.

Android Q

For now, Android Q Beta is only available on Google Pixel devices (Pixel 3, Pixel 3 XL, Pixel 2, Pixel 2 XL, Pixel, or Pixel XL). Nevertheless, it is possible to set up an Android Emulator to run Android Q Beta. The procedure is described in Get Android Q Beta article.

Introduction

Location data strongly increases the possibilities for Android Apps. It allows the developers to build amazing apps that help you to find a restaurant nearby, discover events in your town or to give you directions as you drive, run or walk. However, location is also one of the most sensitive types of personal information. That’s why Android Q brings the new Device Location Permission.

One of the purposes of Android Q is to give users more control over what is happening on its device. It includes the control of the location use. In prior versions of Android, users only had a single control to allow or deny the app access to the device location. Therefore, there was no distinction between background use and foreground of the device location. With Android Q, users are now able to control the access level of an application to the device location. In other words, users can choose to provide their location to apps:

  • All the time: An app with such permission can access the device location at any time whether it is running in the foreground or in the background.
  • While in use: An app with such permission can access the device location only while it is being used in the foreground.
  • Deny: An app with such property cannot access the device location whether it is running in the foreground or in the background.
Android Q – Connectivity Panel

Some apps may only need the device location while the app is being used. For instance, an application that helps you to find shops nearby only need the device location when the user opens the application and performs a search query deliberately.

On the other hand, some applications may need the device location whether it is running in foreground or in background. For example, an application that tracks you during a trip in order to keep track of the places you visited may need the device location without requiring you to interact with the application.

Android Q now offers this greater level of device location access. As there are thousands of apps that rely on the device location, this update may impact a lot of applications. This post describes how to adapt your applications to this new feature.

Video

Setup

First and foremost, you need to set up the Android Q SDK. In order to achieve this, two updates are required in Tools > SDK Manager. Android Q Preview (or higher) must be installed in SDK Platforms tab. Furthermore, Android SDK Build-Tools 28 (or higher) is also required. It can be installed in SDK Tools tab.

Afterwards, it is necessary to update the build configuration of your app as follows:

android {
    compileSdkVersion 'android-Q'

    defaultConfig {
        targetSdkVersion 'Q'
    }
    ...
}

Implementation

To support the new device location permission, Android Q introduced a new location permission:ACCESS_BACKGROUND_LOCATION Unlike the existing location permissions (ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION), this new permission only affects apps that request the device location in the background. To clarify, an app is considered to be in the background if it is not visible and it is not running a foreground service.

Request foreground location

If your app targets Android Q and needs access to device location only when running in the foreground, you don’t need to request ACCESS_BACKGROUND_LOCATION permission. IndeedACCESS_COARSE_LOCATION is sufficient. You need to specify it in your app’s manifest as follows:

<manifest>
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
</manifest>

Some apps may access to the device location in the foreground after a user interaction and require to keep this access, even after the user turns its device’s display off or presses the Home button.

Such apps can retain access to the user location by starting a foreground service. Basically, it is a service that performs an operation that is noticeable by to the user through a notification. Foreground services keep running even when the user isn’t interacting with the application. For instance, a music player or a file downloader would use a foreground service.

A foreground service must be declared in your app’s manifest as follows:

<manifest>
  <service
    android:name="NavigationService"
    android:foregroundServiceType="location" ... >
    ...
  </service>
</manifest>

As foreground services require the ACCESS_COARSE_LOCATION permission, you need to check at runtime if this permission has been granted by the user. A check of the app’s access to the device location would look like this:

if(PermissionUtils.isPermissionGranted(this, Manifest.permission.ACCESS_COARSE_LOCATION)) {
    showSnackbar("App has permission to access location in the foreground")
} else {
    ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION),
        REQUEST_CODE_FOREGROUND_LOCATION_PERMISSION)
}
if(PermissionUtils.isPermissionGranted(this, Manifest.permission.ACCESS_COARSE_LOCATION)) {
    showSnackbar("App has permission to access location in the foreground");
} else {
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_CODE_FOREGROUND_LOCATION_PERMISSION);
}


isPermissionGranted() is a static method we defined in PermissionUtils for convenience. You need to define a request code for each permission request in order to retrieve the corresponding responses in onRequestPermissionsResult().

object PermissionUtils {
    fun isPermissionGranted(context: Context, permission: String): Boolean {
        return ActivityCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
    }
}
public class PermissionUtils {
    public static boolean isPermissionGranted(Context context, String permission) {
        return ActivityCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED;
    }
}

As you can see, first we need to check if the ACCESS_COARSE_LOCATION permission has been granted by the user. If it is granted, the application has permission to access the device location in the foreground. Therefore we can start our foreground service and access the location. Otherwise, we must make a request for the foreground location access. In such a case, a dialog will display the request to the user. When the user responds to your app’s permission request, the system invokes
onRequestPermissionsResult() method, with the result for permission request.

Here is the method that shows the Snackbar at the right place:

private fun showSnackbar(message: String) {
    Snackbar.make(coordinator_layout, message, Snackbar.LENGTH_SHORT)
        .show()
}
private void showSnackbar(String message) {
    CoordinatorLayout coordinatorLayout = findViewById(R.id.coordinator_layout);
    Snackbar.make(coordinatorLayout, message, Snackbar.LENGTH_SHORT)
            .show();
}

You can find more details on Snackbar implementation in this post:

Requesting a foreground permission in Android Q should look like this:

Foreground location request

The above screenshot shows that the request is unequivocal. It only concerns the foreground use of the device location. If a user has denied the permission request and the application requests it a second time, the dialog will be quiete different as you can see below.

Foreground location request after a first deny

The user can choose to deny the request and not be asked again.

Request background location

If your app targets Android Q and needs to access to the device location in the background, you must declare the ACCESS_BACKGROUND_LOCATION permission in addition to the ACCESS_COARSE_LOCATION permission in your app’s manifest.

 <manifest>
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</manifest>

Some apps may need access to the device location in the background on a periodic basis or all the time. For instance, apps that allow users to share their location with friends fall into this category. Such applications request the device location periodically through a background service.

<service
    android:name="FriendLocationSharingService" ... >
    ...
</service>
Note that it is unnecessary to include a foreground service type for services in applications that user has granted all-the-time access to the device location.

Even though the user grants your app all-the-time access to its location, he may still revoke this permission. For this reason, whenever your app starts a service that relies on device location, you must check whether the user still allows your app to access its device location in the background. Such a check can be realized as follows:

if(PermissionUtils.isPermissionGranted(this, Manifest.permission.ACCESS_COARSE_LOCATION)) {
    if(PermissionUtils.isPermissionGranted(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
        showSnackbar("App has permission to access location in the foreground as well as in the background")
    } else {
        ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
            REQUEST_CODE_BACKGROUND_LOCATION_PERMISSION)
    }
} else {
    ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION,
        Manifest.permission.ACCESS_BACKGROUND_LOCATION),
        REQUEST_CODE_BACKGROUND_LOCATION_PERMISSION)
}
if (PermissionUtils.isPermissionGranted(this, Manifest.permission.ACCESS_COARSE_LOCATION)) {
    if (PermissionUtils.isPermissionGranted(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
        showSnackbar("App has permission to access location in the background as well as in the foreground");
    } else {
        // App only has access to the device location in the foreground
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
                REQUEST_CODE_FOREGROUND_LOCATION_PERMISSION);
    }
} else {
    // App hasn't access to the device location, either in foreground or in background
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.ACCESS_COARSE_LOCATION,
                    Manifest.permission.ACCESS_BACKGROUND_LOCATION},
            REQUEST_CODE_BACKGROUND_LOCATION_PERMISSION);
}

As you can see, this code is pretty similar to the check for foreground location permission. The check for background location permission is nested in the check for foreground location permission and the right permission is requested if it is missing.

Requesting background location permission in Android Q should look like this:

Background location request

The above screenshot shows that the user has total control over the application access to the device location. If a user has denied the permission request and the application requests it a second time, the dialog will be quiete different as you can see below.

Background location request after a first deny

The user can choose to deny the request and not be asked again.

Here is the implementation of onRequestPermissionsResult() method :

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
    when(requestCode) {
        REQUEST_CODE_FOREGROUND_LOCATION_PERMISSION-> {
            if(grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission was granted
            } else {
                // Permission was denied
            }
            return
        }
        REQUEST_CODE_BACKGROUND_LOCATION_PERMISSION-> {
            if(grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission was granted
            } else {
                // Permission was denied
            }
        }
    }
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    switch (requestCode) {
        case REQUEST_CODE_FOREGROUND_LOCATION_PERMISSION:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // permission was granted
            } else {
                // permission denied
            }
            return;
        case REQUEST_CODE_BACKGROUND_LOCATION_PERMISSION:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // permission was granted
            } else {
                // permission denied
            }
    }
}

Download

  • Kotlin
  • Java

Conclusion

To sum up, Android Q Location Permissions give users more control over when apps can get access to their location. By checking and requesting location permissions as shown in this course, your apps can successfully keep track of its access level to the device location. In conclusion, remember to request only for the permissions you need because inappropriate permission request can repulse the users.

Leave a Reply

Your email address will not be published. Required fields are marked *