Android 13 Runtime Notification Permission: XML & Jetpack Compose Guide

Foram Soni
Mindful Engineering
5 min readJul 31, 2023

--

Kick-starting Android 13 (API level 33), a significant update was introduced in the form of new runtime permission called POST_NOTIFICATIONS. It enables the apps to send non-exempt notifications, including Foreground Services (FGS). This update prioritizes the most important notifications to users, helping them stay focused on what’s important.

Runtime Permission

In the Android ecosystem, permissions are classified into various categories according to the specific resources or actions they regulate.

  1. Normal Permissions: These kinds of permissions are given whenever there is absolutely no risk to the user’s privacy or the operation of other apps. In such a kind of permission, no permission-granting dialog is prompt. Users cannot revoke this kind of permission.
    Eg: android.permission.VIBRATE
  2. Dangerous Permissions: Dangerous permissions are used whenever the app wants the data and resources that involve the user’s private information. Dialog of this permission is to be accepted by the user unless the app cannot provide functionality that depends on that permission.
    Eg: android.permission.CAMERA
  3. Signature Permissions: The system grants these app permissions at install time, but only when the app that attempts to use permission is signed by the same certificate that defines the permission.
    Eg: android.permission.BIND_INPUT_METHOD

Understanding Runtime Permission

In the earlier releases of Android, apps were automatically granted permission upon installation, leading to privacy and security worries. However, Android 6.0 (Marshmallow) introduced a significant change known as runtime permissions, which empowers users with greater control over their data.

Permission Dialog

  • Allow: The app can access the notification and perform the desired action for which the permission was requested.
  • Don’t allow: The “Don’t Allow” option empowers users to deny a permission request from the app. By selecting this option, the app will be restricted from accessing the notification and performing the requested action. The app may need to handle this denial gracefully and adjust its functionality accordingly.

Impact of Updates on Existing Apps

For eligible apps, the system provides temporary permissions based on certain criteria.

The duration of the temporary permission varies depending on the targetSDK:

  1. Android 13: The permission is valid until the first-time app launches the activity.
  2. Android 12L or earlier: The permission remains valid until the user selects an option in the permission dialog.

If the user dismisses the permission dialog without selecting an option, the temporary grant will be retained by the system.

Get your hands dirty let’s do some coding

From: https://gifer.com/en/2NJd

To declare permission in Manifest

Declare the new notification runtime permission in our AndroidManifest.xml file.

<manifest ...>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application ...>
...
</application>
</manifest>

First of all, you need to change the target and compile the SDK version of this app. Open app/build.gradle and you would see something like this:

android {
// ...
compileSdk 33 // Notice this line

defaultConfig {
targetSdk 33 // And this line!
// ...
}
// ...
}

Runtime Notification implementation with XML

After the user begins using your app, you can implement the following code to request permission for display notifications. The final decision to grant or deny permission lies entirely with the user.

class NotificationWithXMLActivity : AppCompatActivity() {

private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
if (isGranted) {
// make your action here
} else {
// Explain to the user that the feature is unavailable because the
// features requires a permission that the user has denied.
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
checkNotificationPermission()
}
}

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private fun checkNotificationPermission() {
val permission = Manifest.permission.POST_NOTIFICATIONS
when {
ContextCompat.checkSelfPermission(
this, permission
) == PackageManager.PERMISSION_GRANTED -> {
// make your action here
}

shouldShowRequestPermissionRationale(permission) -> {
Snackbar.make(
findViewById(R.id.parent_layout),
getString(R.string.notification_permission_required),
Snackbar.LENGTH_LONG
).setAction(getString(R.string.go_to_settings)) {
// Responds to click on the action
val uri: Uri = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
data = uri
startActivity(this)
}
}.show()
}

else -> {
// The registered ActivityResultCallback gets the result of this request
requestPermissionLauncher.launch(permission)
}
}
}
}

From the above code, first of all, permission status is identified.

Case 1: If the permission state status is PackageManager.PERMISSION_GRANTED notification can be received.

Case 2: If the permission state status is Rationale then Snackbar is called with an action button named “Go to Settings”. With a click of the action button system’s setting screen is opened.

Case 3: If all cases are not satisfied then the launcher is called. We have created a launcher with ActivityResultContracts to check whether the permission of Notification is granted or denied. If it is granted notification can be received. If the user denies explain to the user that the feature is unavailable because the feature requires permission that the user has denied.

Runtime Notification implementation with Jetpack Compose

If you are coding in Jetpack Compose the following code is implemented

class NotificationWithJetpackComposeActivity : ComponentActivity() {

private val requestNotificationPermission =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
// make your action here
} else {
// Explain to the user that the feature is unavailable because the
// features requires a permission that the user has denied.
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val scaffoldState = rememberScaffoldState()
NotificationTheme {
Scaffold(
scaffoldState = scaffoldState,
) { paddingValues ->
Box(Modifier.padding(paddingValues)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
CheckNotificationPermission()
}
Text(text = getString(R.string.show_notification))
}
}
}
}
}

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@Composable
fun CheckNotificationPermission() {
val permission = Manifest.permission.POST_NOTIFICATIONS
when {
ContextCompat.checkSelfPermission(
this, permission
) == PackageManager.PERMISSION_GRANTED -> {
// make your action here
}

shouldShowRequestPermissionRationale(permission) -> {
AlertDialog(
onDismissRequest = { },
text = { Text(text = getString(R.string.notification_permission_required)) },
confirmButton = {
TextButton(onClick = {
val uri: Uri = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
data = uri
startActivity(this)
}
}) { Text(text = getString(R.string.go_to_settings)) }
},
)
}

else -> {
requestNotificationPermission.launch(permission)
}
}
}
}

Recapitulating the above blog Runtime Notification Permission in Android 13 could potentially give users more control over which apps are allowed to show notifications in real-time, rather than granting this permission at the time of installation.

You can access the source code from GitHub.

I hope you liked this blog. Happy coding. :)

--

--