Skip to content

Commit 4ee1897

Browse files
Merge pull request #18 from meticha/feature/cancel-all-alarm
Added functionality to cancel all alarms
2 parents 968eec9 + f933a2e commit 4ee1897

File tree

4 files changed

+97
-10
lines changed

4 files changed

+97
-10
lines changed

.github/workflows/release.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
- main
88

99
env:
10-
VERSION_NAME: 0.0.6 # Update this value for each release
10+
VERSION_NAME: 0.0.7 # Update this value for each release
1111

1212
jobs:
1313
release:

gradle/libs.versions.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
[versions]
22
agp = "8.10.0"
33
datastorePreferences = "1.1.7"
4-
kotlin = "2.1.20"
4+
kotlin = "2.1.21"
55
coreKtx = "1.16.0"
66
junit = "4.13.2"
77
junitVersion = "1.2.1"
88
espressoCore = "3.6.1"
99
lifecycleRuntimeKtx = "2.9.1"
1010
activityCompose = "1.10.1"
11-
composeBom = "2025.06.00"
11+
composeBom = "2025.06.01"
1212
appcompat = "1.7.1"
1313
kotlinSerialization = "2.1.21"
1414
hiltNavigation = "1.2.0"
1515
hilt = "2.56.2"
16-
ksp = "2.1.20-2.0.1"
16+
ksp = "2.1.21-2.0.1"
1717
spotless = "7.0.4"
1818

1919

triggerx/src/main/java/com/meticha/triggerx/TriggerXAlarmScheduler.kt

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ import android.content.Intent
2222
import android.os.Build
2323
import androidx.core.content.getSystemService
2424
import com.meticha.triggerx.logger.LoggerConfig
25+
import com.meticha.triggerx.preference.TriggerXAlarmIdManager
2526
import com.meticha.triggerx.receivers.TriggerXAlarmReceiver
2627

28+
2729
/**
2830
* Manages the scheduling and cancellation of alarms using Android's [AlarmManager].
2931
* This class provides methods to schedule single or multiple alarms and to cancel existing ones.
@@ -44,7 +46,7 @@ class TriggerXAlarmScheduler {
4446
* @return `true` if the alarm was successfully scheduled, `false` otherwise (e.g., if
4547
* permission is denied or an exception occurs).
4648
*/
47-
fun scheduleAlarm(
49+
suspend fun scheduleAlarm(
4850
context: Context,
4951
triggerAtMillis: Long,
5052
type: String,
@@ -80,6 +82,7 @@ class TriggerXAlarmScheduler {
8082
triggerAtMillis,
8183
pendingIntent
8284
)
85+
TriggerXAlarmIdManager.saveAlarmId(context, alarmId)
8386
LoggerConfig.logger.i("Alarm [$alarmId] scheduled for $triggerAtMillis")
8487
return true
8588
} catch (se: SecurityException) {
@@ -104,7 +107,7 @@ class TriggerXAlarmScheduler {
104107
* @return `true` if the alarm was successfully scheduled, `false` otherwise.
105108
* @see scheduleAlarm
106109
*/
107-
fun scheduleAlarm(
110+
suspend fun scheduleAlarm(
108111
context: Context,
109112
triggerAtMillis: Long,
110113
alarmId: Int = (System.currentTimeMillis() % Int.MAX_VALUE).toInt()
@@ -121,7 +124,7 @@ class TriggerXAlarmScheduler {
121124
* @return A list of [Boolean] values, where each boolean indicates whether the
122125
* corresponding alarm in the `events` list was successfully scheduled.
123126
*/
124-
fun scheduleMultipleAlarms(
127+
suspend fun scheduleMultipleAlarms(
125128
context: Context,
126129
events: List<Pair<Int, Long>> // Pair<alarmId, triggerTime>
127130
): List<Boolean> {
@@ -139,7 +142,7 @@ class TriggerXAlarmScheduler {
139142
* @return A list of [Boolean] values, where each boolean indicates whether the
140143
* corresponding alarm in the `events` list was successfully scheduled.
141144
*/
142-
fun scheduleMultipleAlarms(
145+
suspend fun scheduleMultipleAlarms(
143146
context: Context,
144147
type: String,
145148
events: List<Pair<Int, Long>> // Pair<alarmId, triggerTime>
@@ -159,7 +162,7 @@ class TriggerXAlarmScheduler {
159162
* @return A list of [Int] values, where each integer is an ID of an alarm that was
160163
* successfully scheduled.
161164
*/
162-
fun scheduleAlarms(
165+
suspend fun scheduleAlarms(
163166
context: Context,
164167
events: List<Pair<Int, Long>> // Pair<alarmId, triggerTime>
165168
): List<Int> {
@@ -184,7 +187,7 @@ class TriggerXAlarmScheduler {
184187
* @param context The application context.
185188
* @param alarmId The unique integer identifier of the alarm to cancel.
186189
*/
187-
fun cancelAlarm(context: Context, alarmId: Int) {
190+
suspend fun cancelAlarm(context: Context, alarmId: Int) {
188191
val intent = Intent(context, TriggerXAlarmReceiver::class.java).apply {
189192
action = TriggerXAlarmReceiver.ALARM_ACTION
190193
}
@@ -199,9 +202,46 @@ class TriggerXAlarmScheduler {
199202
val alarmManager = context.getSystemService(AlarmManager::class.java)
200203
alarmManager.cancel(pendingIntent)
201204
pendingIntent.cancel()
205+
TriggerXAlarmIdManager.removeAlarmId(context, alarmId)
202206
LoggerConfig.logger.i("Alarm [$alarmId] cancelled.")
203207
} else {
204208
LoggerConfig.logger.i("Alarm [$alarmId] not found to cancel.")
205209
}
206210
}
211+
212+
/**
213+
* Cancels all scheduled alarms.
214+
*
215+
* This method retrieves all stored alarm IDs and cancels each one.
216+
*
217+
* @param context The application context.
218+
*/
219+
suspend fun cancelAllAlarms(context: Context) {
220+
try {
221+
val alarmIds = TriggerXAlarmIdManager.getAlarmIds(context)
222+
var cancelledCount = 0
223+
var failedCount = 0
224+
alarmIds.forEach { idString ->
225+
try {
226+
val id = idString.toIntOrNull()
227+
if (id != null) {
228+
cancelAlarm(context, id)
229+
cancelledCount++
230+
} else {
231+
LoggerConfig.logger.w("Invalid alarm ID format: $idString")
232+
failedCount++
233+
}
234+
} catch (e: Exception) {
235+
LoggerConfig.logger.e("Failed to cancel alarm ID: $idString", e)
236+
failedCount++
237+
}
238+
}
239+
240+
TriggerXAlarmIdManager.clearAllAlarmIds(context)
241+
LoggerConfig.logger.i("Cancelled $cancelledCount alarms. Failed: $failedCount")
242+
} catch (e: Exception) {
243+
LoggerConfig.logger.e("Failed to cancel all alarms", e)
244+
throw e
245+
}
246+
}
207247
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.meticha.triggerx.preference
2+
3+
import android.content.Context
4+
import androidx.datastore.core.DataStore
5+
import androidx.datastore.preferences.core.Preferences
6+
import androidx.datastore.preferences.core.edit
7+
import androidx.datastore.preferences.core.stringSetPreferencesKey
8+
import androidx.datastore.preferences.preferencesDataStore
9+
import kotlinx.coroutines.flow.first
10+
import kotlinx.coroutines.flow.map
11+
12+
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "triggerx_alarm_ids")
13+
14+
internal object TriggerXAlarmIdManager {
15+
16+
private val KEY_ALARM_IDS = stringSetPreferencesKey("alarm_ids")
17+
18+
suspend fun saveAlarmId(context: Context, id: Int) {
19+
context.dataStore.edit { settings ->
20+
val currentIds = settings[KEY_ALARM_IDS] ?: emptySet()
21+
settings[KEY_ALARM_IDS] = currentIds + id.toString()
22+
}
23+
}
24+
25+
suspend fun removeAlarmId(context: Context, id: Int) {
26+
context.dataStore.edit { settings ->
27+
val currentIds = settings[KEY_ALARM_IDS] ?: return@edit
28+
val idString = id.toString()
29+
if (idString in currentIds) {
30+
settings[KEY_ALARM_IDS] = currentIds - idString
31+
}
32+
}
33+
}
34+
35+
suspend fun getAlarmIds(context: Context): Set<String> {
36+
return context.dataStore.data
37+
.map { preferences ->
38+
preferences[KEY_ALARM_IDS] ?: emptySet()
39+
}.first()
40+
}
41+
42+
suspend fun clearAllAlarmIds(context: Context) {
43+
context.dataStore.edit { settings ->
44+
settings.remove(KEY_ALARM_IDS)
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)