DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Android Notification Channels — Local Notifications with Actions & Progress

Android Notification Channels — Local Notifications with Actions & Progress

Starting with Android 8 (API 26), notification channels are required. This guide covers channel creation, actions, and progress notifications.

1. Create Notification Channels

import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import androidx.core.app.NotificationCompat

object NotificationChannels {
    const val GENERAL_CHANNEL_ID = "general_notifications"
    const val IMPORTANT_CHANNEL_ID = "important_notifications"
    const val SILENT_CHANNEL_ID = "silent_notifications"

    fun createChannels(context: Context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

            // General channel (default sound/vibration)
            val generalChannel = NotificationChannel(
                GENERAL_CHANNEL_ID,
                "General Notifications",
                NotificationManager.IMPORTANCE_DEFAULT
            ).apply {
                description = "General app notifications"
            }
            notificationManager.createNotificationChannel(generalChannel)

            // Important channel (high priority, heads-up)
            val importantChannel = NotificationChannel(
                IMPORTANT_CHANNEL_ID,
                "Important Alerts",
                NotificationManager.IMPORTANCE_HIGH
            ).apply {
                description = "Time-sensitive notifications"
            }
            notificationManager.createNotificationChannel(importantChannel)

            // Silent channel (no sound)
            val silentChannel = NotificationChannel(
                SILENT_CHANNEL_ID,
                "Silent Notifications",
                NotificationManager.IMPORTANCE_LOW
            ).apply {
                description = "Background updates"
            }
            notificationManager.createNotificationChannel(silentChannel)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Basic Notification with PendingIntent

import androidx.core.app.NotificationCompat
import android.app.PendingIntent
import android.content.Intent

fun sendBasicNotification(context: Context, notificationId: Int) {
    val intent = Intent(context, MainActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
    }
    val pendingIntent: PendingIntent = PendingIntent.getActivity(
        context,
        0,
        intent,
        PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
    )

    val notification = NotificationCompat.Builder(context, NotificationChannels.GENERAL_CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_notification)
        .setContentTitle("Task Complete")
        .setContentText("Your download has finished")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setContentIntent(pendingIntent)
        .setAutoCancel(true)
        .build()

    val notificationManager = NotificationManagerCompat.from(context)
    notificationManager.notify(notificationId, notification)
}
Enter fullscreen mode Exit fullscreen mode

3. Notification with Action Buttons

fun sendNotificationWithActions(context: Context, notificationId: Int) {
    // Reply action
    val replyIntent = Intent(context, NotificationActionReceiver::class.java).apply {
        action = "REPLY_ACTION"
    }
    val replyPendingIntent = PendingIntent.getBroadcast(
        context,
        1,
        replyIntent,
        PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
    )

    // Dismiss action
    val dismissIntent = Intent(context, NotificationActionReceiver::class.java).apply {
        action = "DISMISS_ACTION"
    }
    val dismissPendingIntent = PendingIntent.getBroadcast(
        context,
        2,
        dismissIntent,
        PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
    )

    val notification = NotificationCompat.Builder(context, NotificationChannels.IMPORTANT_CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_notification)
        .setContentTitle("New Message")
        .setContentText("You have a message from John")
        .setPriority(NotificationCompat.PRIORITY_HIGH)
        .addAction(R.drawable.ic_reply, "Reply", replyPendingIntent)
        .addAction(R.drawable.ic_dismiss, "Dismiss", dismissPendingIntent)
        .setAutoCancel(true)
        .build()

    NotificationManagerCompat.from(context).notify(notificationId, notification)
}

// Receiver to handle actions
class NotificationActionReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        when (intent?.action) {
            "REPLY_ACTION" -> {
                println("User replied to notification")
            }
            "DISMISS_ACTION" -> {
                println("User dismissed notification")
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Progress Notification

fun sendProgressNotification(context: Context, notificationId: Int, progress: Int, max: Int = 100) {
    val notification = NotificationCompat.Builder(context, NotificationChannels.GENERAL_CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_notification)
        .setContentTitle("Downloading File")
        .setContentText("$progress%")
        .setProgress(max, progress, false)
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setOngoing(true)
        .build()

    NotificationManagerCompat.from(context).notify(notificationId, notification)
}

// Example: Update progress in a coroutine
fun downloadWithProgress(context: Context) {
    viewModelScope.launch {
        repeat(100) { progress ->
            sendProgressNotification(context, 1, progress)
            delay(100)
        }
        // Complete notification
        val completeNotification = NotificationCompat.Builder(context, NotificationChannels.GENERAL_CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_notification)
            .setContentTitle("Download Complete")
            .setProgress(0, 0, false)
            .setOngoing(false)
            .build()
        NotificationManagerCompat.from(context).notify(1, completeNotification)
    }
}
Enter fullscreen mode Exit fullscreen mode

5. Compose Permission Handling

import android.Manifest
import androidx.compose.runtime.LaunchedEffect
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.rememberPermissionState

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun NotificationPermissionScreen() {
    val postNotificationPermission = rememberPermissionState(Manifest.permission.POST_NOTIFICATIONS)

    LaunchedEffect(Unit) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            postNotificationPermission.launchPermissionRequest()
        }
    }

    Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
        when {
            postNotificationPermission.hasPermission -> {
                Text("Notifications enabled!")
                Button(onClick = {
                    // Send notification
                    sendBasicNotification(context, 1)
                }) {
                    Text("Send Notification")
                }
            }
            postNotificationPermission.shouldShowRationale -> {
                Text("Notifications are needed to keep you updated")
                Button(onClick = { postNotificationPermission.launchPermissionRequest() }) {
                    Text("Grant Permission")
                }
            }
            else -> {
                Text("Permission required for notifications")
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Key Points:

  • Channels must be created before sending notifications (onCreate recommended)
  • IMPORTANCE_HIGH shows heads-up notifications
  • IMPORTANCE_LOW/NONE bypasses sound
  • FLAG_IMMUTABLE is mandatory for PendingIntent on Android 12+
  • POST_NOTIFICATIONS permission required for Android 13+

8 Android app templates: Gumroad

Top comments (0)