Skip to content

Commit 0d62cc5

Browse files
committed
fix: repeated notifications and wakeup screens (Phase 7 bug fix)
1 parent 717e524 commit 0d62cc5

3 files changed

Lines changed: 28 additions & 12 deletions

File tree

app/src/main/java/com/hotbell/radio/alarms/AlarmReceiver.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ class AlarmReceiver : BroadcastReceiver() {
2020
private const val TAG = "AlarmReceiver"
2121
private const val CHANNEL_ID = "hotbell_alarm_channel"
2222
private const val CHANNEL_NAME = "HotBell Alarms"
23+
24+
// simple debounce to avoid multiple triggers in quick succession (Bug fix Phase 7)
25+
private val lastTriggerTimes = mutableMapOf<String, Long>()
26+
private const val DEBOUNCE_MS = 10000L
2327
}
2428

2529
override fun onReceive(context: Context, intent: Intent) {
@@ -33,6 +37,14 @@ class AlarmReceiver : BroadcastReceiver() {
3337
val dismissType = intent.getStringExtra("EXTRA_DISMISS_TYPE") ?: "math"
3438
val targetPhotoPath = intent.getStringExtra("EXTRA_TARGET_PHOTO_PATH")
3539

40+
val currentTime = System.currentTimeMillis()
41+
val lastTime = lastTriggerTimes[alarmId] ?: 0L
42+
if (currentTime - lastTime < DEBOUNCE_MS) {
43+
Log.d(TAG, "Duplicate alarm trigger for $alarmId within debounce window. Skipping.")
44+
return
45+
}
46+
lastTriggerTimes[alarmId] = currentTime
47+
3648
Log.d(TAG, "Alarm fired via Broadcast! id=$alarmId, station=$stationName")
3749

3850
// Check skipNext from DB (uses goAsync for coroutine)

app/src/main/java/com/hotbell/radio/ui/wakeup/WakeUpViewModel.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,14 @@ class WakeUpViewModel(application: Application) : AndroidViewModel(application)
5959
private var alarmStationUuid: String? = null
6060
private var alarmStationName: String? = null
6161
private var alarmStationUrl: String? = null
62-
6362
private var flashlightJob: Job? = null
64-
6563
private var fallbackTimeoutJob: Job? = null
6664

6765
// Alarm ID for notification cancellation (Fix #3)
6866
private var alarmId: String = ""
6967

68+
private var alarmStarted = false
69+
7070
// Photo Match
7171
private val _dismissType = MutableStateFlow("math")
7272
val dismissType: StateFlow<String> = _dismissType.asStateFlow()
@@ -95,6 +95,12 @@ class WakeUpViewModel(application: Application) : AndroidViewModel(application)
9595
}
9696

9797
fun startAlarm(context: Context, stationUuid: String?, stationName: String?, stationUrl: String? = null) {
98+
if (alarmStarted) {
99+
android.util.Log.d("WakeUpViewModel", "Alarm already started, skipping")
100+
return
101+
}
102+
alarmStarted = true
103+
98104
android.util.Log.d("WakeUpViewModel", "startAlarm called with stationUuid=$stationUuid, stationName=$stationName, stationUrl=$stationUrl")
99105

100106
// Save station info for snooze

app/src/main/java/com/hotbell/radio/utils/AlarmUtils.kt

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,19 @@ object AlarmUtils {
1313
set(Calendar.MILLISECOND, 0)
1414
}
1515

16-
// Drop seconds/millis from 'now' to allow setting an alarm for the current minute without skipping to tomorrow
17-
val nowMinute = Calendar.getInstance().apply {
18-
set(Calendar.SECOND, 0)
19-
set(Calendar.MILLISECOND, 0)
20-
}
21-
16+
// Use real current time for comparison to ensure strictly future triggers
17+
val now = Calendar.getInstance()
18+
2219
if (daysOfWeek == 0) {
23-
// One-time alarm: if time already passed today (comparing only up to minute precision), set for tomorrow
24-
if (alarm.before(nowMinute)) {
20+
// One-time alarm: if time already passed today, set for tomorrow
21+
if (!alarm.after(now)) {
2522
alarm.add(Calendar.DAY_OF_MONTH, 1)
2623
}
2724
return alarm.timeInMillis
2825
}
2926

3027
// Repeating alarm: find the next matching day
31-
for (i in 0..6) {
28+
for (i in 0..14) { // Look further ahead to be safe (though 7 is enough)
3229
val candidate = Calendar.getInstance().apply {
3330
timeInMillis = alarm.timeInMillis
3431
add(Calendar.DAY_OF_MONTH, i)
@@ -37,7 +34,8 @@ object AlarmUtils {
3734
// Our bitmask: bit 0=Sunday, bit 1=Monday, ..., bit 6=Saturday
3835
val dayBit = candidate.get(Calendar.DAY_OF_WEEK) - 1
3936
if (daysOfWeek and (1 shl dayBit) != 0) {
40-
if (i == 0 && candidate.before(nowMinute)) continue
37+
// If today, candidate must be strictly in the future
38+
if (i == 0 && !candidate.after(now)) continue
4139
return candidate.timeInMillis
4240
}
4341
}

0 commit comments

Comments
 (0)