How can I cancel an alarm set with setAlarmClock()?
val alarmReceiver = Intent(applicationContext, AlarmReceiver::class.java)
val alarmReceiverPi = PendingIntent.getBroadcast(applicationContext, 0, alarmReceiver, 0)
val alarmInfo = AlarmManager.AlarmClockInfo(time, alarmReceiverPi)
alarmManager.setAlarmClock(alarmInfo, alarmReceiverPi)
The id to used to locate the PendingIntent, following is my example:
fun setAlarm() {
val alarmPendingIntent = getAlarmPendingIntent(alarmId)
alarmManager.setExact(AlarmManager.RTC_WAKEUP, _timeLong.value, alarmPendingIntent)
showToast("Alarm has been set")
}
fun cancelAlarm() {
val alarmPendingIntent = getAlarmPendingIntent(alarmId)
alarmManager.cancel(alarmPendingIntent)
showToast("Alarm has bee canceled")
}
private fun getAlarmPendingIntent(id: Long): PendingIntent {
val intent = Intent(getApplication(), AlarmReceiver::class.java)
return PendingIntent.getBroadcast(getApplication(), id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT) //will override if same id
}
Helpful reading:
How to cancel alarm from AlarmManager
Related
I'm trying stopping running coroutineWorker from notification button. I tried 3 methods and 2 of them calls "Result.failure()" & working fine. However another one doesn't.
Below CoroutineWorker shows foregroundInfo and Starts ringtone.
class RingWork(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
companion object {
val ALARM_CHANNEL_ID = "alarm_channel6"
}
lateinit var ringtoneSound: Ringtone
val context = applicationContext
#RequiresApi(Build.VERSION_CODES.Q)
override suspend fun doWork(): Result {
return try {
val alarmId = inputData.getInt("alarmId", 0)
val notificationMgr =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
//CHANNEL
val alarmChannel = NotificationChannel(
ALARM_CHANNEL_ID, "alarm" ,NotificationManager.IMPORTANCE_HIGH
)
alarmChannel.setSound(null, null)
alarmChannel.enableVibration(false)
alarmChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
notificationMgr.createNotificationChannel(alarmChannel)
val fullScreenIntent = Intent(context, LockscreenActivity::class.java).putExtra("alarmId", alarmId)
//This calls "failure" properly
val fullScreenPendingIntent = PendingIntent.getActivity(context, 0, fullScreenIntent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
//This calls "failure" properly
val stop1PendingIntent =
WorkManager.getInstance(context).createCancelPendingIntent(getId())
val s2Intent = Intent(context, StopAlarmReceiver::class.java).putExtra("alarmId", alarmId)
//This is not.
val stop2PendingIntent = PendingIntent.getBroadcast(context, 1, s2Intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
val builder = NotificationCompat.Builder(context, ALARM_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentTitle("title")
.setFullScreenIntent(fullScreenPendingIntent, true)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_ALARM)
.setAutoCancel(true)
.setSound(null)
.setVibrate(null)
.addAction(R.drawable.ic_stat_name, "Stop1", stop1PendingIntent)
.addAction(R.drawable.ic_stat_name, "Stop2", stop2PendingIntent)
setForeground(
ForegroundInfo(1999999, builder.build(), FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
)
ringtoneSound =
RingtoneManager.getRingtone(context, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM))
ringtoneSound.play()
delay(30000L)
ringtoneSound.stop()
Result.success()
} catch (e: Exception) {
Result.failure()
} finally {
cleanup()
}
}
fun cleanup(){
ringtoneSound.stop()
}
}
In LockScreenActivity, there is a button to stop ringtone.
binding.stoppingbutton.setOnClickListener {
val workMgr = WorkManager.getInstance(applicationContext)
workMgr.cancelUniqueWork("RingWork-$alarmId")
finish()
}
This calls "result.failure" and "finally" then ringtone will stop, notification will disapear. working fine.
However, if I press "Stop2" button on the notification.
class StopAlarmReceiver: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val alarmId = intent.getIntExtra("alarmId", 0)
val workMgr = WorkManager.getInstance(context)
workMgr.cancelUniqueWork("RingWork-$alarmId")
}
}
It cancels worker, but it won't call "result.failure" and "finally", so ringtone won't stop. Notification also won't disappear.
fullScreenPendingIntent and stop2PendingIntent are doing the same thing, but why it won't behave same?
You can edit your PendingIntent like this and it will trigger onReceive:
val stop2PendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
PendingIntent.getBroadcast(context, downloadFile.productId, cancelIntent, PendingIntent.FLAG_IMMUTABLE)
else
PendingIntent.getBroadcast(context, downloadFile.productId, cancelIntent, 0)
Hello guys i'm making app for tracking insulin injections for my mom. I want to make app that will create notification automatically every 7 p.m. Can you advice me how to do it? I tried to google it but i'm only found videos with notifications by clicking button but it will not work for me because i want my app to create notifications even when app is closed so i need something like Services that will always by alive even app is closed
You firstly need to instantiate a Broadcast Receiver class and override their onReceive function.
Don't forget to add this to your manifest.
<receiver android:name=".AlertReceiver" />
Override the function as talked.
class AlertReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Glide.with(context).asBitmap().load(R.drawable.ic_diagnosis_24dp).into(object : CustomTarget<Bitmap>() {
override fun onLoadCleared(placeholder: Drawable?) {}
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
val notificationHelper = NotificationHelper(context,resource,intent.extras!!.getInt("requestCode"))
val nb = notificationHelper.channelNotification
notificationHelper.manager?.notify(1, nb.build())
}
})
}
}
You can prepare a Notification Helper just in case you want to deal with things in a better format.
class NotificationHelper(base: Context?, healthReportIcon: Bitmap, requestCode: Int) : ContextWrapper(base) {
private val healthIcon = healthReportIcon
private var mManager: NotificationManager? = null
private lateinit var beforeTime: String
private var intentActivity: Class<*>
#TargetApi(Build.VERSION_CODES.O)
private fun createChannel() {
val channel = NotificationChannel(channelID, channelName, NotificationManager.IMPORTANCE_HIGH)
manager!!.createNotificationChannel(channel)
}
val manager: NotificationManager? get() {
if (mManager == null) {
mManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
}
return mManager
}
val channelNotification: NotificationCompat.Builder get() = NotificationCompat.Builder(applicationContext, channelID)
.setContentTitle("Reminder!")
.setContentText("Update medical records before $beforeTime")
.setSmallIcon(R.drawable.ic_round_local_hospital_24)
.setColor(ContextCompat.getColor(this,R.color.blue_diff))
.setLargeIcon(healthIcon)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setPriority(NotificationCompat.PRIORITY_MAX)
.setTimeoutAfter(1800000)
.setAutoCancel(true)
.setContentIntent(PendingIntent.getActivity(baseContext, 0, Intent(baseContext, intentActivity), 0))
companion object {
const val channelID = "phoneId"
const val channelName = "phoneChannel"
}
init {
when (requestCode) {
0 -> {
beforeTime = "12:30 AM"
}
1 -> {
beforeTime = "6:30 AM"
}
2 -> {
beforeTime = "12:30 PM"
}
3 -> {
beforeTime = "6:30 PM"
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createChannel()
}
intentActivity = if(FirebaseAuth.getInstance().currentUser!=null) {
HomeActivity::class.java
} else{
Splash::class.java
}
}
}
Finally, set or cancel your alarms in your codebase.
private fun startAlarms() {
val cal0 = Calendar.getInstance()
cal0[Calendar.HOUR_OF_DAY]=24
cal0[Calendar.MINUTE]=0
cal0[Calendar.SECOND]=0
val cal1 = Calendar.getInstance()
cal1[Calendar.HOUR_OF_DAY]=6
cal1[Calendar.MINUTE]=0
cal1[Calendar.SECOND]=0
val cal2 = Calendar.getInstance()
cal2[Calendar.HOUR_OF_DAY]=12
cal2[Calendar.MINUTE]=0
cal2[Calendar.SECOND]=0
val cal3 = Calendar.getInstance()
cal3[Calendar.HOUR_OF_DAY]=18
cal3[Calendar.MINUTE]=0
cal3[Calendar.SECOND]=0
if (cal0.before(Calendar.getInstance())) {
cal0.add(Calendar.DATE, 1)
}
if (cal1.before(Calendar.getInstance())) {
cal1.add(Calendar.DATE, 1)
}
if (cal2.before(Calendar.getInstance())) {
cal2.add(Calendar.DATE, 1)
}
if (cal3.before(Calendar.getInstance())) {
cal3.add(Calendar.DATE, 1)
}
val alarmManager: AlarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(this, AlertReceiver::class.java)
intent.putExtra("requestCode",0)
alarmManager.setExact(AlarmManager.RTC_WAKEUP, cal0.timeInMillis, PendingIntent.getBroadcast(this, 0,intent , PendingIntent.FLAG_UPDATE_CURRENT))
intent.putExtra("requestCode",1)
alarmManager.setExact(AlarmManager.RTC_WAKEUP, cal1.timeInMillis, PendingIntent.getBroadcast(this, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT))
intent.putExtra("requestCode",2)
alarmManager.setExact(AlarmManager.RTC_WAKEUP, cal2.timeInMillis, PendingIntent.getBroadcast(this, 2,intent, PendingIntent.FLAG_UPDATE_CURRENT))
intent.putExtra("requestCode",3)
alarmManager.setExact(AlarmManager.RTC_WAKEUP, cal3.timeInMillis, PendingIntent.getBroadcast(this, 3, intent, PendingIntent.FLAG_UPDATE_CURRENT))
}
private fun cancelAlarms() {
val alarmManager: AlarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(this, AlertReceiver::class.java)
alarmManager.cancel(PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT))
alarmManager.cancel(PendingIntent.getBroadcast(this, 1, intent, PendingIntent.FLAG_CANCEL_CURRENT))
alarmManager.cancel(PendingIntent.getBroadcast(this, 2, intent, PendingIntent.FLAG_CANCEL_CURRENT))
alarmManager.cancel(PendingIntent.getBroadcast(this, 3, intent, PendingIntent.FLAG_CANCEL_CURRENT))
PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT).cancel()
PendingIntent.getBroadcast(this, 1, intent, PendingIntent.FLAG_CANCEL_CURRENT).cancel()
PendingIntent.getBroadcast(this, 2, intent, PendingIntent.FLAG_CANCEL_CURRENT).cancel()
PendingIntent.getBroadcast(this, 3, intent, PendingIntent.FLAG_CANCEL_CURRENT).cancel()
}
Hope this helped you out. Happy Coding! :)
I'm trying to schedule notifications using the AlarmManager but for whatever reason, the onReceive method on my receiver isn't firing.
Here's how I'm scheduling the alarm
val intent = Intent(this, ReminderReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT)
val cal: Calendar = Calendar.getInstance()
cal.add(Calendar.SECOND, 5)
val amanager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
amanager.set(AlarmManager.RTC_WAKEUP, cal.timeInMillis, pendingIntent)
and here's the onReceive method
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(context,"notification", Toast.LENGTH_SHORT).show()
}
I tried manually sending a broadcast to see if it works and there's no problem there so the issue shouldn't be related to the manifest.
I'm running this on MIUI12 android 10
I see no issue with your logic if your problem still prevails, try the following as a workaround
val intent = Intent(this, ReminderReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT)
// val cal: Calendar = Calendar.getInstance()
// cal.add(Calendar.SECOND, 5)
val triggerTime = SystemClock.elapsedRealtime() + 5_000 // five seconds from now
val amanager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
// amanager.set(AlarmManager.RTC_WAKEUP, cal.timeInMillis, pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(
amanager,
AlarmManager.ELAPSED_REALTIME_WAKEUP,
triggerTime,
pendingIntent
)
I tried to create an app that send a notification (with a customised text) when the time is the same the user inserted in the timePicker, but the notification doesn't appear at all.
MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val UniqueID = 47245
timePicker.setIs24HourView(true)
button1.setOnClickListener {
if((Calendar.HOUR_OF_DAY == timePicker.hour) && (Calendar.MINUTE == timePicker.minute)) {
var notification = NotificationCompat.Builder(this, "M_CH_ID")
notification.setSmallIcon(R.mipmap.ic_launcher)
notification.setTicker(editText.text)
notification.setWhen(System.currentTimeMillis())
notification.setContentTitle("Notifica")
notification.setContentText(editText.text)
var ringtone: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
var intent = Intent(this, MainActivity::class.java)
var pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
notification.setContentIntent(pendingIntent)
notification.setLights(Color.RED, 2500, 1000)
notification.setSound(ringtone)
notification.setAutoCancel(true)
var notManager: NotificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notManager.notify(UniqueID, notification.build())
}
}
}
Calendar.HOUR_OF_DAY and Calendar.MINUTE are constant values in the Calendar class to use with the get() method. To get the current hour and minute, you can do the following:
val now = Calendar.newInstance()
val hour = now.get(Calendar.HOUR_OF_DAY)
val minute = now.get(Calendar.MINUTE)
These are the values that your if check should be against.
After I call schedulePing my alarm fires at set time period. With pingScheduled I can see if pending intent exists. However, if I cancel the alarm with cancelPing the pingScheduled still return true.
fun schedulePing(context: Context) {
val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(context, EventReceiver::class.java)
intent.action = EventReceiver.PING
val pendingIntent = PendingIntent.getBroadcast(context, PING, intent,
PendingIntent.FLAG_CANCEL_CURRENT)
val s = SettingsUtil.load(context)
am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + s.periodPing, s.periodPing,
pendingIntent)
}
fun cancelPing(context: Context) {
if (pingScheduled(context).not()) return
val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(context, EventReceiver::class.java)
intent.action = EventReceiver.PING
val pendingIntent = PendingIntent.getBroadcast(context, PING, intent,
PendingIntent.FLAG_CANCEL_CURRENT)
am.cancel(pendingIntent)
}
fun pingScheduled(context: Context): Boolean {
val intent = Intent(context, EventReceiver::class.java)
intent.action = EventReceiver.PING
return PendingIntent.getBroadcast(context, PING,
intent, PendingIntent.FLAG_NO_CREATE) != null
}
am.cancel(pendingIntent) cancel alram, but does not discard the pending intent. So it is important to call
pendingIntent.cancel after am.cancel to be able to check correct presence with PendingIntent.FLAG_NO_CREATE