By Tim Judkins | Principal Engineer

Android 12 Geofences won’t trigger on a pending event unless you ensure the pending event is not immutable. This was a detail that seemed to slip through the cracks and wasn’t updated in Google’s examples and docs (at least not yet!). Apps that target Android 12 (SDK31) must now specify a mutability flag for any pending intents. The documentation and the errors in Android Studio indicate that PendingIntent.FLAG_IMMUTABLE must be used. However, if this flag is used for pending intents with geofences, the broadcast receiver will not have the information it needs to parse the GeofencingEvent from the intent. This article provides code snippets for fixing this. It is based on the Google Codelabs geofence example on github. At the time of writing this, the example had not been updated to target Android 12. To update the codelabs example to work when targeting Android 12, a few things need to be done. First, update the app’s gradle to target and compile against SDK 31. To update the codelabs example to work when targeting Android 12, a few things need to be done. First, update the app’s gradle to target and compile against SDK 31.
				
					android {
    compileSdkVersion 31
    defaultConfig {
        targetSdkVersion 31
        ...
    }
}

				
			

We also recommend updating gradle and switch jcenter() to mavenCentral() in the root build.gradle.

				
					buildscript {
    ext.kotlin_version = '1.5.30'
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:7.0.3'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}
allprojects {
    repositories {
        google()
        mavenCentral()
    }
}
				
			
The last gradle change is to remove the apply plugin: 'kotlin-android-extensions' statement from the app build.gradle since it is no longer needed. This will cause a few errors and warnings that need to be fixed before addressing the pending intent immutability flags. Add the and set the android:exported="true" attribute for the main activity in the AndroidManifest.xml. Now, we need to address the pending intent immutability. For the geofence pending intent, we need to explicitly tell the OS that this pending intent can be modified by adding PendingIntent.FLAG_MUTABLE. Since this flag is only supported on SDK31 and higher, we’ll need to do the following in the HuntMainActivity.
				
					private val geofencePendingIntent: PendingIntent by lazy {
        val intent = Intent(this, GeofenceBroadcastReceiver::class.java)
        intent.action = ACTION_GEOFENCE_EVENT
        // Use FLAG_UPDATE_CURRENT so that you get the same pending intent back when calling
        // addGeofences() and removeGeofences().
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
        } else {
            PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
        }
    }
				
			
If you want to get rid of the lint warning, add the following annotation to the activity: @SuppressLint("UnspecifiedImmutableFlag") The notification pending intent that is created in the NotificationUtils.kt file needs the PendingIntent.FLAG_IMMUTABLE since this intent should not be modified. To do so, update the sendGeofenceEnteredNotification method as follows (note the PendingIntent.FLAG_IMMUTABLE flag was introduced in SDK23):
				
					@SuppressLint("UnspecifiedImmutableFlag")
fun NotificationManager.sendGeofenceEnteredNotification(context: Context, foundIndex: Int) {
    val contentIntent = Intent(context, HuntMainActivity::class.java)
    contentIntent.putExtra(GeofencingConstants.EXTRA_GEOFENCE_INDEX, foundIndex)
    val contentPendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        PendingIntent.getActivity(
            context,
            NOTIFICATION_ID,
            contentIntent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
    } else {
        PendingIntent.getActivity(
            context,
            NOTIFICATION_ID,
            contentIntent,
            PendingIntent.FLAG_UPDATE_CURRENT
        )
    }
    // … the rest of the method is the same
}

				
			

While it’s not clear from the documentation that geofence pending intents need to be mutable, hopefully this will help developers avoid issues when using geofences on Android 12. A fork of the codelabs examples with these changes is available at https://github.com/RadiusNetworks/android-kotlin-geo-fences 

Flybuy Notify

Brands leverage Flybuy Notify to send proximity-triggered notifications to their app users as they approach and/or enter a store. Flybuy Notify can prompt loyalty and rewards, send special offers, and push mobile ordering when a customer is nearing a point of interest. Learn more about Flybuy Notify.

About the Flybuy Platform

Radius Networks is a location technology company that helps companies save time for customers and staff by streamlining operations and the user experience. The Flybuy SaaS platform is leveraged by restaurants, retailers, hospitality, and grocers around the world and includes: Flybuy Pickup for curbside, delivery, and in-store pickup; Flybuy Pay for mobile payment optimization; Flybuy Drive-Thru for loyalty identification and offer redemption; and Flybuy Tableside for innovative dine-in experiences.