Showing a notification in an android phone involves few steps:
- Create a notification channel (for Android 8.0 and above).
- Create notification.
- Check notification permission (for Android 13 and above).
- Show notification.
We will be creating a utility Kotlin object NotificationUtils that will handle the notification
related work and expose some functions to show notification.
So let's start.
- In
NotificationUtils.ktcreate these extension functions and properties so that it can be called easily. They will help us to create notification channel.
object : NotificationUtils {
private const val NOTIFICATION_CHANNEL_ID = "notification_deep_link_channel_id"
private val Context.notificationManager
get() = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
fun Context.createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
NotificationChannel(
NOTIFICATION_CHANNEL_ID, //a unique id
"App Notification", //channel name
NotificationManager.IMPORTANCE_HIGH //priority of the notification from this channel
).also(notificationManager::createNotificationChannel) //create notification channel
}
}- Create an
Appclass which will extendsApplicationclass. It acts as the entry point and serves as a singleton instance that's created when the app is launched.
class App : Application() {
//called before initiating any
//component of the app like Activity
override fun onCreate() {
super.onCreate()
//Create required notification channel before
//entering application.
//if the channel already exist then
//it won't be created
createNotificationChannel()
}
}Register this class in the AndroidManifest.xml so that Android will know about our
implementation of Application class and will use it.
<application
android:name=".App"
android:allowBackup="true"
...
- In
NotificationUtils.kt, create these extension functions. They will help us to show notification:
object NotificationUtils {
private const val NOTIFICATION_ID = 23423
private const val ENTER_APP_REQUEST_CODE = 92384
val Context.isNotificationPermissionGranted
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
ContextCompat.checkSelfPermission(
this,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
else true
fun Context.sendNotification() {
val notification = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setContentTitle("Demo notification Title") //title
.setSmallIcon(R.drawable.ic_notification) //notification icon
.setContentText("Click the action button to enter app") //notification content
.setPriority(NotificationCompat.PRIORITY_HIGH) //notification priority
.addAction(getEnterAppAction())
.build()
//show notification only when permission is granted
if (isNotificationPermissionGranted)
notificationManager.notify(
//unique ID for notification for
//handling it later (e.g. cancelling it)
NOTIFICATION_ID,
notification
)
}
private fun Context.getEnterAppAction() =
NotificationCompat.Action.Builder(
R.drawable.ic_notification,
"Enter App",
PendingIntent.getActivity(
this,
ENTER_APP_REQUEST_CODE,
Intent(this, MainActivity::class.java),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
)
.build()
}getEnterAppAction()returns a Notification actions. An action is a like a text button shown below the notification body by using which user can perform specific actions directly from the notification without fully opening the app. Notification actions are associated withPendingIntentinstances that define the action's behavior when triggered. This can include opening an activity, service, or broadcast. In the above code, we are requesting to open anMainActivitywhen the action is clicked by the user.isNotificationPermissionGrantedtells us whether the app has permission to show notification. If the Android version is greater or equal to 13 then check for Notification permission else returntruebecause in lower android we don't need get notification permission to show notification.
- In
MainActivitycreate a button which will be used to show notification when clicked
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
private val permissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) {
if (it)
runAfterPermissionGranted?.invoke()
}
//consider it as a pending task that needs to be executed
//after the permission is granted
private var runAfterPermissionGranted: (() -> Unit)? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.notifyButton.setOnClickListener {
//when button click, show notification
checkAndNotify()
}
}
private fun checkAndNotify() {
//isNotificationPermissionGranted defined in NotificationUtils.kt
if (isNotificationPermissionGranted)
//permission granted, show notification
sendNotification()
else {
Toast.makeText(
this,
"Please allow notification permission in order to show notification",
Toast.LENGTH_SHORT
).show()
//assign the task that will be executed if the
//permission is granted
runAfterPermissionGranted = { sendNotification() }
//if the permission is not granted, request for it
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}
}
}The big challenge was to check Notification permission before showing
notification. That I tackle with creating a extension property for Context
isNotificationPermissionGranted that returns boolean according to the permission
state. If the permission is not granted, I am requesting for it and then only
we are showing. I am also showing a toast, so that even permission dialog will not be shown
(happens if user declines two or more times), user get notified that that
the app needs notification permission.
- We can check and show a rationale dialog before requesting permission stating why we need notification permission. In this way user will understand better why we need permission.
- In the intent that is opening
MainActivity, we can put some args and check it in theonCreateto cancel the notification if the args are present in the intent.