Notification Delegates
In Android 10 (API level 29), it is possible for an application to post notifications for another application. This is called using notification delegate.
Basic Usage​
Let's say we have 2 Android applications:
- App 1:
com.hanmajid.app1
- App 2:
com.hanmajid.app2
App 1 will set its notification delegate to App 2. This means that App 2 can post notifications through App 1's notification channel.
Setting Up App 2​
To do this, let's setup App 2 first. The first thing we want to do is add App 1's package name in <queries>
tag inside AndroidManifest.xml
file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application>
</application>
<queries>
<package android:name="com.hanmajid.app1" />
</queries>
</manifest>
This will allow App 2 to be aware of App 1's existence in user device.
The next thing to do is for App 2 to call canNotifyAsPackage()
and notifyAsPackage()
methods:
canNotifyAsPackage()
method will check whether App 2 is allowed to post notification to App 1.notifyAsPackage()
method will post the actual notification to App 1.
Here, I have prepared a simple utility object to wrap the 2 methods:
package com.hanmajid.app2
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import android.util.Log
import androidx.core.app.NotificationCompat
/**
* Notification delegate-related utility object
*/
object NotificationDelegateUtil {
/**
* Post a dummy notification as [packageName] through [channelId].
*
* When an error occurred, it will log the exception to Logcat.
*/
fun postNotificationAsPackage(
context: Context,
packageName: String,
channelId: String,
): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val notificationManager = context.getSystemService(NotificationManager::class.java)
val canNotify = notificationManager.canNotifyAsPackage(packageName)
if (canNotify) {
try {
notificationManager.notifyAsPackage(
packageName,
null,
1,
NotificationCompat.Builder(context, channelId)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("Ready for Upside Down Cake?")
.setContentText("Tap here to learn more about Android 14")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.build()
)
return true
} catch (e: Exception) {
Log.e("TAG", e.message, e)
return false
}
} else {
Log.e("TAG", "canNotify == false")
return false
}
} else {
Log.e("TAG", "API level < 29")
return false
}
}
}
Then we only need to call the method above somewhere. In this example, let's call this method in a button click callback:
package com.hanmajid.app2
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
import com.hanmajid.app2.ui.theme.App2Theme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
App2Theme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column {
Text(text = "This is App 2!")
Button(onClick = {
val isSuccess = NotificationDelegateUtil.postNotificationAsPackage(
this@MainActivity,
"com.hanmajid.app1",
"channel-for-app2"
)
if (!isSuccess) {
Toast.makeText(
this@MainActivity,
"Failed to post notification. Read error in log",
Toast.LENGTH_LONG,
).show()
}
}) {
Text(text = "Notify to App 1")
}
}
}
}
}
}
}
Here's what App 2 should look like now:
Make sure to run App 2 at least once so that it's installed in your device.
Setting Up App 1​
Now, we need to setup App 1. The first thing we need is to add App 2's package name in <queries>
tag inside AndroidManifest.xml
file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application>
</application>
<queries>
<package android:name="com.hanmajid.app2" />
</queries>
</manifest>
This will allow App 1 to be aware of App 2's existence in user device.
The next thing we need to do is create a notification channel for App 2 to post their notifications. Somewhere in App 1, create the channel like this:
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
// Call this somewhere in the application
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = context.getSystemService(NotificationManager::class.java)
val channelId = "channel-for-app2"
val name = "Notification Channel From App 2"
val channel = NotificationChannel(
channelId,
name,
NotificationManager.IMPORTANCE_DEFAULT,
)
notificationManager.createNotificationChannel(channel)
}
Notice that we use the same notification channel id (channel-for-app2
) as defined in App 2. This will allow the two apps to communicate properly.
Lastly, we need to use the setNotificationDelegate()
method to set App 2 as App 1's notification delegate:
import android.app.NotificationManager
import android.content.Context
import android.os.Build
// Call this somewhere in the application
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val packageName = "com.hanmajid.app2"
val notificationManager = context.getSystemService(NotificationManager::class.java)
notificationManager.notificationDelegate = packageName
val isSuccess = notificationManager.notificationDelegate == packageName
Log.i("TAG", "isSuccess: $isSuccess")
}
Make sure that the notification delegate is properly set by checking the value returned by getNotificationDelegate()
method. If everything went correctly, the value should be "com.hanmajid.app2"
.
Putting It All Together​
Now that we have both App 1 and App 2 installed in our device, we can test the notification delegate by running App 2 and press the "Notify to App 1" button: