Search code examples
androidkotlinnotificationsalarmmanager

Scheduled Alarm not showing notification Android


I have developed a reminder app using kotlin. You can set title, memo and date and time and it's going to show notification to the user on exact set of date and time. The problem is that on that time nothing happens though everything seems totally fine. I'll provide you with my codes and I hope you'll help me to fix it. Thanks in advance.

Here's my Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
    <uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.XReminder"
        tools:targetApi="31">
        <activity
            android:name=".activity.MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name=".notification.NotificationReceiver" />
    </application>

</manifest>

Notification Class:

@RequiresApi(Build.VERSION_CODES.O)
class Notification(
    name: String,
    description: String,
    private var context: Context,
    var id: String,
    private var reminder: Reminder
) {

    init {
        createChannel(name, description)
        startNotification()
    }

    @SuppressLint("ScheduleExactAlarm")
    private fun startNotification() {
        val calendar = Calendar.getInstance()
        calendar.set(Calendar.HOUR_OF_DAY, (reminder.time).split(":")[0].toInt())
        calendar.set(Calendar.MINUTE, (reminder.time).split(":")[1].toInt())
        calendar.set(Calendar.SECOND, 0) // Change its default from 13 to 0 to be precise
        calendar.set(Calendar.MILLISECOND, 0) // Change its default from 14 to 0 to be precise
        calendar.set(Calendar.DAY_OF_YEAR, (reminder.date).split(".")[0].toInt())
        calendar.set(
            Calendar.MONTH,
            (reminder.date).split(".")[1].toInt() - 1
        ) // Month start from 0 but in reality it starts from 1
        calendar.set(Calendar.YEAR, (reminder.date).split(".")[2].toInt())

        val notificationIntent = Intent(context, NotificationReceiver::class.java)
        notificationIntent.putExtra("id", id)
        val alarmManager = context.getSystemService(ALARM_SERVICE) as AlarmManager
        alarmManager.setExact(
            AlarmManager.RTC_WAKEUP, calendar.timeInMillis, PendingIntent.getBroadcast(
                context, id.toInt(), notificationIntent, PendingIntent.FLAG_IMMUTABLE
            )
        )
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private fun createChannel(name: String, description: String) {
        val importance = NotificationManager.IMPORTANCE_HIGH
        val channel = NotificationChannel(id, name, importance)
        channel.description = description
        val notificationManager = context.getSystemService(NotificationManager::class.java)
        notificationManager.createNotificationChannel(channel)
    }
}

NotificationReceiver Class:

class NotificationReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        val currentId = intent!!.extras!!.getString("id")
        val notificationManager =
            context!!.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        // Use runBlocking because we have no access to lifecycleScope
        runBlocking {
            val currentReminder = context.dataStore.data.first().remindersList.find {
                it.hashCode() == currentId!!.toInt()
            }

            createNotification(
                context,
                currentId.toString(),
                currentReminder!!.title,
                currentReminder.memo,
                notificationManager
            )
        }
    }

    private fun createNotification(
        context: Context?,
        id: String,
        title: String,
        memo: String,
        notificationManager: NotificationManager
    ) {
        val notification = NotificationCompat.Builder(context!!, id)
            .setSmallIcon(R.drawable.baseline_notification_important)
            .setContentTitle(title)
            .setContentText(memo)
            .setPriority(NotificationCompat.PRIORITY_HIGH).build()

        notificationManager.notify(id.toInt(), notification)
    }
}

I tried to debug the app. I put breakpoints almost everywhere and I realized all data have been passed correctly.


Solution

  • SOLUTION

    As it runs on android 13, notification is blocked as default and we have to implement runtime permission in MainActivity.