Android Location Permission Guide

Yevhenii Smirnov
Yevhenii Smirnov
Jan 26 2024
Posted in Engineering & Technology

A straightforward guide for location permission in Android.

Android Location Permission Guide

Welcome to this straightforward guide on managing location permissions in Android. Throughout this guide, our primary focus will be on the technical aspects of handling location permissions, setting aside specific use cases for now.

Before delving into the code, let's categorize location permission requests into two main parts:

  • Foreground Location
  • Background Location

Declare Permissions in the Manifest

The permissions required by your app should be declared in the manifest. While we already handle these declarations in our SDK, it's still worth mentioning.

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

  <!-- Precise location access. -->
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <!-- Background location access. -->
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</manifest>

Requesting Foreground Permission

Foreground location access can be either Approximate or Precise. For most scenarios, you should include both permissions in the request.

Check if the permission is granted before making the request:

    private fun checkLocationPermissionStatus(): Boolean {
        val permission = Manifest.permission.ACCESS_FINE_LOCATION

        return ContextCompat.checkSelfPermission(
            requireContext(),
            permission
        ) == PackageManager.PERMISSION_GRANTED
    }

Request the permission:

    // First, declare your permission launcher

    private val foregroundLocationPermissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestMultiplePermissions()
    ) { permissions ->
        val granted = permissions.any { it.value }

        if (granted) {
            // Permission is granted
        }
    }

    // Request the permission

    private fun ensureForegroundLocationPermission() {
        foregroundLocationPermissionLauncher.launch(
            arrayOf(
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.ACCESS_FINE_LOCATION
            )
        )
    }

This is a very basic example, and we are not handling a particular scenario - when we should show permission rationale. It is not mandatory, but highly recommended, as this may be the key to obtain the permission. Usually, we should show the rationale if the permission has been denied previously.

In the next example, I will combine permission check, request and rationale in one, so it is much easier to maintain and understand.

    private fun ensureForegroundLocationPermission(): Boolean {
        val permission = Manifest.permission.ACCESS_FINE_LOCATION
        val granted = ContextCompat.checkSelfPermission(
            requireContext(),
            permission
        ) == PackageManager.PERMISSION_GRANTED

        // Permission has been granted
        if (granted) return true

        if (shouldShowRequestPermissionRationale(permission)) {
            AlertDialog.Builder(requireContext())
                .setTitle("Your title")
                .setMessage("Your message")
                .setCancelable(false)
                .setPositiveButton("Ok") { _, _ ->
                    // Requesting foreground location permission
                    foregroundLocationPermissionLauncher.launch(
                        arrayOf(
                            Manifest.permission.ACCESS_COARSE_LOCATION,
                            Manifest.permission.ACCESS_FINE_LOCATION
                        )
                    )
                }
                .setNegativeButton("Cancel") { _, _ ->
                    // Foreground location permission rationale cancelled
                }
                .show()

            return false
        }

        // Requesting foreground location permission
        foregroundLocationPermissionLauncher.launch(
            arrayOf(
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.ACCESS_FINE_LOCATION
            )
        )

        return false
    }

Requesting Background Permission

This step is relevant only if foreground permission has been previously requested and granted.

You might wonder what if only approximate foreground location is granted?

Even if only approximate foreground location is granted, you can still request background permission. Whether precise or approximate foreground location was granted earlier, background location updates will align accordingly, maintaining either precision level.

To request background permission, we first declare our launcher:

    private val backgroundLocationPermissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { granted ->
        if (granted) {
            // Permisison granted
        }
    }

Checking, handling rationale and requesting the permission:

    private fun ensureBackgroundLocationPermission(): Boolean {
        val permission = when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> Manifest.permission.ACCESS_BACKGROUND_LOCATION
            else -> Manifest.permission.ACCESS_FINE_LOCATION
        }

        val granted = ContextCompat.checkSelfPermission(
            requireContext(),
            permission
        ) == PackageManager.PERMISSION_GRANTED

        if (granted) return true

        if (shouldShowRequestPermissionRationale(permission)) {
            AlertDialog.Builder(requireContext())
                .setTitle("Title")
                .setMessage("Message")
                .setCancelable(false)
                .setPositiveButton("Ok") { _, _ ->
                    // Requesting background location permission
                    backgroundLocationPermissionLauncher.launch(permission)
                }
                .setNegativeButton("Cancel") { _, _ ->
                    // Background location permission rationale cancelled
                }
                .show()

            return false
        }

        // Requesting background location permission
        backgroundLocationPermissionLauncher.launch(permission)

        return false
    }

Conclusion

I aimed to make this guide as straightforward as possible, and for the most part, it should be sufficient. However, there's more to explore, including requesting only approximate foreground permission, transitioning from foreground approximate location to precise, dealing with permanently denied permission, and more.

Although, for the most use cases, you don't have to worry about those aspects, it's always beneficial to have a comprehensive understanding of how location services work, isn't it?

Stay tuned for my upcoming blog post, where I will explore these topics and more!

As always, you can find us available for any question you might have via our Support Channel.

Keep up-to-date with the latest news