Approximate location

On Android 12 (API level 31) and higher, users have the option to control the location accuracy that they want to grant to their apps. On earlier versions of Android, location accuracy is implicitly controlled by whether the app requests the ACCESS_FINE_LOCATION permission or the ACCESS_COARSE_LOCATION permission.

This codelab will provide guidance and best practices for apps whose target SDK version is 31 and higher to request location permissions.

Prerequisites

What you will learn

  • How to request location permissions in Android S
  • How to implement an optimal location request flow, so that users can grant your app precise location permissions

Terms

Precise location: the granularity of location for the ACCESS_FINE_LOCATION permission.

Approximate location: the granularity of location for the ACCESS_COARSE_LOCATION permission.

APIs to covered

New UI flow

Let's first have a look at the new UI to get some ideas of the location accuracy changes.

Figure 1a and Figure 1b, below, show the permission dialog that appears when your app has neither the ACCESS_FINE_LOCATION permission nor the ACCESS_COARSE_LOCATION permission granted.

7acc5e2fe09d67ca.jpeg a5e9363364fcc9f9.jpeg

Figure 1a: Precise location is selected Figure 1b: Approximate location is selected

Figure 2a, below, shows the permission upgrade dialog that appears when your app has been granted the ACCESS_COARSE_LOCATION permission while in use. This dialog appears if the user selects "While using the app" for Approximate location from the previous screen (shown in Figure 1b).

2624d89993700ea5.jpeg

Figure 2a

Figure 2b, below, shows the permission upgrade dialog that appears when your app has been granted the ACCESS_COARSE_LOCATION permission for only the current session. This dialog appears if the user selects "Only this time" from the previous screen (shown in Figure 1b).

a2dfb923b8f3548d.jpeg

Figure 2b

Declare location permissions in your app's manifest

In order to request ACCESS_FINE_LOCATION, the system will require your app to declare both ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION in your app's manifest. Otherwise, the system ignores the request and doesn't grant your app either permission.

<manifest ... >
  <!-- Required when requesting precise location access on Android 12 (API level 31) and higher. -->
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
</manifest>

Request the ACCESS_FINE_LOCATION permission

MyActivity.kt (Kotlin)

when {
    ContextCompat.checkSelfPermission(
            CONTEXT,
            Manifest.permission.ACCESS_FINE_LOCATION
            ) == PackageManager.PERMISSION_GRANTED -> {
        // You can use the API that requires the permission.
        performAction(...)
    }
    shouldShowRequestPermissionRationale(...) -> {
        // In an educational UI, explain to the user why your app requires this
        // permission for a specific feature to behave as expected. In this UI,
        // include a "cancel" or "no thanks" button that allows the user to
        // continue using your app without granting the permission.
        showInContextUI(...)
    }
    else -> {
        // Ask for both the ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permissions. 
        requestPermissions(CONTEXT,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, 
                        Manifest.permission.ACCESS_COARSE_LOCATION),
                REQUEST_CODE)
    }
}

MyActivity.java (Java)

if (ContextCompat.checkSelfPermission(
        CONTEXT, Manifest.permission.ACCESS_FINE_LOCATION) ==
        PackageManager.PERMISSION_GRANTED) {
    // You can use the API that requires the permission.
    performAction(...);
} else if (shouldShowRequestPermissionRationale(...)) {
    // In an educational UI, explain to the user why your app requires this
    // permission for a specific feature to behave as expected. In this UI,
    // include a "cancel" or "no thanks" button that allows the user to
    // continue using your app without granting the permission.
    showInContextUI(...);
} else {
    // Ask for both the ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permissions.
    requestPermissions(CONTEXT,
            new String[] { Manifest.permission.ACCESS_FINE_LOCATION,             
                           Manifest.permission.ACCESS_COARSE_LOCATION },
            REQUEST_CODE);
}

Handle the response

MyActivity.kt (Kotlin)

override fun onRequestPermissionsResult(requestCode: Int,
        permissions: Array<String>, grantResults: IntArray) {
    when (requestCode) {
        REQUEST_CODE -> {
            // If the request is cancelled, the result arrays are empty.
            if (grantResults.isNotEmpty()) {
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // ACCESS_FINE_LOCATION is granted
                } else if (grantResults[1] == 
                               PackageManager.PERMISSION_GRANTED) {
                    // ACCESS_COARSE_LOCATION is granted
                }
            } else {
                // Explain to the user that the feature is unavailable because
                // the feature requires a permission that the user has denied.
                // At the same time, respect the user's decision. Don't link to
                // system settings in an effort to convince the user to change
                // their decision.
            }
            return
        }

        // Add other 'when' lines to check for other
        // permissions this app might request.
        else -> {
            // Ignore all other requests.
        }
    }
}

MyActivity.java (Java)

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
        int[] grantResults) {
    switch (requestCode) {
        case PERMISSION_REQUEST_CODE:
            // If the request is cancelled, the result arrays are empty.
            if (grantResults.length > 0) {
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // ACCESS_FINE_LOCATION is granted
                } else if (grantResults[1] == 
                               PackageManager.PERMISSION_GRANTED) {
                    // ACCESS_COARSE_LOCATION is granted
                }
            } else {
                // Explain to the user that the feature is unavailable because
                // the feature requires a permission that the user has denied.
                // At the same time, respect the user's decision. Don't link to
                // system settings in an effort to convince the user to change
                // their decision.
            }
            return;
        }
        // Other 'case' lines to check for other
        // permissions this app might request.
    }
}

Figure 4 shows the permission dialog that appears when your app requests only the ACCESS_COARSE_LOCATION permission.

9d20729f14673547.jpeg

Figure 4

To work with only the approximate location permission, your app needs to declare and handle only the approximate permission in each step.

Figure 5, below, shows the new location permission settings. There's a new toggle switch for users to control whether the app can access precise location or approximate location.

a9553249c3e2b90c.jpeg

Figure 5

The new location precision options (precise/approximate) are not visible to apps that target SDK version 30 and below.

You've learned how to request location permission in Android 12, and successfully explored many key components of location permissions!

You can now request location permissions for different use cases to meet your app's needs.

Learn more about location: