react-native-alarmageddon 1.1.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +169 -189
- package/android/build/.transforms/33d69af0aa8d226a34981fd71aab63e2/results.bin +1 -0
- package/android/build/.transforms/33d69af0aa8d226a34981fd71aab63e2/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/generated/source/buildConfig/debug/com/rnalarmmodule/BuildConfig.java +10 -0
- package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +41 -0
- package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +18 -0
- package/android/build/intermediates/aar_metadata/debug/writeDebugAarMetadata/aar-metadata.properties +6 -0
- package/android/build/intermediates/annotation_processor_list/debug/javaPreCompileDebug/annotationProcessors.json +1 -0
- package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
- package/android/build/intermediates/compile_symbol_list/debug/generateDebugRFile/R.txt +1 -0
- package/android/build/intermediates/compiled_local_resources/debug/compileDebugLibraryResources/out/raw_alarm_default.wav.flat +0 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +2 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +2 -0
- package/android/build/intermediates/incremental/mergeDebugAssets/merger.xml +2 -0
- package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +2 -0
- package/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +2 -0
- package/android/build/intermediates/java_res/debug/processDebugJavaRes/out/META-INF/react-native-alarmageddon_debug.kotlin_module +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/rnalarmmodule/BuildConfig.class +0 -0
- package/android/build/intermediates/local_only_symbol_list/debug/parseDebugLocalResources/R-def.txt +3 -0
- package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +68 -0
- package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +41 -0
- package/android/build/intermediates/navigation_json/debug/extractDeepLinksDebug/navigation.json +1 -0
- package/android/build/intermediates/nested_resources_validation_report/debug/generateDebugResources/nestedResourcesValidationReport.txt +1 -0
- package/android/build/intermediates/packaged_res/debug/packageDebugResources/raw/alarm_default.wav +0 -0
- package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +2 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/counters.tab +2 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
- package/android/build/outputs/logs/manifest-merger-debug-report.txt +71 -0
- package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
- package/android/build/tmp/kotlin-classes/debug/META-INF/react-native-alarmageddon_debug.kotlin_module +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/AlarmModule$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/AlarmModule.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/AlarmPackage.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/AlarmReceiver$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/AlarmReceiver.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/BootReceiver$Companion.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/BootReceiver.class +0 -0
- package/android/build.gradle +11 -21
- package/android/src/main/AndroidManifest.xml +16 -35
- package/android/src/main/java/com/rnalarmmodule/AlarmModule.kt +154 -390
- package/android/src/main/java/com/rnalarmmodule/AlarmPackage.kt +0 -1
- package/android/src/main/java/com/rnalarmmodule/AlarmReceiver.kt +290 -129
- package/android/src/main/java/com/rnalarmmodule/BootReceiver.kt +66 -124
- package/android/src/main/res/raw/alarm_default.wav +0 -0
- package/ios/{RNAlarmModule.m → AlarmModule.m} +9 -20
- package/ios/AlarmModule.swift +190 -36
- package/ios/RNAlarmModule-Bridging-Header.h +5 -1
- package/lib/index.d.ts +47 -85
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +60 -120
- package/lib/index.js.map +1 -1
- package/package.json +14 -23
- package/react-native-alarmageddon.podspec +10 -8
- package/react-native.config.js +3 -1
- package/src/index.ts +94 -201
- package/android/gradle.properties +0 -13
- package/android/src/main/java/com/rnalarmmodule/AlarmActivity.kt +0 -79
- package/android/src/main/java/com/rnalarmmodule/AlarmService.kt +0 -290
- package/android/src/main/res/raw/README.md +0 -36
|
@@ -1,156 +1,98 @@
|
|
|
1
1
|
package com.rnalarmmodule
|
|
2
2
|
|
|
3
|
-
import android.app.AlarmManager
|
|
4
|
-
import android.app.PendingIntent
|
|
5
3
|
import android.content.BroadcastReceiver
|
|
6
4
|
import android.content.Context
|
|
7
5
|
import android.content.Intent
|
|
6
|
+
import android.app.AlarmManager
|
|
7
|
+
import android.app.PendingIntent
|
|
8
8
|
import android.os.Build
|
|
9
|
+
import org.json.JSONObject
|
|
10
|
+
import java.text.SimpleDateFormat
|
|
11
|
+
import java.util.Locale
|
|
9
12
|
import android.util.Log
|
|
10
|
-
import org.json.JSONArray
|
|
11
13
|
|
|
12
14
|
class BootReceiver : BroadcastReceiver() {
|
|
13
15
|
|
|
14
16
|
companion object {
|
|
15
17
|
private const val TAG = "BootReceiver"
|
|
16
|
-
private const val
|
|
17
|
-
private const val
|
|
18
|
+
private const val PREFS = "rn_alarm_module_alarms"
|
|
19
|
+
private const val DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss"
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
override fun onReceive(context: Context, intent: Intent) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
when (action) {
|
|
22
|
+
override fun onReceive(context: Context, intent: Intent?) {
|
|
23
|
+
if (intent == null) return
|
|
24
|
+
when (intent.action) {
|
|
25
25
|
Intent.ACTION_BOOT_COMPLETED,
|
|
26
|
-
Intent.ACTION_LOCKED_BOOT_COMPLETED,
|
|
27
26
|
Intent.ACTION_TIME_CHANGED,
|
|
28
|
-
Intent.ACTION_TIMEZONE_CHANGED
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
else -> {
|
|
33
|
-
Log.w(TAG, "Unhandled action: $action")
|
|
27
|
+
Intent.ACTION_TIMEZONE_CHANGED -> {
|
|
28
|
+
Log.d(TAG, "Rescheduling alarms after: ${intent.action}")
|
|
29
|
+
rescheduleAlarms(context)
|
|
34
30
|
}
|
|
35
31
|
}
|
|
36
32
|
}
|
|
37
33
|
|
|
38
|
-
private fun
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
private fun rescheduleAlarms(context: Context) {
|
|
35
|
+
val prefs = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
|
36
|
+
val all = prefs.all
|
|
37
|
+
if (all.isEmpty()) {
|
|
38
|
+
Log.d(TAG, "No alarms to reschedule.")
|
|
39
|
+
return
|
|
40
|
+
}
|
|
45
41
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
val sdf = SimpleDateFormat(DATE_PATTERN, Locale.getDefault())
|
|
43
|
+
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
|
44
|
+
val now = System.currentTimeMillis()
|
|
49
45
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
val id = alarm.getString("id")
|
|
53
|
-
val triggerTime = alarm.getLong("triggerTime")
|
|
54
|
-
val title = alarm.optString("title", "Alarm")
|
|
55
|
-
val body = alarm.optString("body", "")
|
|
56
|
-
val soundUri = alarm.optString("soundUri", "")
|
|
57
|
-
val vibrate = alarm.optBoolean("vibrate", true)
|
|
58
|
-
val snoozeMinutes = alarm.optInt("snoozeMinutes", 5)
|
|
59
|
-
val autoStopSeconds = alarm.optInt("autoStopSeconds", 60)
|
|
46
|
+
var rescheduledCount = 0
|
|
47
|
+
var skippedCount = 0
|
|
60
48
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
49
|
+
for ((id, rawJson) in all) {
|
|
50
|
+
val jsonStr = rawJson as? String ?: continue
|
|
51
|
+
val obj = try {
|
|
52
|
+
JSONObject(jsonStr)
|
|
53
|
+
} catch (_: Exception) {
|
|
54
|
+
continue
|
|
55
|
+
}
|
|
56
|
+
val datetimeISO = obj.optString("datetimeISO", "")
|
|
57
|
+
val title = obj.optString("title", "Alarm")
|
|
58
|
+
val body = obj.optString("body", "")
|
|
59
|
+
|
|
60
|
+
val date = try {
|
|
61
|
+
sdf.parse(datetimeISO)
|
|
62
|
+
} catch (_: Exception) {
|
|
63
|
+
null
|
|
64
|
+
} ?: continue
|
|
65
|
+
|
|
66
|
+
val triggerAt = date.time
|
|
67
|
+
if (triggerAt <= now) {
|
|
68
|
+
// Past alarms: skip (could optionally fire immediately if desired)
|
|
69
|
+
skippedCount++
|
|
70
|
+
continue
|
|
80
71
|
}
|
|
81
72
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
Log.e(TAG, "Failed to reschedule alarms: ${e.message}", e)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
private fun scheduleAlarm(
|
|
92
|
-
context: Context,
|
|
93
|
-
id: String,
|
|
94
|
-
triggerTimeMillis: Long,
|
|
95
|
-
title: String,
|
|
96
|
-
body: String,
|
|
97
|
-
soundUri: String,
|
|
98
|
-
vibrate: Boolean,
|
|
99
|
-
snoozeMinutes: Int,
|
|
100
|
-
autoStopSeconds: Int
|
|
101
|
-
) {
|
|
102
|
-
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
|
103
|
-
|
|
104
|
-
val intent = Intent(context, AlarmReceiver::class.java).apply {
|
|
105
|
-
action = AlarmReceiver.ACTION_TRIGGER
|
|
106
|
-
putExtra("alarmId", id)
|
|
107
|
-
putExtra("title", title)
|
|
108
|
-
putExtra("body", body)
|
|
109
|
-
putExtra("soundUri", soundUri)
|
|
110
|
-
putExtra("vibrate", vibrate)
|
|
111
|
-
putExtra("snoozeMinutes", snoozeMinutes)
|
|
112
|
-
putExtra("autoStopSeconds", autoStopSeconds)
|
|
113
|
-
}
|
|
73
|
+
val alarmIntent = Intent(context, AlarmReceiver::class.java).apply {
|
|
74
|
+
putExtra("id", id)
|
|
75
|
+
putExtra("title", title)
|
|
76
|
+
putExtra("body", body)
|
|
77
|
+
}
|
|
114
78
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
79
|
+
val pendingIntent = PendingIntent.getBroadcast(
|
|
80
|
+
context,
|
|
81
|
+
id.hashCode(),
|
|
82
|
+
alarmIntent,
|
|
83
|
+
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
|
84
|
+
)
|
|
121
85
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (alarmManager.canScheduleExactAlarms()) {
|
|
125
|
-
alarmManager.setAlarmClock(
|
|
126
|
-
AlarmManager.AlarmClockInfo(triggerTimeMillis, pendingIntent),
|
|
127
|
-
pendingIntent
|
|
128
|
-
)
|
|
129
|
-
} else {
|
|
130
|
-
// Fallback to inexact alarm if exact permission not granted
|
|
131
|
-
alarmManager.setAndAllowWhileIdle(
|
|
132
|
-
AlarmManager.RTC_WAKEUP,
|
|
133
|
-
triggerTimeMillis,
|
|
134
|
-
pendingIntent
|
|
135
|
-
)
|
|
136
|
-
Log.w(TAG, "Exact alarm permission not granted, using inexact alarm for: $id")
|
|
137
|
-
}
|
|
138
|
-
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
139
|
-
alarmManager.setAlarmClock(
|
|
140
|
-
AlarmManager.AlarmClockInfo(triggerTimeMillis, pendingIntent),
|
|
141
|
-
pendingIntent
|
|
142
|
-
)
|
|
86
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
87
|
+
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAt, pendingIntent)
|
|
143
88
|
} else {
|
|
144
|
-
alarmManager.setExact(
|
|
145
|
-
AlarmManager.RTC_WAKEUP,
|
|
146
|
-
triggerTimeMillis,
|
|
147
|
-
pendingIntent
|
|
148
|
-
)
|
|
89
|
+
alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerAt, pendingIntent)
|
|
149
90
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
Log.e(TAG, "Failed to schedule alarm $id: ${e.message}", e)
|
|
91
|
+
|
|
92
|
+
rescheduledCount++
|
|
93
|
+
Log.d(TAG, "Rescheduled alarm id=$id at=$datetimeISO")
|
|
154
94
|
}
|
|
95
|
+
|
|
96
|
+
Log.d(TAG, "Rescheduling complete: $rescheduledCount rescheduled, $skippedCount skipped (past)")
|
|
155
97
|
}
|
|
156
98
|
}
|
|
Binary file
|
|
@@ -3,46 +3,35 @@
|
|
|
3
3
|
|
|
4
4
|
@interface RCT_EXTERN_MODULE(AlarmModule, RCTEventEmitter)
|
|
5
5
|
|
|
6
|
+
RCT_EXTERN_METHOD(requestPermissions:(RCTPromiseResolveBlock)resolve
|
|
7
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
8
|
+
|
|
6
9
|
RCT_EXTERN_METHOD(scheduleAlarm:(NSDictionary *)alarm
|
|
7
10
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
8
11
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
9
12
|
|
|
10
|
-
RCT_EXTERN_METHOD(cancelAlarm:(NSString *)
|
|
13
|
+
RCT_EXTERN_METHOD(cancelAlarm:(NSString *)id
|
|
11
14
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
12
15
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
13
16
|
|
|
14
17
|
RCT_EXTERN_METHOD(listAlarms:(RCTPromiseResolveBlock)resolve
|
|
15
18
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
16
19
|
|
|
17
|
-
RCT_EXTERN_METHOD(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
RCT_EXTERN_METHOD(checkExactAlarmPermission:(RCTPromiseResolveBlock)resolve
|
|
21
|
-
rejecter:(RCTPromiseRejectBlock)reject)
|
|
22
|
-
|
|
23
|
-
RCT_EXTERN_METHOD(openExactAlarmSettings:(RCTPromiseResolveBlock)resolve
|
|
24
|
-
rejecter:(RCTPromiseRejectBlock)reject)
|
|
25
|
-
|
|
26
|
-
RCT_EXTERN_METHOD(snoozeAlarm:(NSString *)alarmId
|
|
27
|
-
minutes:(nonnull NSNumber *)minutes
|
|
20
|
+
RCT_EXTERN_METHOD(snoozeAlarm:(NSString *)id
|
|
21
|
+
minutes:(int)minutes
|
|
28
22
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
29
23
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
30
24
|
|
|
31
|
-
RCT_EXTERN_METHOD(stopCurrentAlarm:(NSString *)
|
|
25
|
+
RCT_EXTERN_METHOD(stopCurrentAlarm:(NSString *)id
|
|
32
26
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
33
27
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
34
28
|
|
|
35
|
-
RCT_EXTERN_METHOD(snoozeCurrentAlarm:(NSString *)
|
|
36
|
-
minutes:(
|
|
29
|
+
RCT_EXTERN_METHOD(snoozeCurrentAlarm:(NSString *)id
|
|
30
|
+
minutes:(int)minutes
|
|
37
31
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
38
32
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
39
33
|
|
|
40
34
|
RCT_EXTERN_METHOD(getCurrentAlarmPlaying:(RCTPromiseResolveBlock)resolve
|
|
41
35
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
42
36
|
|
|
43
|
-
+ (BOOL)requiresMainQueueSetup
|
|
44
|
-
{
|
|
45
|
-
return NO;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
37
|
@end
|
package/ios/AlarmModule.swift
CHANGED
|
@@ -1,79 +1,233 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import React
|
|
3
|
+
import UserNotifications
|
|
3
4
|
|
|
4
5
|
@objc(AlarmModule)
|
|
5
6
|
class AlarmModule: RCTEventEmitter {
|
|
6
7
|
|
|
7
|
-
private
|
|
8
|
+
private static let PREFS_KEY = "rn_alarm_module_alarms"
|
|
8
9
|
|
|
9
10
|
override init() {
|
|
10
11
|
super.init()
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
@objc override static func requiresMainQueueSetup() -> Bool {
|
|
14
|
-
return
|
|
15
|
+
return true
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
override func supportedEvents() -> [String]! {
|
|
18
19
|
return ["activeAlarmId"]
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
// MARK: - Permission Methods
|
|
23
|
+
|
|
24
|
+
@objc(requestPermissions:rejecter:)
|
|
25
|
+
func requestPermissions(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
26
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
27
|
+
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
|
|
28
|
+
if let error = error {
|
|
29
|
+
reject("PERMISSION_ERROR", error.localizedDescription, error)
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
resolve(["granted": granted])
|
|
33
|
+
}
|
|
23
34
|
}
|
|
24
35
|
|
|
25
|
-
|
|
26
|
-
|
|
36
|
+
// MARK: - Alarm Scheduling
|
|
37
|
+
|
|
38
|
+
@objc(scheduleAlarm:resolver:rejecter:)
|
|
39
|
+
func scheduleAlarm(_ alarm: NSDictionary,
|
|
40
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
41
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
42
|
+
guard let id = alarm["id"] as? String,
|
|
43
|
+
let datetimeISO = alarm["datetimeISO"] as? String else {
|
|
44
|
+
reject("INVALID_PARAMS", "id and datetimeISO are required", nil)
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let title = alarm["title"] as? String ?? "Alarm"
|
|
49
|
+
let body = alarm["body"] as? String ?? ""
|
|
50
|
+
|
|
51
|
+
// Parse ISO date
|
|
52
|
+
let dateFormatter = ISO8601DateFormatter()
|
|
53
|
+
dateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
|
54
|
+
|
|
55
|
+
var triggerDate: Date?
|
|
56
|
+
triggerDate = dateFormatter.date(from: datetimeISO)
|
|
57
|
+
|
|
58
|
+
// Try without fractional seconds if first parse fails
|
|
59
|
+
if triggerDate == nil {
|
|
60
|
+
dateFormatter.formatOptions = [.withInternetDateTime]
|
|
61
|
+
triggerDate = dateFormatter.date(from: datetimeISO)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Try basic format
|
|
65
|
+
if triggerDate == nil {
|
|
66
|
+
let basicFormatter = DateFormatter()
|
|
67
|
+
basicFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
|
|
68
|
+
basicFormatter.locale = Locale(identifier: "en_US_POSIX")
|
|
69
|
+
triggerDate = basicFormatter.date(from: datetimeISO)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
guard let date = triggerDate else {
|
|
73
|
+
reject("INVALID_DATE", "Could not parse datetimeISO: \(datetimeISO)", nil)
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Create notification content
|
|
78
|
+
let content = UNMutableNotificationContent()
|
|
79
|
+
content.title = title
|
|
80
|
+
content.body = body
|
|
81
|
+
content.sound = UNNotificationSound.default
|
|
82
|
+
content.categoryIdentifier = "ALARM_CATEGORY"
|
|
83
|
+
content.userInfo = ["alarmId": id]
|
|
84
|
+
|
|
85
|
+
// Create trigger
|
|
86
|
+
let calendar = Calendar.current
|
|
87
|
+
let components = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date)
|
|
88
|
+
let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: false)
|
|
89
|
+
|
|
90
|
+
// Create request
|
|
91
|
+
let request = UNNotificationRequest(identifier: id, content: content, trigger: trigger)
|
|
92
|
+
|
|
93
|
+
UNUserNotificationCenter.current().add(request) { error in
|
|
94
|
+
if let error = error {
|
|
95
|
+
reject("SCHEDULE_ERROR", error.localizedDescription, error)
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Save alarm to UserDefaults
|
|
100
|
+
self.saveAlarm(id: id, datetimeISO: datetimeISO, title: title, body: body)
|
|
101
|
+
resolve(nil)
|
|
102
|
+
}
|
|
27
103
|
}
|
|
28
104
|
|
|
29
|
-
@objc
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
105
|
+
@objc(cancelAlarm:resolver:rejecter:)
|
|
106
|
+
func cancelAlarm(_ id: String,
|
|
107
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
108
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
109
|
+
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [id])
|
|
110
|
+
removeAlarm(id: id)
|
|
111
|
+
resolve(nil)
|
|
33
112
|
}
|
|
34
113
|
|
|
35
|
-
@objc
|
|
36
|
-
|
|
37
|
-
|
|
114
|
+
@objc(listAlarms:rejecter:)
|
|
115
|
+
func listAlarms(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
116
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
117
|
+
let alarms = getAlarms()
|
|
118
|
+
resolve(alarms)
|
|
38
119
|
}
|
|
39
120
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
121
|
+
// MARK: - Snooze Methods
|
|
122
|
+
|
|
123
|
+
@objc(snoozeAlarm:minutes:resolver:rejecter:)
|
|
124
|
+
func snoozeAlarm(_ id: String,
|
|
125
|
+
minutes: Int,
|
|
126
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
127
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
128
|
+
// Create a new alarm in X minutes
|
|
129
|
+
let content = UNMutableNotificationContent()
|
|
130
|
+
content.title = "Snoozed Alarm"
|
|
131
|
+
content.body = id
|
|
132
|
+
content.sound = UNNotificationSound.default
|
|
133
|
+
content.categoryIdentifier = "ALARM_CATEGORY"
|
|
134
|
+
|
|
135
|
+
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: TimeInterval(minutes * 60), repeats: false)
|
|
136
|
+
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
|
|
137
|
+
|
|
138
|
+
UNUserNotificationCenter.current().add(request) { error in
|
|
139
|
+
if let error = error {
|
|
140
|
+
reject("SNOOZE_ERROR", error.localizedDescription, error)
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
resolve(nil)
|
|
144
|
+
}
|
|
43
145
|
}
|
|
44
146
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
147
|
+
// MARK: - Current Alarm Methods
|
|
148
|
+
|
|
149
|
+
@objc(stopCurrentAlarm:resolver:rejecter:)
|
|
150
|
+
func stopCurrentAlarm(_ id: String,
|
|
151
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
152
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
153
|
+
// iOS doesn't have persistent alarm sounds like Android
|
|
154
|
+
// Remove the delivered notification
|
|
155
|
+
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [id])
|
|
156
|
+
resolve(nil)
|
|
48
157
|
}
|
|
49
158
|
|
|
50
|
-
@objc
|
|
51
|
-
|
|
52
|
-
|
|
159
|
+
@objc(snoozeCurrentAlarm:minutes:resolver:rejecter:)
|
|
160
|
+
func snoozeCurrentAlarm(_ id: String,
|
|
161
|
+
minutes: Int,
|
|
162
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
163
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
164
|
+
// Remove current notification
|
|
165
|
+
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [id])
|
|
166
|
+
|
|
167
|
+
// Get alarm details
|
|
168
|
+
if let alarm = getAlarmById(id: id) {
|
|
169
|
+
let title = alarm["title"] as? String ?? "Alarm"
|
|
170
|
+
let body = (alarm["body"] as? String ?? "") + " (Snoozed)"
|
|
171
|
+
|
|
172
|
+
let content = UNMutableNotificationContent()
|
|
173
|
+
content.title = title
|
|
174
|
+
content.body = body
|
|
175
|
+
content.sound = UNNotificationSound.default
|
|
176
|
+
content.categoryIdentifier = "ALARM_CATEGORY"
|
|
177
|
+
content.userInfo = ["alarmId": id]
|
|
178
|
+
|
|
179
|
+
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: TimeInterval(minutes * 60), repeats: false)
|
|
180
|
+
let request = UNNotificationRequest(identifier: id, content: content, trigger: trigger)
|
|
181
|
+
|
|
182
|
+
UNUserNotificationCenter.current().add(request) { error in
|
|
183
|
+
if let error = error {
|
|
184
|
+
reject("SNOOZE_ERROR", error.localizedDescription, error)
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
resolve(nil)
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
// Alarm not found, just snooze with generic content
|
|
191
|
+
snoozeAlarm(id, minutes: minutes, resolver: resolve, rejecter: reject)
|
|
192
|
+
}
|
|
53
193
|
}
|
|
54
194
|
|
|
55
|
-
@objc
|
|
56
|
-
|
|
195
|
+
@objc(getCurrentAlarmPlaying:rejecter:)
|
|
196
|
+
func getCurrentAlarmPlaying(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
197
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
198
|
+
// iOS doesn't have a concept of "currently playing" alarm like Android
|
|
199
|
+
// Return nil as there's no persistent alarm sound
|
|
57
200
|
resolve(nil)
|
|
58
201
|
}
|
|
59
202
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
203
|
+
// MARK: - Storage Helpers
|
|
204
|
+
|
|
205
|
+
private func saveAlarm(id: String, datetimeISO: String, title: String, body: String) {
|
|
206
|
+
var alarms = getAlarmsDict()
|
|
207
|
+
alarms[id] = [
|
|
208
|
+
"id": id,
|
|
209
|
+
"datetimeISO": datetimeISO,
|
|
210
|
+
"title": title,
|
|
211
|
+
"body": body
|
|
212
|
+
]
|
|
213
|
+
UserDefaults.standard.set(alarms, forKey: AlarmModule.PREFS_KEY)
|
|
63
214
|
}
|
|
64
215
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
216
|
+
private func removeAlarm(id: String) {
|
|
217
|
+
var alarms = getAlarmsDict()
|
|
218
|
+
alarms.removeValue(forKey: id)
|
|
219
|
+
UserDefaults.standard.set(alarms, forKey: AlarmModule.PREFS_KEY)
|
|
68
220
|
}
|
|
69
221
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
reject("NOT_IMPLEMENTED", "Alarm snooze is not yet implemented for iOS.", nil)
|
|
222
|
+
private func getAlarmsDict() -> [String: [String: String]] {
|
|
223
|
+
return UserDefaults.standard.dictionary(forKey: AlarmModule.PREFS_KEY) as? [String: [String: String]] ?? [:]
|
|
73
224
|
}
|
|
74
225
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
226
|
+
private func getAlarms() -> [[String: String]] {
|
|
227
|
+
return Array(getAlarmsDict().values)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private func getAlarmById(id: String) -> [String: String]? {
|
|
231
|
+
return getAlarmsDict()[id]
|
|
78
232
|
}
|
|
79
233
|
}
|