DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Adding Push Notifications to AI-Generated Android Apps

Adding Push Notifications to AI-Generated Android Apps

Push notifications are one of the most powerful engagement features in modern Android applications. They allow your app to communicate directly with users even when the app isn't open, driving re-engagement and user retention. If you're building AI-generated Android apps, integrating push notifications can significantly increase user interaction and app value.

In this comprehensive guide, we'll explore how to add push notifications to your Android applications using Kotlin and the AndroidX NotificationCompat library. We'll cover everything from setting up notification channels to handling permission requests on modern Android versions.

Understanding Android Notifications

Before diving into implementation, let's understand the notification system in Android. Notifications consist of several key components:

  1. NotificationCompat: A backward-compatible library that provides notification functionality across Android versions
  2. Notification Channels: Required on Android 8.0+ for grouping related notifications
  3. PendingIntent: Specifies the action to perform when the user taps the notification
  4. Notification Builder: The API for constructing notification content

Setting Up NotificationCompat

The first step is to ensure you have the AndroidX libraries in your build.gradle:

dependencies {
    implementation 'androidx.core:core:1.10.1'
}
Enter fullscreen mode Exit fullscreen mode

NotificationCompat provides a builder pattern for creating notifications that work across all Android versions. Here's a basic example:

import androidx.core.app.NotificationCompat

val notification = NotificationCompat.Builder(context, channelId)
    .setContentTitle("Hello")
    .setContentText("Notification message")
    .setSmallIcon(R.drawable.ic_notification)
    .build()
Enter fullscreen mode Exit fullscreen mode

Creating Notification Channels (Android 8+)

Starting with Android 8.0 (API level 26), all notifications must be assigned to a channel. Channels allow users to control notification behavior and appearance.

import androidx.core.app.NotificationManagerCompat

fun createNotificationChannel(context: Context) {
    val channelId = "default_channel"
    val channelName = "Default Notifications"
    val importance = NotificationManager.IMPORTANCE_DEFAULT

    val channel = NotificationChannel(
        channelId,
        channelName,
        importance
    ).apply {
        description = "This channel is used for general app notifications"
        enableVibration(true)
        enableLights(true)
        setShowBadge(true)
    }

    val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE)
        as NotificationManager
    notificationManager.createNotificationChannel(channel)
}
Enter fullscreen mode Exit fullscreen mode

Best practices for channels:

  • Create channels during app initialization
  • Use meaningful channel IDs and names
  • Set appropriate importance levels (IMPORTANCE_NONE, MIN, LOW, DEFAULT, HIGH, MAX)
  • Enable or disable vibration, lights, and badges based on notification type

Requesting Notification Permissions (Android 13+)

Starting with Android 13 (API level 33), apps must request the POST_NOTIFICATIONS permission. This is critical for modern Android devices.

First, add the permission to AndroidManifest.xml:

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
Enter fullscreen mode Exit fullscreen mode

Then, implement runtime permission requests in your Activity:

import androidx.core.content.ContextCompat
import androidx.activity.result.contract.ActivityResultContracts

class MainActivity : AppCompatActivity() {
    private val requestPermissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted ->
        if (isGranted) {
            // Permission granted, send notification
            sendNotification()
        } else {
            // Permission denied
            Toast.makeText(this, "Notification permission denied", Toast.LENGTH_SHORT).show()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Check and request permission
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            when {
                ContextCompat.checkSelfPermission(
                    this,
                    Manifest.permission.POST_NOTIFICATIONS
                ) == PackageManager.PERMISSION_GRANTED -> {
                    sendNotification()
                }
                else -> {
                    requestPermissionLauncher.launch(
                        Manifest.permission.POST_NOTIFICATIONS
                    )
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Using PendingIntent for Notification Actions

PendingIntent specifies what happens when users interact with your notification. Always use the immutable flag for security:

import android.app.PendingIntent
import android.content.Intent

fun createPendingIntent(context: Context): PendingIntent {
    val intent = Intent(context, MainActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
    }

    return PendingIntent.getActivity(
        context,
        0,
        intent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )
}
Enter fullscreen mode Exit fullscreen mode

Important security note: Always use PendingIntent.FLAG_IMMUTABLE to prevent apps from modifying the intent.

Building Complete Notifications

Here's a complete example combining all components:

import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat

fun sendCompleteNotification(
    context: Context,
    title: String,
    text: String,
    notificationId: Int = 1
) {
    // Create notification channel (safe to call multiple times)
    createNotificationChannel(context)

    // Build notification with PendingIntent
    val pendingIntent = createPendingIntent(context)

    val notification = NotificationCompat.Builder(context, "default_channel")
        .setSmallIcon(R.drawable.ic_notification)
        .setContentTitle(title)
        .setContentText(text)
        .setContentIntent(pendingIntent)
        .setAutoCancel(true)
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setStyle(
            NotificationCompat.BigTextStyle()
                .bigText(text)
        )
        .build()

    // Post notification
    with(NotificationManagerCompat.from(context)) {
        notify(notificationId, notification)
    }
}
Enter fullscreen mode Exit fullscreen mode

Advanced Notification Features

Large Icon and Big Picture Style

val largeIcon = BitmapFactory.decodeResource(context.resources, R.drawable.large_icon)

val notification = NotificationCompat.Builder(context, "default_channel")
    .setSmallIcon(R.drawable.ic_notification)
    .setLargeIcon(largeIcon)
    .setContentTitle("Breaking News")
    .setStyle(
        NotificationCompat.BigPictureStyle()
            .bigPicture(BitmapFactory.decodeResource(
                context.resources,
                R.drawable.news_image
            ))
            .bigLargeIcon(largeIcon)
    )
    .build()
Enter fullscreen mode Exit fullscreen mode

Action Buttons

val snoozeIntent = Intent(context, NotificationReceiver::class.java).apply {
    action = "com.example.SNOOZE"
}
val snoozePendingIntent = PendingIntent.getBroadcast(
    context, 0, snoozeIntent,
    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)

val notification = NotificationCompat.Builder(context, "default_channel")
    .setSmallIcon(R.drawable.ic_notification)
    .setContentTitle("Reminder")
    .setContentText("Time to check your app!")
    .addAction(R.drawable.ic_snooze, "Snooze", snoozePendingIntent)
    .build()
Enter fullscreen mode Exit fullscreen mode

Complete Implementation Pattern

Here's a production-ready pattern for notification integration:

class NotificationManager(private val context: Context) {

    companion object {
        const val CHANNEL_ID_DEFAULT = "default_channel"
        const val CHANNEL_ID_URGENT = "urgent_channel"
    }

    init {
        createChannels()
    }

    private fun createChannels() {
        listOf(
            NotificationChannel(
                CHANNEL_ID_DEFAULT,
                "Default",
                NotificationManager.IMPORTANCE_DEFAULT
            ),
            NotificationChannel(
                CHANNEL_ID_URGENT,
                "Urgent",
                NotificationManager.IMPORTANCE_HIGH
            )
        ).forEach {
            context.getSystemService(NotificationManager::class.java)
                .createNotificationChannel(it)
        }
    }

    fun sendNotification(
        title: String,
        text: String,
        channelId: String = CHANNEL_ID_DEFAULT,
        notificationId: Int = System.currentTimeMillis().toInt()
    ) {
        val pendingIntent = createPendingIntent()

        val notification = NotificationCompat.Builder(context, channelId)
            .setSmallIcon(R.drawable.ic_notification)
            .setContentTitle(title)
            .setContentText(text)
            .setContentIntent(pendingIntent)
            .setAutoCancel(true)
            .build()

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

    private fun createPendingIntent(): PendingIntent {
        val intent = Intent(context, MainActivity::class.java)
        return PendingIntent.getActivity(
            context, 0, intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
    }
}
Enter fullscreen mode Exit fullscreen mode

Testing Notifications

Test your notifications on different Android versions:

// In MainActivity
val notificationManager = NotificationManager(this)
button.setOnClickListener {
    notificationManager.sendNotification(
        "Test Notification",
        "This is a test notification with full features"
    )
}
Enter fullscreen mode Exit fullscreen mode

Common Pitfalls to Avoid

  1. Forgetting notification channels on Android 8+: Always create channels before posting
  2. Not requesting POST_NOTIFICATIONS permission: Your app will crash on Android 13+
  3. Using mutable PendingIntent: Always use FLAG_IMMUTABLE for security
  4. Ignoring user preference: Respect channel settings and allow users to disable notifications
  5. Spamming users: Implement rate limiting and respect notification frequency guidelines

Conclusion

Push notifications are essential for keeping users engaged with your app. By following this guide, you can integrate NotificationCompat, manage notification channels, handle modern permission requirements, and build rich notifications with images and actions.

The key principles are:

  • Always use NotificationCompat for backward compatibility
  • Create and use notification channels
  • Request POST_NOTIFICATIONS permission on Android 13+
  • Use immutable PendingIntent for security
  • Respect user preferences and notification etiquette

All 8 templates are ready for notification integration. https://myougatheax.gumroad.com

Top comments (0)