Fix AndroidAlarmManager dropping alarms

It was possible that the 'callback' property was set to 'null' after another alarm was already scheduled. This meant the callback function wasn't called when the next alarm went off.
This commit is contained in:
cketti 2021-07-08 18:49:30 +02:00
parent 1809f4a1ef
commit cac4874f25
2 changed files with 30 additions and 14 deletions

View file

@ -9,14 +9,18 @@ import android.os.Build
import android.os.SystemClock
import com.fsck.k9.backend.imap.SystemAlarmManager
import com.fsck.k9.helper.AlarmManagerCompat
import java.util.concurrent.atomic.AtomicReference
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import timber.log.Timber
private const val ALARM_ACTION = "com.fsck.k9.backends.ALARM"
private const val REQUEST_CODE = 1
private typealias Callback = () -> Unit
class AndroidAlarmManager(
private val context: Context,
private val alarmManager: AlarmManagerCompat,
@ -33,17 +37,20 @@ class AndroidAlarmManager(
PendingIntent.getBroadcast(context, REQUEST_CODE, intent, flags)
}
@Volatile
private var callback: (() -> Unit)? = null
private val callback = AtomicReference<Callback?>(null)
init {
val intentFilter = IntentFilter(ALARM_ACTION)
context.registerReceiver(
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
coroutineScope.launch {
callback?.invoke()
callback = null
val callback = callback.getAndSet(null)
if (callback == null) {
Timber.w("Alarm triggered but 'callback' was null")
} else {
coroutineScope.launch {
callback.invoke()
}
}
}
},
@ -51,12 +58,13 @@ class AndroidAlarmManager(
)
}
override fun setAlarm(triggerTime: Long, callback: () -> Unit) {
this.callback = callback
override fun setAlarm(triggerTime: Long, callback: Callback) {
this.callback.set(callback)
alarmManager.scheduleAlarm(triggerTime, pendingIntent)
}
override fun cancelAlarm() {
callback.set(null)
alarmManager.cancelAlarm(pendingIntent)
}

View file

@ -9,14 +9,18 @@ import android.os.Build
import android.os.SystemClock
import com.fsck.k9.backend.imap.SystemAlarmManager
import com.fsck.k9.helper.AlarmManagerCompat
import java.util.concurrent.atomic.AtomicReference
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import timber.log.Timber
private const val ALARM_ACTION = "com.fsck.k9.backends.ALARM"
private const val REQUEST_CODE = 1
private typealias Callback = () -> Unit
class AndroidAlarmManager(
private val context: Context,
private val alarmManager: AlarmManagerCompat,
@ -33,17 +37,20 @@ class AndroidAlarmManager(
PendingIntent.getBroadcast(context, REQUEST_CODE, intent, flags)
}
@Volatile
private var callback: (() -> Unit)? = null
private val callback = AtomicReference<Callback?>(null)
init {
val intentFilter = IntentFilter(ALARM_ACTION)
context.registerReceiver(
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
coroutineScope.launch {
callback?.invoke()
callback = null
val callback = callback.getAndSet(null)
if (callback == null) {
Timber.w("Alarm triggered but 'callback' was null")
} else {
coroutineScope.launch {
callback.invoke()
}
}
}
},
@ -51,12 +58,13 @@ class AndroidAlarmManager(
)
}
override fun setAlarm(triggerTime: Long, callback: () -> Unit) {
this.callback = callback
override fun setAlarm(triggerTime: Long, callback: Callback) {
this.callback.set(callback)
alarmManager.scheduleAlarm(triggerTime, pendingIntent)
}
override fun cancelAlarm() {
callback.set(null)
alarmManager.cancelAlarm(pendingIntent)
}