Changes in Android 12

Joris Verbogt
Joris Verbogt
Jul 16 2021
Posted in Best Practices

Precise location and notifications handling

Changes in Android 12

In a previous blog post, we've already touched some of the changes that are to come in Android 12 (and iOS 15). In this post, we will explain how these changes will impact your app, and specifically the implementation of Notifications and Location Updates in the Notificare Android SDK.

To prepare your app and make it compatible with the changes in Android 12, there are 3 important parts that need to be addressed:

  • Handling of notification opens and actions from the notification manager
  • Explicit permission for precise location and BTLE beacon scanning
  • Verification of deep links in your app

Let's go over them one by one.

Notification Handling

Up until version 2.6 (and Android 11), the Notificare SDK used an intent receiver to handle the opening of notifications from the notification manager, thereby automatically logging these opens before launching an actual Activity to show the necessary user interface.

Android 12 no longer allows intent receivers to act as a so-called "trampoline" for opening notifications. Instead, notifications that need to display a user interface must directly open an activity. The only type of notification that can (and will) be handled in the background is a quick reply from the notification manager. Since those actions do not need any further user interface, they can still be handled by the intent receiver.

This means that for all other notification opens and actions, from version 2.7 onward, your application's (main) activity should handle the logging and open the necessary user interface, thereby acting as the "trampoline".

The changes to be made in code are minimal and it only require one extra entry in your manifest.

To make sure opens (and actions) from the notification manager end up in your activity, add an intent filter for re.notifica.intent.action.RemoteMessageOpened to AndroidManifest.xml:

<activity android:name=".MainActivity">
    
    <intent-filter>
        <action android:name="re.notifica.intent.action.RemoteMessageOpened" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>

    <!-- other intent filters -->
    
</activity>

Then, in the intent handling code of your activity, parse the incoming intent:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Notificare.shared().addNotificareReadyListener(this);
    handleIntent(getIntent());
}

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    handleIntent(intent);
}

protected void handleIntent(Intent intent) {
    if (Notificare.shared().handleTrampolineIntent(intent)) {
        Log.d(TAG, "trampoline intent handled");
    } else {
        // handle other intent
    } 

The handleTrampolineIntent method will take care of necessary logging and displaying the appropriate user interface like the intent receiver does in the current SDK 2.6.

If your activity was also set up to handle the re.notifica.intent.action.NotificationOpened intents, please keep doing whatever your app does there now, it should work fine.

However, if your app overrides and customises the onNotificationOpened method in the intent receiver, you will have to move that code to your handling of the re.notifica.intent.action.RemoteMessageOpened intents. The exact details on how your code changes depend on what your app actually does there and is beyond the scope of this blog post.

Location and Bluetooth Permissions

As mentioned in the previous blog post, location updates in Android are now approximate by default and apps require explicit permission from the user to use a precise location. Your app now needs to ask for both COARSE and FINE location permissions at the same time when asking users for location updates.

If your app uses the Notificare SDK's helper methods to request permissions, this is taken care of for you automatically.

If you are handling the permission requests yourself, please make sure you change your code to request both permissions.

One important caveat (and one you will want to explain to your users) is that Geo-fencing will only work with Precise location permission granted. Obviously, the existing (extra) requirement for background location permission still remains necessary and needs to be requested after the user has granted foreground location permissions.

Another change to permissions in Android 12 is the requirement to explicitly allow your app to scan for bluetooth devices. If your app makes use of Bluetooth Beacons, you will now need to explicitly ask the user for this permission too.

The Notificare SDK 2.7 adds some helper methods for this, analogous to those for location permission.

Your activity could use a method like the following:

public void askBluetoothScanPermission() {
    if (Notificare.shared().hasBeaconSupport()) {
        if (!Notificare.shared().hasBluetoothScanPermissionGranted()) {
            Log.i(TAG, "permission not granted");
            if (Notificare.shared().shouldShowBluetoothScanRequestPermissionRationale(this)) {
                // Here we should show a dialog explaining location updates
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setMessage(R.string.alert_bluetooth_scan_permission_rationale)
                        .setTitle(R.string.app_name)
                        .setCancelable(true)
                        .setNegativeButton(R.string.button_location_permission_rationale_cancel, (dialog, id) -> {
                            Log.i(TAG, "bluetooth scan not agreed");
                        });
                builder.setPositiveButton(R.string.button_location_permission_rationale_ok, (dialog, id) -> Notificare.shared().requestBluetoothScanPermission(this, BLUETOOTH_SCAN_PERMISSION_REQUEST_CODE));
                builder.create();
                builder.show();
            } else {
                Notificare.shared().requestBluetoothScanPermission(this, BLUETOOTH_SCAN_PERMISSION_REQUEST_CODE);
            }
        } else {
            Log.i(TAG, "bluetooth scan permission granted, we can scan beacons");
            Notificare.shared().enableBeacons(30000);
        }
    }
}

And then handle the permission result as usual:

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case BLUETOOTH_SCAN_PERMISSION_REQUEST_CODE:
            if (Notificare.shared().checkRequestBluetoothScanPermissionResult(permissions, grantResults)) {
                Log.i(TAG, "bluetooth scan permission granted");
                Notificare.shared().enableBeacons(30000);
            }
            break;
    }
}

Notificare Loyalty Deeplinks

There is one more change in Android 12 that requires an update to your app. You are no longer allowed to open non-verified HTTP(S) intents. This means that for all deep links that do not use a custom URL scheme, you will need to verify those domains.

If your app uses Notificare loyalty passes, there is one important deeplink that your app will need: the link to open a pass from, for example, an email. In the current SDK, these links are defined like so in your manifest:

<activity
    android:name="re.notifica.ui.PassbookActivity"
    android:exported="true">

    <intent-filter>
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data
            android:host="https://push.notifica.re"
            android:pathPrefix="/pass/forapplication/${notificareAppId}"
            android:scheme="https" />
    </intent-filter>
</activity>

Those links (https://push.notifica.re/pass/forapplication/${notificareAppId}) will not be opened anymore by your app in Android 12.

Instead, your app needs to switch to a new link format that will be introduced with the next release of the API. These links will look like https://${notificareAppId}.applinks.notifica.re/pass

You will need to add those to your manifest like so (don't forget the autoVerify!)

<activity
    android:name="re.notifica.ui.PassbookActivity"
    android:exported="true">

    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data
            android:host="${notificareAppId}.applinks.notifica.re"
            android:pathPrefix="/pass"
            android:scheme="https" />
        <data
            android:host="https://push.notifica.re"
            android:pathPrefix="/pass/forapplication/${notificareAppId}"
            android:scheme="https" />
    </intent-filter>
</activity>

For the verification to succeed, your app's package name and fingerprints need to be registered in the Notificare Dashboard, in the same way they already need to be registered for Dynamic Links.

Please refer to the documentation for Links to see how to fill the necessary fields.

Beta SDK available

We are aware that this information may still sound a bit preliminary, as we are still some months away from the Android 12 release. But if you are currently in the process of preparing your app for Android 12, you can already start testing with our 2.7 beta release.

To use the beta release, please change your dependencies config in your build.gradle as follows:

repositories {
    google()
    maven {
        url "https://maven.notifica.re/releases"
    }
    maven {
        url "https://maven.notifica.re/prereleases"
    }
}

dependencies {
    implementation 're.notifica:notificare-core:2.7.0-beta.2'
    implementation 're.notifica:notificare-core-hms:2.7.0-beta.2'
    implementation 're.notifica:notificare-location:2.7.0-beta.2'
    implementation 're.notifica:notificare-location-hms:2.7.0-beta.2'
    implementation 're.notifica:notificare-beacon:2.7.0-beta.2'
    implementation 're.notifica:notificare-scannable:2.7.0-beta.2'
    implementation 're.notifica:notificare-scannable-hms:2.7.0-beta.2'
}

The changes mentioned in this blog post will only take full effect when actually building with a target SDK version 31 (Android 12). Regardless, the code changes to your app are necessary when implementing the 2.7 SDK, although the app will still work when it targets an SDK version lower than 31.

Get ready for the future

If you want to give the beta SDK 2.7 a try, please go ahead and don't hesitate to give us your feedback if you run into issues. As always, we are available via our Support Channel for any questions you might have.

Keep up-to-date with the latest news