0

What is the proper way to create and use a BroadcastReceiver for an alarm manager inside an Activity?

I did have a look at the following answers but it's not clear to me whether I need to register the broadcast receiver and how to do so with the AlarmManager if it is. Also, it is not clear to me whether I should create the BroadcastReceiver inside the OnCreate() function or as class variable.

Broadcast Receiver in kotlin

Kotlin AlarmManager and BroadcastReceiver not working


Background:

I am trying to make an app that:

  1. Plays a song at a specific time

  2. Updates the time when the song should next be played

I was able to get 1) to work by creating a separate file for the BroadcastReceiver file as is commonly done, but then I am unable to access the AlarmManager because it is only available in the AppCompatActivity class. I also tried to use an intent to move back to the main activity and set the new alarm there, but that also did work because BroadcastReceiver does not have a context.

I am now trying to have the BroadcastReceiver inside the MainActivity but it does not get triggered:

private lateinit var picker: MaterialTimePicker
private lateinit var alarmManager: AlarmManager
private lateinit var calendar: Calendar

val broadCastReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        var mp = MediaPlayer.create(context, R.raw.song_title)

        mp.setVolume(1.0f, 1.0f)
        mp.start()
    }
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    var calendar = Calendar.getInstance()

    val intent = Intent(this, MainActivity::class.java)
    val pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0)
    alarmManager = getSystemService(ALARM_SERVICE) as AlarmManager

    val newDate = Calendar.getInstance()
    newDate.add(Calendar.MINUTE, 1)

    calendar[java.util.Calendar.HOUR_OF_DAY] = newDate.get(Calendar.HOUR_OF_DAY)
    calendar[java.util.Calendar.MINUTE] = newDate.get(Calendar.MINUTE)
    calendar[java.util.Calendar.SECOND] = 0
    calendar[java.util.Calendar.MILLISECOND] = 0

    alarmManager.setAndAllowWhileIdle(
        AlarmManager.RTC_WAKEUP,
        calendar.timeInMillis,
        pendingIntent
    )


}

}

nayriz
  • 353
  • 1
  • 17
  • Why not pass your `AlarmManager` to your `BroadcastReceiver` in the seperate file via the constructor? – gtxtreme Oct 14 '21 at 05:07
  • @gtxtreme I'm not sure how that would work. When you do the pending intent from the MainActivity to the other file, you are instantiating the class, so I think you can't pass the AlarmManager as a constructor. – nayriz Oct 14 '21 at 07:41
  • Oh yes, my bad but this code should work I guess let me try to figure out what's wrong – gtxtreme Oct 14 '21 at 10:53
  • @nayriz Could you please clarify what you want to achieve specifically? – akhil nair Oct 14 '21 at 12:53
  • @akhilnair I want to play a song at a given time, and each time the song is played, set the time when the song will be played next using a special function. For example: 1. Trigger the media player to play a song a 13:00 . At the same time set the media player to play the same song at 13:14 2. Trigger the media player to play a song a 13:14 . At the same time set the media player to play the same song at 13:26 etc... (the function used to choose the next time is irrelevant) – nayriz Oct 14 '21 at 14:38

2 Answers2

0

AlarmManager is not just only available in the AppCompatActivity class but from anywhere where you have access to Context as shown below:

class MyReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
    val alarmManager = context?.getSystemService(ALARM_SERVICE) as AlarmManager
  }
}
akhil nair
  • 1,371
  • 1
  • 11
  • 19
  • I have added an answer, let me know if this is what you are needed. – akhil nair Oct 14 '21 at 14:36
  • Your solution works for the ultimate purpose, however since I also need to update the UI from the main activity, I also have to use an intent to go back to the main activity. This is poor coding, so I'd still prefer to have the broadcast receiver inside the MainActivity. I can create a new question "How to use AlarmManager in a BroadCasr receiver" and I will accept your answer there. – nayriz Oct 17 '21 at 01:20
0

After tweaking this answer I got it to work:

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val receiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                var TAG = "check"
                Log.i(TAG,"==============")
                Log.i(TAG,"inside receiver")
                Log.i(TAG,"==============")
            }
        }

        this.registerReceiver(receiver, IntentFilter("TEST"))

        val intent = Intent()
        intent.action = "TEST"

        val alarmManager = this.getSystemService(ALARM_SERVICE) as? AlarmManager
        val pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0)

        val calendar = Calendar.getInstance()

        calendar.add(Calendar.SECOND, 10)

        alarmManager?.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
        
    }
}

I can confirm that registering the receiver is indeed required.

nayriz
  • 353
  • 1
  • 17