Make a Timer App: Notifications (Ep 4) – Android Kotlin Tutorial (Code)

Learn how to create a beautiful material design timer app for Android.

In this course you will learn how to make a user interface. Later we’re going to code a timer which can run in the foreground. Then we are going to upgrade it to be able to run also in the background – and we will control it from notifications! Finally we will create a settings activity where a user will be able to set the length of the timer.

In the fourth part we’re creating notifications for when the timer is running, paused, or when it finishes. We also include the code which can control the timer right from the notifications!

 

This post contains all the code that’s been written in this YouTube video.

 

You can also check out this GitHub repository: https://github.com/ResoCoder/TimerAppAndroidTutorial

 

NotificationUtil.kt

package com.resocoder.timertutorial.util

import android.annotation.TargetApi
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.TaskStackBuilder
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.media.RingtoneManager
import android.net.Uri
import android.os.Build
import android.support.v4.app.NotificationCompat
import com.resocoder.timertutorial.AppConstants
import com.resocoder.timertutorial.R
import com.resocoder.timertutorial.TimerActivity
import com.resocoder.timertutorial.TimerNotificationActionReceiver
import java.text.SimpleDateFormat
import java.util.*


class NotificationUtil {
    companion object {
        private const val CHANNEL_ID_TIMER = "menu_timer"
        private const val CHANNEL_NAME_TIMER = "Timer App Timer"
        private const val TIMER_ID = 0

        fun showTimerExpired(context: Context){
            val startIntent = Intent(context, TimerNotificationActionReceiver::class.java)
            startIntent.action = AppConstants.ACTION_START
            val startPendingIntent = PendingIntent.getBroadcast(context,
                    0, startIntent, PendingIntent.FLAG_UPDATE_CURRENT)

            val nBuilder = getBasicNotificationBuilder(context, CHANNEL_ID_TIMER, true)
            nBuilder.setContentTitle("Timer Expired!")
                    .setContentText("Start again?")
                    .setContentIntent(getPendingIntentWithStack(context, TimerActivity::class.java))
                    .addAction(R.drawable.ic_play_arrow, "Start", startPendingIntent)

            val nManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            nManager.createNotificationChannel(CHANNEL_ID_TIMER, CHANNEL_NAME_TIMER, true)

            nManager.notify(TIMER_ID, nBuilder.build())
        }

        fun showTimerRunning(context: Context, wakeUpTime: Long){
            val stopIntent = Intent(context, TimerNotificationActionReceiver::class.java)
            stopIntent.action = AppConstants.ACTION_STOP
            val stopPendingIntent = PendingIntent.getBroadcast(context,
                    0, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT)

            val pauseIntent = Intent(context, TimerNotificationActionReceiver::class.java)
            pauseIntent.action = AppConstants.ACTION_PAUSE
            val pausePendingIntent = PendingIntent.getBroadcast(context,
                    0, pauseIntent, PendingIntent.FLAG_UPDATE_CURRENT)

            val df = SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT)

            val nBuilder = getBasicNotificationBuilder(context, CHANNEL_ID_TIMER, true)
            nBuilder.setContentTitle("Timer is Running.")
                    .setContentText("End: ${df.format(Date(wakeUpTime))}")
                    .setContentIntent(getPendingIntentWithStack(context, TimerActivity::class.java))
                    .setOngoing(true)
                    .addAction(R.drawable.ic_stop, "Stop", stopPendingIntent)
                    .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent)

            val nManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            nManager.createNotificationChannel(CHANNEL_ID_TIMER, CHANNEL_NAME_TIMER, true)

            nManager.notify(TIMER_ID, nBuilder.build())
        }

        fun showTimerPaused(context: Context){
            val resumeIntent = Intent(context, TimerNotificationActionReceiver::class.java)
            resumeIntent.action = AppConstants.ACTION_RESUME
            val resumePendingIntent = PendingIntent.getBroadcast(context,
                    0, resumeIntent, PendingIntent.FLAG_UPDATE_CURRENT)

            val nBuilder = getBasicNotificationBuilder(context, CHANNEL_ID_TIMER, true)
            nBuilder.setContentTitle("Timer is paused.")
                    .setContentText("Resume?")
                    .setContentIntent(getPendingIntentWithStack(context, TimerActivity::class.java))
                    .setOngoing(true)
                    .addAction(R.drawable.ic_play_arrow, "Resume", resumePendingIntent)

            val nManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            nManager.createNotificationChannel(CHANNEL_ID_TIMER, CHANNEL_NAME_TIMER, true)

            nManager.notify(TIMER_ID, nBuilder.build())
        }

        fun hideTimerNotification(context: Context){
            val nManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            nManager.cancel(TIMER_ID)
        }

        private fun getBasicNotificationBuilder(context: Context, channelId: String, playSound: Boolean)
        : NotificationCompat.Builder{
            val notificationSound: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
            val nBuilder = NotificationCompat.Builder(context, channelId)
                    .setSmallIcon(R.drawable.ic_timer)
                    .setAutoCancel(true)
                    .setDefaults(0)
            if (playSound) nBuilder.setSound(notificationSound)
            return nBuilder
        }

        private fun <T> getPendingIntentWithStack(context: Context, javaClass: Class<T>): PendingIntent{
            val resultIntent = Intent(context, javaClass)
            resultIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP

            val stackBuilder = TaskStackBuilder.create(context)
            stackBuilder.addParentStack(javaClass)
            stackBuilder.addNextIntent(resultIntent)

            return stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
        }

        @TargetApi(26)
        private fun NotificationManager.createNotificationChannel(channelID: String,
                                                                  channelName: String,
                                                                  playSound: Boolean){
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
                val channelImportance = if (playSound) NotificationManager.IMPORTANCE_DEFAULT
                else NotificationManager.IMPORTANCE_LOW
                val nChannel = NotificationChannel(channelID, channelName, channelImportance)
                nChannel.enableLights(true)
                nChannel.lightColor = Color.BLUE
                this.createNotificationChannel(nChannel)
            }
        }
    }
}

 

TimerNotificationActionReceiver.kt

package com.resocoder.timertutorial

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.resocoder.timertutorial.util.NotificationUtil
import com.resocoder.timertutorial.util.PrefUtil

class TimerNotificationActionReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        when (intent.action){
            AppConstants.ACTION_STOP -> {
                TimerActivity.removeAlarm(context)
                PrefUtil.setTimerState(TimerActivity.TimerState.Stopped, context)
                NotificationUtil.hideTimerNotification(context)
            }
            AppConstants.ACTION_PAUSE -> {
                var secondsRemaining = PrefUtil.getSecondsRemaining(context)
                val alarmSetTime = PrefUtil.getAlarmSetTime(context)
                val nowSeconds = TimerActivity.nowSeconds

                secondsRemaining -= nowSeconds - alarmSetTime
                PrefUtil.setSecondsRemaining(secondsRemaining, context)

                TimerActivity.removeAlarm(context)
                PrefUtil.setTimerState(TimerActivity.TimerState.Paused, context)
                NotificationUtil.showTimerPaused(context)
            }
            AppConstants.ACTION_RESUME -> {
                val secondsRemaining = PrefUtil.getSecondsRemaining(context)
                val wakeUpTime = TimerActivity.setAlarm(context, TimerActivity.nowSeconds, secondsRemaining)
                PrefUtil.setTimerState(TimerActivity.TimerState.Running, context)
                NotificationUtil.showTimerRunning(context, wakeUpTime)
            }
            AppConstants.ACTION_START -> {
                val minutesRemaining = PrefUtil.getTimerLength(context)
                val secondsRemaining = minutesRemaining * 60L
                val wakeUpTime = TimerActivity.setAlarm(context, TimerActivity.nowSeconds, secondsRemaining)
                PrefUtil.setTimerState(TimerActivity.TimerState.Running, context)
                PrefUtil.setSecondsRemaining(secondsRemaining, context)
                NotificationUtil.showTimerRunning(context, wakeUpTime)
            }
        }
    }
}

 

AppConstants.kt

package com.resocoder.timertutorial


class AppConstants {
    companion object {
        const val ACTION_STOP = "stop"
        const val ACTION_PAUSE = "pause"
        const val ACTION_RESUME = "resume"
        const val ACTION_START = "start"
    }
}

 

AndroidManifest.xml

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".TimerActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver
            android:name=".TimerExpiredReceiver"
            android:enabled="true"
            android:exported="false" />
        <receiver
            android:name=".TimerNotificationActionReceiver"
            android:enabled="true"
            android:exported="false">
            <intent-filter>
                <action android:name="stop"/>
                <action android:name="pause" />
                <action android:name="resume" />
                <action android:name="start" />
            </intent-filter>
        </receiver>
    </application>

</manifest>

 

TimerExpiredReceiver.kt

package com.resocoder.timertutorial

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.resocoder.timertutorial.util.NotificationUtil
import com.resocoder.timertutorial.util.PrefUtil

class TimerExpiredReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        NotificationUtil.showTimerExpired(context)

        PrefUtil.setTimerState(TimerActivity.TimerState.Stopped, context)
        PrefUtil.setAlarmSetTime(0, context)
    }
}

 

TimerActivity.kt

package com.resocoder.timertutorial

import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.CountDownTimer
import android.support.v7.app.AppCompatActivity
import android.view.Menu
import android.view.MenuItem
import com.resocoder.timertutorial.util.NotificationUtil
import com.resocoder.timertutorial.util.PrefUtil
import kotlinx.android.synthetic.main.activity_timer.*
import kotlinx.android.synthetic.main.content_timer.*
import java.util.*

class TimerActivity : AppCompatActivity() {

    companion object {
        fun setAlarm(context: Context, nowSeconds: Long, secondsRemaining: Long): Long{
            val wakeUpTime = (nowSeconds + secondsRemaining) * 1000
            val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
            val intent = Intent(context, TimerExpiredReceiver::class.java)
            val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0)
            alarmManager.setExact(AlarmManager.RTC_WAKEUP, wakeUpTime, pendingIntent)
            PrefUtil.setAlarmSetTime(nowSeconds, context)
            return wakeUpTime
        }

        fun removeAlarm(context: Context){
            val intent = Intent(context, TimerExpiredReceiver::class.java)
            val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0)
            val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
            alarmManager.cancel(pendingIntent)
            PrefUtil.setAlarmSetTime(0, context)
        }

        val nowSeconds: Long
            get() = Calendar.getInstance().timeInMillis / 1000
    }

    enum class TimerState{
        Stopped, Paused, Running
    }

    private lateinit var timer: CountDownTimer
    private var timerLengthSeconds: Long = 0
    private var timerState = TimerState.Stopped

    private var secondsRemaining: Long = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_timer)
        setSupportActionBar(toolbar)
        supportActionBar?.setIcon(R.drawable.ic_timer)
        supportActionBar?.title = "      Timer"

        fab_start.setOnClickListener{v ->
            startTimer()
            timerState =  TimerState.Running
            updateButtons()
        }

        fab_pause.setOnClickListener { v ->
            timer.cancel()
            timerState = TimerState.Paused
            updateButtons()
        }

        fab_stop.setOnClickListener { v ->
            timer.cancel()
            onTimerFinished()
        }
    }

    override fun onResume() {
        super.onResume()

        initTimer()

        removeAlarm(this)
        NotificationUtil.hideTimerNotification(this)
    }

    override fun onPause() {
        super.onPause()

        if (timerState == TimerState.Running){
            timer.cancel()
            val wakeUpTime = setAlarm(this, nowSeconds, secondsRemaining)
            NotificationUtil.showTimerRunning(this, wakeUpTime)
        }
        else if (timerState == TimerState.Paused){
            NotificationUtil.showTimerPaused(this)
        }

        PrefUtil.setPreviousTimerLengthSeconds(timerLengthSeconds, this)
        PrefUtil.setSecondsRemaining(secondsRemaining, this)
        PrefUtil.setTimerState(timerState, this)
    }

    private fun initTimer(){
        timerState = PrefUtil.getTimerState(this)

        //we don't want to change the length of the timer which is already running
        //if the length was changed in settings while it was backgrounded
        if (timerState == TimerState.Stopped)
            setNewTimerLength()
        else
            setPreviousTimerLength()

        secondsRemaining = if (timerState == TimerState.Running || timerState == TimerState.Paused)
            PrefUtil.getSecondsRemaining(this)
        else
            timerLengthSeconds

        val alarmSetTime = PrefUtil.getAlarmSetTime(this)
        if (alarmSetTime > 0)
            secondsRemaining -= nowSeconds - alarmSetTime

        if (secondsRemaining <= 0)
            onTimerFinished()
        else if (timerState == TimerState.Running)
            startTimer()

        updateButtons()
        updateCountdownUI()
    }

    private fun onTimerFinished(){
        timerState = TimerState.Stopped

        //set the length of the timer to be the one set in SettingsActivity
        //if the length was changed when the timer was running
        setNewTimerLength()

        progress_countdown.progress = 0

        PrefUtil.setSecondsRemaining(timerLengthSeconds, this)
        secondsRemaining = timerLengthSeconds

        updateButtons()
        updateCountdownUI()
    }

    private fun startTimer(){
        timerState = TimerState.Running

        timer = object : CountDownTimer(secondsRemaining * 1000, 1000) {
            override fun onFinish() = onTimerFinished()

            override fun onTick(millisUntilFinished: Long) {
                secondsRemaining = millisUntilFinished / 1000
                updateCountdownUI()
            }
        }.start()
    }

    private fun setNewTimerLength(){
        val lengthInMinutes = PrefUtil.getTimerLength(this)
        timerLengthSeconds = (lengthInMinutes * 60L)
        progress_countdown.max = timerLengthSeconds.toInt()
    }

    private fun setPreviousTimerLength(){
        timerLengthSeconds = PrefUtil.getPreviousTimerLengthSeconds(this)
        progress_countdown.max = timerLengthSeconds.toInt()
    }

    private fun updateCountdownUI(){
        val minutesUntilFinished = secondsRemaining / 60
        val secondsInMinuteUntilFinished = secondsRemaining - minutesUntilFinished * 60
        val secondsStr = secondsInMinuteUntilFinished.toString()
        textView_countdown.text = "$minutesUntilFinished:${if (secondsStr.length == 2) secondsStr else "0" + secondsStr}"
        progress_countdown.progress = (timerLengthSeconds - secondsRemaining).toInt()
    }

    private fun updateButtons(){
        when (timerState) {
            TimerState.Running ->{
                fab_start.isEnabled = false
                fab_pause.isEnabled = true
                fab_stop.isEnabled = true
            }
            TimerState.Stopped -> {
                fab_start.isEnabled = true
                fab_pause.isEnabled = false
                fab_stop.isEnabled = false
            }
            TimerState.Paused -> {
                fab_start.isEnabled = true
                fab_pause.isEnabled = false
                fab_stop.isEnabled = true
            }
        }
    }


    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        // Inflate the menu; this adds items to the action bar if it is present.
        menuInflater.inflate(R.menu.menu_timer, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        return when (item.itemId) {
            R.id.action_settings -> true
            else -> super.onOptionsItemSelected(item)
        }
    }
}

 

About the author 

Matt Rešetár

Matt is an app developer with a knack for teaching others. Working as a freelancer and most importantly developer educator, he is set on helping other people succeed in their Flutter app development career.

You may also like

  • i use this in my app and it works perfect, but when i finish the 4th video it starts crashing when i put it in background.
    it throws:

    FATAL EXCEPTION: main
    Process: com.martin.brewer, PID: 28546
    java.lang.NoClassDefFoundError: Failed resolution of: Landroid/app/NotificationChannel;
    at com.martin.brewer.util.NotificationUtil$Companion.createNotificationChannel(NotificationUtil.kt:123)
    at com.martin.brewer.util.NotificationUtil$Companion.showTimerRunning(NotificationUtil.kt:65)
    at com.martin.brewer.View.MainActivity.onPause(MainActivity.kt:92)
    at android.app.Activity.performPause(Activity.java:6348)
    at android.app.Instrumentation.callActivityOnPause(Instrumentation.java:1311)
    at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3367)
    at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3340)
    at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:3315)
    at android.app.ActivityThread.-wrap13(ActivityThread.java)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1355)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5417)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
    Caused by: java.lang.ClassNotFoundException: Didn’t find class “android.app.NotificationChannel” on path: DexPathList[[zip file “/data/app/com.martin.brewer-2/base.apk”],nativeLibraryDirectories=[/data/app/com.martin.brewer-2/lib/x86, /vendor/lib, /system/lib]]
    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
    at com.martin.brewer.util.NotificationUtil$Companion.createNotificationChannel(NotificationUtil.kt:123) 
    at com.martin.brewer.util.NotificationUtil$Companion.showTimerRunning(NotificationUtil.kt:65) 
    at com.martin.brewer.View.MainActivity.onPause(MainActivity.kt:92) 
    at android.app.Activity.performPause(Activity.java:6348) 
    at android.app.Instrumentation.callActivityOnPause(Instrumentation.java:1311) 
    at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3367) 
    at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3340) 
    at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:3315) 
    at android.app.ActivityThread.-wrap13(ActivityThread.java) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1355) 
    at android.os.Handler.dispatchMessage(Handler.java:102) 
    at android.os.Looper.loop(Looper.java:148) 
    at android.app.ActivityThread.main(ActivityThread.java:5417) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
    Suppressed: java.lang.ClassNotFoundException: android.app.NotificationChannel
    at java.lang.Class.classForName(Native Method)
    at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
    at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
    … 17 more
    Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available

    Do you know how can i fix it?

    Thanks
    Martin

  • y comment this 3 lines [ nManager.createNotificationChannel(CHANNEL_ID_TIMER, CHANNEL_NAME_TIMER, true) ] and its works, do yo know why is crashing with them and how can i fix it?

    here is the function:

    @TargetApi(26)
    private fun NotificationManager.createNotificationChannel(channelID: String, channelName: String, playSound: Boolean){
    val channelImportance = if(playSound) {
    NotificationManager.IMPORTANCE_DEFAULT
    } else {
    NotificationManager.IMPORTANCE_LOW
    }
    val nChannel = NotificationChannel(channelID, channelName, channelImportance)
    nChannel.enableLights(true)
    nChannel.lightColor = Color.BLUE
    this.createNotificationChannel(nChannel)
    }

    Thanks!

    • Hey! It seems that you’ve forgotten to check the version of the device / emulator.

      Wrap the content of the provided function in this if statement: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) and it should work.

      Are you using a pre-Oreo device to test your app?

  • It works!! thanks! I was using marshmallow, I do what you say and it works on it and in oreo too. Thank you very much for the tutorial and for helping me!

  • Hi,

    Everything worked fine unt,l I implemented Video4 notifications. Do you have any idea what might be wrong?

    The code that is causing the problem is
    nBuilder.setContentIntent(getPendingIntentWithStack(context, TimerActivity::class.java))

    Thanks
    E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.cudinal.tmertutorial, PID: 24273
    java.lang.RuntimeException: Unable to pause activity {com.cudinal.tmertutorial/com.cudinal.tmertutorial.TimerActivity}: java.lang.IllegalArgumentException: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{com.cudinal.tmertutorial/com.cudinal.tmertutorial.util.NotificationUtil$Companion}
    at android.app.ActivityThread.performPauseActivityIfNeeded(ActivityThread.java:3912)
    at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3878)
    at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3852)
    at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:3826)
    at android.app.ActivityThread.-wrap15(Unknown Source:0)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1601)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6494)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
    Caused by: java.lang.IllegalArgumentException: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{com.cudinal.tmertutorial/com.cudinal.tmertutorial.util.NotificationUtil$Companion}
    at android.app.TaskStackBuilder.addParentStack(TaskStackBuilder.java:181)
    at android.app.TaskStackBuilder.addParentStack(TaskStackBuilder.java:152)
    at com.cudinal.tmertutorial.util.NotificationUtil$Companion.getPendingIntentWithStack(NotificationUtil.kt:105)
    at com.cudinal.tmertutorial.util.NotificationUtil$Companion.showTimerRunning(NotificationUtil.kt:58)
    at com.cudinal.tmertutorial.TimerActivity.onPause(TimerActivity.kt:90)
    at android.app.Activity.performPause(Activity.java:7153)
    at android.app.Instrumentation.callActivityOnPause(Instrumentation.java:1408)
    at android.app.ActivityThread.performPauseActivityIfNeeded(ActivityThread.java:3901)
    at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3878) 
    at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3852) 
    at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:3826) 
    at android.app.ActivityThread.-wrap15(Unknown Source:0) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1601) 
    at android.os.Handler.dispatchMessage(Handler.java:106) 
    at android.os.Looper.loop(Looper.java:164) 
    at android.app.ActivityThread.main(ActivityThread.java:6494) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 
    Caused by: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{com.cudinal.tmertutorial/com.cudinal.tmertutorial.util.NotificationUtil$Companion}
    at android.app.ApplicationPackageManager.getActivityInfo(ApplicationPackageManager.java:418)
    at android.app.TaskStackBuilder.addParentStack(TaskStackBuilder.java:168)
    at android.app.TaskStackBuilder.addParentStack(TaskStackBuilder.java:152) 
    at com.cudinal.tmertutorial.util.NotificationUtil$Companion.getPendingIntentWithStack(NotificationUtil.kt:105) 
    at com.cudinal.tmertutorial.util.NotificationUtil$Companion.showTimerRunning(NotificationUtil.kt:58) 
    at com.cudinal.tmertutorial.TimerActivity.onPause(TimerActivity.kt:90) 
    at android.app.Activity.performPause(Activity.java:7153) 
    at android.app.Instrumentation.callActivityOnPause(Instrumentation.java:1408) 
    at android.app.ActivityThread.performPauseActivityIfNeeded(ActivityThread.java:3901) 
    at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3878) 
    at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3852) 
    at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:3826) 
    at android.app.ActivityThread.-wrap15(Unknown Source:0) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1601) 
    at android.os.Handler.dispatchMessage(Handler.java:106) 
    at android.os.Looper.loop(Looper.java:164) 
    at android.app.ActivityThread.main(ActivityThread.java:6494) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 

  • Thank you for your tutorial…its great.
    Question: how can i play a sound(raw.mp3) instead of the Ringtonemanager.?
    my sound:
    val soundUri: Uri = Uri.parse(
    “android.resource://” + context.packageName.toString()
    + “/” + q7.com.eieruhrbackforenot.R.raw.see)
    i have tried it, but the default one is still ringing.
    Thank you

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
    >