The indigitall SDK can manage the user's location. This allows you to use the location filters on the send push campaign screen ( Campaigns> Push> New push campaign > Filters> Geographical Filters)

Once we have enabled this functionality, the end user will have to give their consent to the location permission and enable the location services of their smartphone, so that the application can obtain the exact location of the user.
x
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>You can find the AndroidManifest.xml file in the following path:

Options to keep in mind
There are two ways to manage location permissions:
- Manual: It is the default option and the developer is in charge of managing the location permissions.
- Automatic: It is the SDK that manages the location permissions.
Here we explain how to configure location permissions in automatic mode.
The AutoRequestPermissionLocation parameter must be added when the SDK is initialized. This is done using the following code excerpt:
//When you want to initialize indigitall
Configuration config = new Configuration
.Builder("<YOUR-APP-KEY>", "<YOUR-SENDER-ID>")
.setAutoRequestPermissionLocation(true)
.build();
Indigitall.init(context, config);In Android there is a class where the status of the permissions is received after the user has changed them through the configuration. The following piece of code should be added to capture the status of the permissions:
//In the class you use to receive the results of asking for the permissions
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Indigitall.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}Request background location: Android 11 and higher
When a feature in your app requests background location on a device that runs Android 10 (API level 29), the system permissions dialog includes an option named Allow all the time. If the user selects this option, the feature in your app gains background location access.
On Android 11 (API level 30) and higher, however, the system dialog doesn't include the Allow all the time option. Instead, users must enable background location on a settings page, as shown in following figure.
To handle background location updates reliably on Android, the SDK uses WorkManager, the official Android library for scheduling deferred and persistent background tasks.
The dependency:
implementation("androidx.work:work-runtime-ktx:2.9.0")Provides:
- Kotlin-friendly APIs using coroutines.
- Reliable Workers that can run even if the app is closed or the device restarts.
- Full compatibility across all Android versions and background execution restrictions.
- Automatic retries and constraints, ensuring tasks complete under system limitations.
The SDK requires this library to schedule and execute background location tasks in a consistent and system-compliant way.
Using a version lower than 2.9.0 may lead to critical compatibility issues. > Older versions of WorkManager do not fully support the strict background execution and "Expedited Job" requirements introduced in recent Android versions. This can result in:
- Location updates being throttled or blocked by the OS.
- Runtime crashes due to missing internal APIs.
- Inconsistent behavior when the device enters Battery Saver mode.
Troubleshooting: Version Conflicts
If your project already uses a different version of WorkManager, you might encounter a Duplicate Class or Method Not Found error. You can force the project to use the compatible version by adding this to your root build.gradle or build.gradle.kts:
configurations.all {
resolutionStrategy {
force("androidx.work:work-runtime-ktx:2.9.0")
}
}ProGuard / R8 Configuration
If you are using R8 (enabled by default in most Android projects) or ProGuard, you must ensure that the WorkManager classes and your custom Workers are not obfuscated. This allows the system to instantiate them correctly in the background.
Add the following rules to your proguard-rules.pro file:
# Keep WorkManager internal classes
-keep class androidx.work.** { *; }
# Keep your specific Worker implementation
# Replace 'com.yourpackage' with your actual package name if necessary
-keep class com.yourpackage.location.LocationWorker { *; }
Troubleshooting & Compatibility
| Issue | Potential Cause | Recommended Solution |
|---|---|---|
| Worker not found | ProGuard/R8 renamed the Worker classes during minification. | Add the -keep rules to your proguard-rules.pro file. |
| Tasks not running | Using an outdated WorkManager version (lower than 2.9.0). | Update dependency to androidx.work:work-runtime-ktx:2.9.0. |
| Silent Failures | Device-specific battery optimizations (e.g., "Doze Mode"). | Check if the app is exempt from battery optimization in system settings. |
| Missing Updates | Missing ACCESS_BACKGROUND_LOCATION permission. | Ensure the user has granted "Allow all the time" location access. |
App targets Android 11 or higher
If your app hasn't been granted the ACCESS_BACKGROUND_LOCATION permission, and shouldShowRequestPermissionRationale() returns true, show an educational UI to users that includes the following:
- A clear explanation of why your app's feature needs access to background location.
- The user-visible label of the settings option that grants background location (for example, Allow all the time in previous figure). You can call getBackgroundPermissionOptionLabel() to get this label. The return value of this method is localized to the user's device language preference.
- An option for users to decline the permission. If users decline background location access, they should be able to continue using your app.
Simple Example code without explanation background access
class MainActivity : AppCompatActivity() {
[...]
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
Indigitall.onRequestPermissionsResult(this, requestCode, permissions, grantResults)
if (requestCode == Constants.REQUEST_PERMISSION_CODE) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Location permission granted")
// Show info about background permissions
requestBackgroundPermission(this)
} else {
Log.e(TAG, "Location permission denied")
Toast.makeText(
this,
"Location permission is required for this feature",
Toast.LENGTH_SHORT
).show()
}
} else if (requestCode == BACKGROUND_LOCATION_REQUEST_CODE) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Background location permission granted")
} else {
Log.e(TAG, "Background location permission denied")
}
}
}
// go to location permission menu
fun requestBackgroundPermission(activity: Activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ActivityCompat.requestPermissions(
activity,
arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
BACKGROUND_LOCATION_REQUEST_CODE
)
}
}Complete example code
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// Pass the result to the SDK
Indigitall.onRequestPermissionsResult(this, requestCode, permissions, grantResults)
when (requestCode) {
Constants.REQUEST_PERMISSION_CODE -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Fine location granted")
// Ask for background permission after fine location is granted
requestBackgroundPermissionWithRationale()
} else {
Log.e(TAG, "Fine location denied")
if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) {
// Show rationale before asking again
showRationaleDialog(
"We need your location to send you personalized notifications. Without this permission, some features will not be available."
) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
Constants.REQUEST_PERMISSION_CODE
)
}
} else {
Toast.makeText(
this,
"Permission is required. Enable it in Settings if you have permanently denied it.",
Toast.LENGTH_LONG
).show()
}
}
}
BACKGROUND_LOCATION_REQUEST_CODE -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Background location granted")
} else {
Log.e(TAG, "Background location denied")
}
}
}
}
private fun requestBackgroundPermissionWithRationale() {
val permission = Manifest.permission.ACCESS_BACKGROUND_LOCATION
when {
ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED -> {
Log.d(TAG, "Background location already granted")
}
shouldShowRequestPermissionRationale(permission) -> {
// Show rationale before requesting background location
showRationaleDialog(
"To continue receiving notifications even when the app is closed, we need background location access."
) {
requestBackgroundPermission(this)
}
}
else -> {
// Request directly if no rationale is needed
requestBackgroundPermission(this)
}
}
}
fun requestBackgroundPermission(activity: Activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ActivityCompat.requestPermissions(
activity,
arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
BACKGROUND_LOCATION_REQUEST_CODE
)
}
}
private fun showRationaleDialog(message: String, onPositive: () -> Unit) {
AlertDialog.Builder(this)
.setTitle("Permission required")
.setMessage(message)
.setPositiveButton("Allow") { _, _ -> onPositive() }
.setNegativeButton("Cancel", null)
.show()
}
