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.
Files changed (148) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +169 -189
  3. package/android/build/.transforms/33d69af0aa8d226a34981fd71aab63e2/results.bin +1 -0
  4. package/android/build/.transforms/33d69af0aa8d226a34981fd71aab63e2/transformed/classes/classes_dex/classes.dex +0 -0
  5. package/android/build/generated/source/buildConfig/debug/com/rnalarmmodule/BuildConfig.java +10 -0
  6. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +41 -0
  7. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +18 -0
  8. package/android/build/intermediates/aar_metadata/debug/writeDebugAarMetadata/aar-metadata.properties +6 -0
  9. package/android/build/intermediates/annotation_processor_list/debug/javaPreCompileDebug/annotationProcessors.json +1 -0
  10. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  11. package/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
  12. package/android/build/intermediates/compile_symbol_list/debug/generateDebugRFile/R.txt +1 -0
  13. package/android/build/intermediates/compiled_local_resources/debug/compileDebugLibraryResources/out/raw_alarm_default.wav.flat +0 -0
  14. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +2 -0
  15. package/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +2 -0
  16. package/android/build/intermediates/incremental/mergeDebugAssets/merger.xml +2 -0
  17. package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +2 -0
  18. package/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +2 -0
  19. package/android/build/intermediates/java_res/debug/processDebugJavaRes/out/META-INF/react-native-alarmageddon_debug.kotlin_module +0 -0
  20. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/rnalarmmodule/BuildConfig.class +0 -0
  21. package/android/build/intermediates/local_only_symbol_list/debug/parseDebugLocalResources/R-def.txt +3 -0
  22. package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +68 -0
  23. package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +41 -0
  24. package/android/build/intermediates/navigation_json/debug/extractDeepLinksDebug/navigation.json +1 -0
  25. package/android/build/intermediates/nested_resources_validation_report/debug/generateDebugResources/nestedResourcesValidationReport.txt +1 -0
  26. package/android/build/intermediates/packaged_res/debug/packageDebugResources/raw/alarm_default.wav +0 -0
  27. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  28. package/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +2 -0
  29. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
  30. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream +0 -0
  31. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream.len +0 -0
  32. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.len +0 -0
  33. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
  34. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i +0 -0
  35. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i.len +0 -0
  36. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
  37. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
  38. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
  39. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.len +0 -0
  40. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
  41. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
  42. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i.len +0 -0
  43. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
  44. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
  45. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
  46. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len +0 -0
  47. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
  48. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
  49. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i.len +0 -0
  50. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab +0 -0
  51. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream +0 -0
  52. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream.len +0 -0
  53. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.len +0 -0
  54. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.values.at +0 -0
  55. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab_i +0 -0
  56. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab_i.len +0 -0
  57. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
  58. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
  59. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
  60. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
  61. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
  62. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
  63. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i.len +0 -0
  64. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
  65. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
  66. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
  67. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.len +0 -0
  68. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
  69. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
  70. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i.len +0 -0
  71. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
  72. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream +0 -0
  73. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len +0 -0
  74. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.len +0 -0
  75. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
  76. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i +0 -0
  77. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i.len +0 -0
  78. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
  79. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream +0 -0
  80. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream.len +0 -0
  81. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.len +0 -0
  82. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
  83. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab_i +0 -0
  84. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab_i.len +0 -0
  85. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
  86. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream +0 -0
  87. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream.len +0 -0
  88. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.len +0 -0
  89. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.values.at +0 -0
  90. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i +0 -0
  91. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i.len +0 -0
  92. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/counters.tab +2 -0
  93. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab +0 -0
  94. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream +0 -0
  95. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream.len +0 -0
  96. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.len +0 -0
  97. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at +0 -0
  98. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i +0 -0
  99. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i.len +0 -0
  100. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab +0 -0
  101. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream +0 -0
  102. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len +0 -0
  103. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len +0 -0
  104. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at +0 -0
  105. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i +0 -0
  106. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i.len +0 -0
  107. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
  108. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
  109. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
  110. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
  111. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
  112. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
  113. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i.len +0 -0
  114. package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
  115. package/android/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin +0 -0
  116. package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
  117. package/android/build/outputs/logs/manifest-merger-debug-report.txt +71 -0
  118. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  119. package/android/build/tmp/kotlin-classes/debug/META-INF/react-native-alarmageddon_debug.kotlin_module +0 -0
  120. package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/AlarmModule$Companion.class +0 -0
  121. package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/AlarmModule.class +0 -0
  122. package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/AlarmPackage.class +0 -0
  123. package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/AlarmReceiver$Companion.class +0 -0
  124. package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/AlarmReceiver.class +0 -0
  125. package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/BootReceiver$Companion.class +0 -0
  126. package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/BootReceiver.class +0 -0
  127. package/android/build.gradle +11 -21
  128. package/android/src/main/AndroidManifest.xml +16 -35
  129. package/android/src/main/java/com/rnalarmmodule/AlarmModule.kt +154 -390
  130. package/android/src/main/java/com/rnalarmmodule/AlarmPackage.kt +0 -1
  131. package/android/src/main/java/com/rnalarmmodule/AlarmReceiver.kt +290 -129
  132. package/android/src/main/java/com/rnalarmmodule/BootReceiver.kt +66 -124
  133. package/android/src/main/res/raw/alarm_default.wav +0 -0
  134. package/ios/{RNAlarmModule.m → AlarmModule.m} +9 -20
  135. package/ios/AlarmModule.swift +190 -36
  136. package/ios/RNAlarmModule-Bridging-Header.h +5 -1
  137. package/lib/index.d.ts +47 -85
  138. package/lib/index.d.ts.map +1 -1
  139. package/lib/index.js +60 -120
  140. package/lib/index.js.map +1 -1
  141. package/package.json +14 -23
  142. package/react-native-alarmageddon.podspec +10 -8
  143. package/react-native.config.js +3 -1
  144. package/src/index.ts +94 -201
  145. package/android/gradle.properties +0 -13
  146. package/android/src/main/java/com/rnalarmmodule/AlarmActivity.kt +0 -79
  147. package/android/src/main/java/com/rnalarmmodule/AlarmService.kt +0 -290
  148. package/android/src/main/res/raw/README.md +0 -36
@@ -1,485 +1,249 @@
1
1
  package com.rnalarmmodule
2
2
 
3
+ import com.facebook.react.bridge.*
3
4
  import android.app.AlarmManager
4
5
  import android.app.PendingIntent
5
6
  import android.content.Context
6
7
  import android.content.Intent
7
- import android.content.SharedPreferences
8
8
  import android.os.Build
9
- import android.provider.Settings
9
+ import java.text.SimpleDateFormat
10
+ import java.util.*
10
11
  import android.util.Log
11
- import com.facebook.react.bridge.*
12
- import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
13
- import org.json.JSONArray
14
12
  import org.json.JSONObject
13
+ import android.content.pm.PackageManager
15
14
  import java.lang.ref.WeakReference
16
- import java.text.SimpleDateFormat
17
- import java.util.*
18
15
 
19
16
  class AlarmModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
20
17
 
21
18
  companion object {
22
19
  private const val TAG = "AlarmModule"
23
- private const val PREFS_NAME = "rn_alarm_module_alarms"
24
- private const val ALARMS_KEY = "alarms"
20
+ private const val PREFS = "rn_alarm_module_alarms"
25
21
  private const val DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss"
26
- private const val ACTIVE_ALARM_KEY = "active_alarm_id"
27
22
 
28
- // Weak reference to avoid memory leaks
23
+ // Weak reference to avoid memory leaks - used by AlarmReceiver to emit events
29
24
  private var reactContextRef: WeakReference<ReactApplicationContext>? = null
30
25
 
31
26
  fun getReactContext(): ReactApplicationContext? = reactContextRef?.get()
32
-
33
- fun emitActiveAlarmId(id: String?) {
34
- val reactContext = getReactContext()
35
- if (reactContext != null && reactContext.hasActiveCatalystInstance()) {
36
- try {
37
- reactContext
38
- .getJSModule(RCTDeviceEventEmitter::class.java)
39
- .emit("activeAlarmId", id)
40
- } catch (e: Exception) {
41
- Log.e(TAG, "Failed to emit activeAlarmId event: ${e.message}")
42
- }
43
- }
44
- }
45
27
  }
46
28
 
47
- private val prefs: SharedPreferences = reactContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
48
- private val dateFormat = SimpleDateFormat(DATE_PATTERN, Locale.getDefault())
49
-
50
29
  init {
30
+ // Update the reference whenever the module is instantiated
51
31
  reactContextRef = WeakReference(reactContext)
52
- dateFormat.timeZone = TimeZone.getDefault()
53
32
  }
54
33
 
55
34
  override fun getName(): String = "AlarmModule"
56
35
 
57
- override fun getConstants(): Map<String, Any> {
58
- return mapOf(
59
- "EXACT_ALARM_PERMISSION_REQUIRED" to (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
60
- )
61
- }
62
-
63
- @ReactMethod
64
- fun addListener(eventName: String) {
65
- // Required for NativeEventEmitter
66
- }
67
-
68
- @ReactMethod
69
- fun removeListeners(count: Int) {
70
- // Required for NativeEventEmitter
71
- }
72
-
73
36
  @ReactMethod
74
- fun scheduleAlarm(alarmParams: ReadableMap, promise: Promise) {
75
- try {
76
- val id = alarmParams.getString("id") ?: run {
77
- promise.reject("INVALID_PARAMS", "Alarm ID is required")
78
- return
79
- }
80
- val datetimeISO = alarmParams.getString("datetimeISO") ?: run {
81
- promise.reject("INVALID_PARAMS", "datetimeISO is required")
82
- return
83
- }
84
- val title = alarmParams.getString("title") ?: "Alarm"
85
- val body = alarmParams.getString("body") ?: ""
86
- val soundUri = if (alarmParams.hasKey("soundUri")) alarmParams.getString("soundUri") else null
87
- val vibrate = if (alarmParams.hasKey("vibrate")) alarmParams.getBoolean("vibrate") else true
88
- val snoozeMinutes = if (alarmParams.hasKey("snoozeMinutes")) alarmParams.getInt("snoozeMinutes") else 5
89
- val autoStopSeconds = if (alarmParams.hasKey("autoStopSeconds")) alarmParams.getInt("autoStopSeconds") else 60
90
-
91
- val triggerTime = parseDateTime(datetimeISO)
92
- if (triggerTime == null) {
93
- promise.reject("INVALID_DATE", "Could not parse datetimeISO: $datetimeISO")
94
- return
95
- }
96
-
97
- // Store alarm in SharedPreferences
98
- val alarm = JSONObject().apply {
99
- put("id", id)
100
- put("datetimeISO", datetimeISO)
101
- put("triggerTime", triggerTime)
102
- put("title", title)
103
- put("body", body)
104
- put("soundUri", soundUri ?: "")
105
- put("vibrate", vibrate)
106
- put("snoozeMinutes", snoozeMinutes)
107
- put("autoStopSeconds", autoStopSeconds)
108
- }
109
- saveAlarm(alarm)
110
-
111
- // Schedule the alarm
112
- scheduleExactAlarm(id, triggerTime, title, body, soundUri, vibrate, snoozeMinutes, autoStopSeconds)
113
-
114
- Log.d(TAG, "Alarm scheduled: id=$id, triggerTime=$triggerTime")
37
+ fun getCurrentAlarmPlaying(promise: Promise) {
38
+ if (AlarmReceiver.activeAlarmId != null) {
39
+ val map = Arguments.createMap()
40
+ map.putString("activeAlarmId", AlarmReceiver.activeAlarmId)
41
+ promise.resolve(map)
42
+ } else {
115
43
  promise.resolve(null)
116
- } catch (e: Exception) {
117
- Log.e(TAG, "Failed to schedule alarm: ${e.message}", e)
118
- promise.reject("SCHEDULE_ERROR", "Failed to schedule alarm: ${e.message}", e)
119
44
  }
120
45
  }
121
46
 
122
47
  @ReactMethod
123
- fun cancelAlarm(id: String, promise: Promise) {
48
+ fun stopCurrentAlarm(id: String, promise: Promise) {
124
49
  try {
125
- cancelScheduledAlarm(id)
126
- removeAlarm(id)
127
- Log.d(TAG, "Alarm cancelled: id=$id")
50
+ val intent = Intent(reactApplicationContext, AlarmReceiver::class.java).apply {
51
+ action = AlarmReceiver.ACTION_STOP
52
+ putExtra("id", id)
53
+ }
54
+ reactApplicationContext.sendBroadcast(intent)
128
55
  promise.resolve(null)
129
56
  } catch (e: Exception) {
130
- Log.e(TAG, "Failed to cancel alarm: ${e.message}", e)
131
- promise.reject("CANCEL_ERROR", "Failed to cancel alarm: ${e.message}", e)
57
+ Log.e(TAG, "Failed to stop alarm: ${e.message}")
58
+ promise.reject("STOP_ERROR", e)
132
59
  }
133
60
  }
134
61
 
135
62
  @ReactMethod
136
- fun listAlarms(promise: Promise) {
63
+ fun snoozeCurrentAlarm(id: String, minutes: Int, promise: Promise) {
137
64
  try {
138
- val alarms = getStoredAlarms()
139
- val result = Arguments.createArray()
140
- for (i in 0 until alarms.length()) {
141
- val alarm = alarms.getJSONObject(i)
142
- val map = Arguments.createMap().apply {
143
- putString("id", alarm.getString("id"))
144
- putString("datetimeISO", alarm.getString("datetimeISO"))
145
- putString("title", alarm.optString("title", "Alarm"))
146
- putString("body", alarm.optString("body", ""))
147
- putBoolean("vibrate", alarm.optBoolean("vibrate", true))
148
- putInt("snoozeMinutes", alarm.optInt("snoozeMinutes", 5))
149
- putInt("autoStopSeconds", alarm.optInt("autoStopSeconds", 60))
150
- }
151
- result.pushMap(map)
152
- }
153
- promise.resolve(result)
65
+ val alarm = getAlarm(id) ?: throw Exception("Alarm not found")
66
+ val title = alarm.optString("title", "Alarm")
67
+ val body = alarm.optString("body", "")
68
+
69
+ val intent = Intent(reactApplicationContext, AlarmReceiver::class.java).apply {
70
+ action = AlarmReceiver.ACTION_SNOOZE
71
+ putExtra("id", id)
72
+ putExtra("title", title)
73
+ putExtra("body", "$body (Snoozed)")
74
+ putExtra("snoozeMinutes", minutes)
75
+ }
76
+ reactApplicationContext.sendBroadcast(intent)
77
+ promise.resolve(null)
154
78
  } catch (e: Exception) {
155
- Log.e(TAG, "Failed to list alarms: ${e.message}", e)
156
- promise.reject("LIST_ERROR", "Failed to list alarms: ${e.message}", e)
79
+ Log.e(TAG, "Failed to snooze alarm: ${e.message}")
80
+ promise.reject("SNOOZE_ERROR", e)
157
81
  }
158
82
  }
159
83
 
160
84
  @ReactMethod
161
85
  fun requestPermissions(promise: Promise) {
162
- try {
163
- val result = Arguments.createMap()
164
-
165
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
166
- val alarmManager = reactApplicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager
167
- val canScheduleExact = alarmManager.canScheduleExactAlarms()
168
- result.putBoolean("granted", canScheduleExact)
169
- result.putBoolean("exactAlarmGranted", canScheduleExact)
170
-
171
- if (!canScheduleExact) {
172
- // Open settings to grant exact alarm permission
173
- try {
174
- val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM).apply {
175
- flags = Intent.FLAG_ACTIVITY_NEW_TASK
176
- }
177
- reactApplicationContext.startActivity(intent)
178
- } catch (e: Exception) {
179
- Log.e(TAG, "Failed to open exact alarm settings: ${e.message}")
180
- }
181
- }
182
- } else {
183
- result.putBoolean("granted", true)
184
- result.putBoolean("exactAlarmGranted", true)
185
- }
186
-
187
- promise.resolve(result)
188
- } catch (e: Exception) {
189
- Log.e(TAG, "Failed to request permissions: ${e.message}", e)
190
- promise.reject("PERMISSION_ERROR", "Failed to request permissions: ${e.message}", e)
191
- }
86
+ val granted = if (Build.VERSION.SDK_INT >= 33) {
87
+ reactApplicationContext.checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED
88
+ } else true
89
+ val map = Arguments.createMap()
90
+ map.putBoolean("granted", granted)
91
+ promise.resolve(map)
192
92
  }
193
93
 
194
94
  @ReactMethod
195
- fun checkExactAlarmPermission(promise: Promise) {
95
+ fun scheduleAlarm(alarm: ReadableMap, promise: Promise) {
196
96
  try {
197
- val result = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
198
- val alarmManager = reactApplicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager
199
- alarmManager.canScheduleExactAlarms()
200
- } else {
201
- true
202
- }
203
- promise.resolve(result)
204
- } catch (e: Exception) {
205
- Log.e(TAG, "Failed to check exact alarm permission: ${e.message}", e)
206
- promise.reject("PERMISSION_ERROR", "Failed to check permission: ${e.message}", e)
207
- }
208
- }
97
+ val id = alarm.getString("id") ?: UUID.randomUUID().toString()
98
+ val datetimeISO = alarm.getString("datetimeISO") ?: throw Exception("datetimeISO required")
99
+ val title = alarm.getString("title") ?: "Alarm"
100
+ val body = alarm.getString("body") ?: ""
209
101
 
210
- @ReactMethod
211
- fun openExactAlarmSettings(promise: Promise) {
212
- try {
213
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
214
- val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM).apply {
215
- flags = Intent.FLAG_ACTIVITY_NEW_TASK
216
- }
217
- reactApplicationContext.startActivity(intent)
218
- }
219
- promise.resolve(null)
220
- } catch (e: Exception) {
221
- Log.e(TAG, "Failed to open exact alarm settings: ${e.message}", e)
222
- promise.reject("SETTINGS_ERROR", "Failed to open settings: ${e.message}", e)
223
- }
224
- }
102
+ val sdf = SimpleDateFormat(DATE_PATTERN, Locale.getDefault())
103
+ val date = sdf.parse(datetimeISO) ?: throw Exception("Invalid date format")
104
+ val triggerAt = date.time
225
105
 
226
- @ReactMethod
227
- fun snoozeAlarm(id: String, minutes: Int, promise: Promise) {
228
- try {
229
- val alarm = getAlarmById(id)
230
- if (alarm == null) {
231
- promise.reject("NOT_FOUND", "Alarm not found: $id")
232
- return
106
+ val now = System.currentTimeMillis()
107
+ if (triggerAt < now) {
108
+ Log.w(TAG, "scheduleAlarm: trigger time in past ($datetimeISO); scheduling anyway.")
233
109
  }
234
110
 
235
- val snoozeTime = System.currentTimeMillis() + (minutes * 60 * 1000L)
236
- val newDatetimeISO = dateFormat.format(Date(snoozeTime))
237
-
238
- // Update alarm with new time
239
- alarm.put("datetimeISO", newDatetimeISO)
240
- alarm.put("triggerTime", snoozeTime)
241
- updateAlarm(alarm)
111
+ val alarmManager = reactApplicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager
112
+ val alarmIntent = Intent(reactApplicationContext, AlarmReceiver::class.java).apply {
113
+ putExtra("id", id)
114
+ putExtra("title", title)
115
+ putExtra("body", body)
116
+ }
242
117
 
243
- // Reschedule
244
- scheduleExactAlarm(
245
- id,
246
- snoozeTime,
247
- alarm.optString("title", "Alarm"),
248
- alarm.optString("body", ""),
249
- alarm.optString("soundUri", null),
250
- alarm.optBoolean("vibrate", true),
251
- alarm.optInt("snoozeMinutes", 5),
252
- alarm.optInt("autoStopSeconds", 60)
118
+ val pendingIntent = PendingIntent.getBroadcast(
119
+ reactApplicationContext,
120
+ id.hashCode(),
121
+ alarmIntent,
122
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
253
123
  )
254
124
 
255
- Log.d(TAG, "Alarm snoozed: id=$id, new trigger=$snoozeTime")
125
+ // Use exact alarm scheduling
126
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
127
+ alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAt, pendingIntent)
128
+ } else {
129
+ alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerAt, pendingIntent)
130
+ }
131
+
132
+ saveAlarm(id, datetimeISO, title, body)
133
+ Log.d(TAG, "Scheduled alarm id=$id at=$datetimeISO")
256
134
  promise.resolve(null)
257
135
  } catch (e: Exception) {
258
- Log.e(TAG, "Failed to snooze alarm: ${e.message}", e)
259
- promise.reject("SNOOZE_ERROR", "Failed to snooze alarm: ${e.message}", e)
136
+ Log.e(TAG, "Failed to schedule alarm: ${e.message}")
137
+ promise.reject("SCHEDULE_ERROR", e)
260
138
  }
261
139
  }
262
140
 
263
141
  @ReactMethod
264
- fun stopCurrentAlarm(id: String, promise: Promise) {
142
+ fun cancelAlarm(id: String, promise: Promise) {
265
143
  try {
266
- // Stop the alarm service
267
- val stopIntent = Intent(reactApplicationContext, AlarmService::class.java).apply {
268
- action = AlarmService.ACTION_STOP
269
- putExtra("alarmId", id)
270
- }
271
- reactApplicationContext.stopService(stopIntent)
272
-
273
- // Clear active alarm state
274
- prefs.edit().remove(ACTIVE_ALARM_KEY).apply()
275
- emitActiveAlarmId(null)
276
-
277
- Log.d(TAG, "Alarm stopped: id=$id")
144
+ val intent = Intent(reactApplicationContext, AlarmReceiver::class.java)
145
+ val pi = PendingIntent.getBroadcast(
146
+ reactApplicationContext,
147
+ id.hashCode(),
148
+ intent,
149
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
150
+ )
151
+ val am = reactApplicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager
152
+ am.cancel(pi)
153
+ removeAlarm(id)
154
+ Log.d(TAG, "Cancelled alarm id=$id")
278
155
  promise.resolve(null)
279
156
  } catch (e: Exception) {
280
- Log.e(TAG, "Failed to stop alarm: ${e.message}", e)
281
- promise.reject("STOP_ERROR", "Failed to stop alarm: ${e.message}", e)
157
+ Log.e(TAG, "Failed to cancel alarm: ${e.message}")
158
+ promise.reject("CANCEL_ERROR", e)
282
159
  }
283
160
  }
284
161
 
285
162
  @ReactMethod
286
- fun snoozeCurrentAlarm(id: String, minutes: Int, promise: Promise) {
287
- try {
288
- // Stop the current alarm
289
- val stopIntent = Intent(reactApplicationContext, AlarmService::class.java).apply {
290
- action = AlarmService.ACTION_STOP
291
- putExtra("alarmId", id)
292
- }
293
- reactApplicationContext.stopService(stopIntent)
294
-
295
- // Clear active alarm state
296
- prefs.edit().remove(ACTIVE_ALARM_KEY).apply()
297
- emitActiveAlarmId(null)
298
-
299
- // Schedule snooze
300
- snoozeAlarm(id, minutes, promise)
301
- } catch (e: Exception) {
302
- Log.e(TAG, "Failed to snooze current alarm: ${e.message}", e)
303
- promise.reject("SNOOZE_ERROR", "Failed to snooze current alarm: ${e.message}", e)
304
- }
163
+ fun listAlarms(promise: Promise) {
164
+ val arr = Arguments.createArray()
165
+ val prefs = reactApplicationContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
166
+ val all = prefs.all
167
+ for ((id, rawJson) in all) {
168
+ val jsonStr = rawJson as? String ?: continue
169
+ val obj = try {
170
+ JSONObject(jsonStr)
171
+ } catch (_: Exception) {
172
+ continue
173
+ }
174
+ val map = Arguments.createMap()
175
+ map.putString("id", id)
176
+ map.putString("datetimeISO", obj.optString("datetimeISO"))
177
+ map.putString("title", obj.optString("title"))
178
+ map.putString("body", obj.optString("body"))
179
+ arr.pushMap(map)
180
+ }
181
+ promise.resolve(arr)
305
182
  }
306
183
 
307
184
  @ReactMethod
308
- fun getCurrentAlarmPlaying(promise: Promise) {
185
+ fun snoozeAlarm(message: String, snoozeMinutes: Int, promise: Promise) {
309
186
  try {
310
- val activeAlarmId = prefs.getString(ACTIVE_ALARM_KEY, null)
311
- if (activeAlarmId != null) {
312
- val result = Arguments.createMap().apply {
313
- putString("activeAlarmId", activeAlarmId)
314
- }
315
- promise.resolve(result)
316
- } else {
317
- promise.resolve(null)
318
- }
319
- } catch (e: Exception) {
320
- Log.e(TAG, "Failed to get current alarm: ${e.message}", e)
321
- promise.reject("GET_ERROR", "Failed to get current alarm: ${e.message}", e)
322
- }
323
- }
324
-
325
- private fun parseDateTime(datetimeISO: String): Long? {
326
- return try {
327
- // Try parsing with timezone info
328
- val formats = listOf(
329
- SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.getDefault()),
330
- SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX", Locale.getDefault()),
331
- SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()),
332
- SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault()),
333
- SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.getDefault()),
334
- SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()),
335
- SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
187
+ val id = UUID.randomUUID().toString()
188
+ val alarmIntent = Intent(reactApplicationContext, AlarmReceiver::class.java).apply {
189
+ putExtra("id", id)
190
+ putExtra("title", "Snoozed Alarm")
191
+ putExtra("body", message)
192
+ }
193
+ val pendingIntent = PendingIntent.getBroadcast(
194
+ reactApplicationContext,
195
+ id.hashCode(),
196
+ alarmIntent,
197
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
336
198
  )
337
-
338
- for (format in formats) {
339
- try {
340
- val date = format.parse(datetimeISO)
341
- if (date != null) {
342
- return date.time
343
- }
344
- } catch (e: Exception) {
345
- // Try next format
346
- }
347
- }
348
- null
349
- } catch (e: Exception) {
350
- Log.e(TAG, "Failed to parse date: $datetimeISO", e)
351
- null
352
- }
353
- }
354
-
355
- private fun scheduleExactAlarm(
356
- id: String,
357
- triggerTimeMillis: Long,
358
- title: String,
359
- body: String,
360
- soundUri: String?,
361
- vibrate: Boolean,
362
- snoozeMinutes: Int,
363
- autoStopSeconds: Int
364
- ) {
365
- val alarmManager = reactApplicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager
366
-
367
- val intent = Intent(reactApplicationContext, AlarmReceiver::class.java).apply {
368
- action = "com.rnalarmmodule.ALARM_TRIGGER"
369
- putExtra("alarmId", id)
370
- putExtra("title", title)
371
- putExtra("body", body)
372
- putExtra("soundUri", soundUri ?: "")
373
- putExtra("vibrate", vibrate)
374
- putExtra("snoozeMinutes", snoozeMinutes)
375
- putExtra("autoStopSeconds", autoStopSeconds)
376
- }
377
-
378
- val pendingIntent = PendingIntent.getBroadcast(
379
- reactApplicationContext,
380
- id.hashCode(),
381
- intent,
382
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
383
- )
384
199
 
385
- try {
386
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
387
- if (alarmManager.canScheduleExactAlarms()) {
388
- alarmManager.setAlarmClock(
389
- AlarmManager.AlarmClockInfo(triggerTimeMillis, pendingIntent),
390
- pendingIntent
391
- )
392
- } else {
393
- // Fallback to inexact alarm if exact permission not granted
394
- alarmManager.setAndAllowWhileIdle(
395
- AlarmManager.RTC_WAKEUP,
396
- triggerTimeMillis,
397
- pendingIntent
398
- )
399
- Log.w(TAG, "Exact alarm permission not granted, using inexact alarm")
400
- }
401
- } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
402
- alarmManager.setAlarmClock(
403
- AlarmManager.AlarmClockInfo(triggerTimeMillis, pendingIntent),
404
- pendingIntent
405
- )
200
+ val alarmManager = reactApplicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager
201
+ val triggerAt = System.currentTimeMillis() + snoozeMinutes * 60_000L
202
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
203
+ alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAt, pendingIntent)
406
204
  } else {
407
- alarmManager.setExact(
408
- AlarmManager.RTC_WAKEUP,
409
- triggerTimeMillis,
410
- pendingIntent
411
- )
205
+ alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerAt, pendingIntent)
412
206
  }
413
- } catch (e: SecurityException) {
414
- Log.e(TAG, "Security exception scheduling alarm: ${e.message}", e)
415
- throw e
207
+ Log.d(TAG, "Snoozed alarm for $snoozeMinutes minutes")
208
+ promise.resolve(null)
209
+ } catch (e: Exception) {
210
+ Log.e(TAG, "Failed to snooze alarm: ${e.message}")
211
+ promise.reject("SNOOZE_ERROR", e)
416
212
  }
417
213
  }
418
214
 
419
- private fun cancelScheduledAlarm(id: String) {
420
- val alarmManager = reactApplicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager
421
-
422
- val intent = Intent(reactApplicationContext, AlarmReceiver::class.java).apply {
423
- action = "com.rnalarmmodule.ALARM_TRIGGER"
424
- }
425
-
426
- val pendingIntent = PendingIntent.getBroadcast(
427
- reactApplicationContext,
428
- id.hashCode(),
429
- intent,
430
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
431
- )
432
-
433
- alarmManager.cancel(pendingIntent)
434
- pendingIntent.cancel()
435
- }
436
-
437
- private fun getStoredAlarms(): JSONArray {
438
- val alarmsJson = prefs.getString(ALARMS_KEY, "[]") ?: "[]"
439
- return JSONArray(alarmsJson)
215
+ @ReactMethod
216
+ fun addListener(eventName: String) {
217
+ // Required for NativeEventEmitter
440
218
  }
441
219
 
442
- private fun saveAlarm(alarm: JSONObject) {
443
- val alarms = getStoredAlarms()
444
- val id = alarm.getString("id")
445
-
446
- // Remove existing alarm with same ID
447
- val filteredAlarms = JSONArray()
448
- for (i in 0 until alarms.length()) {
449
- val existingAlarm = alarms.getJSONObject(i)
450
- if (existingAlarm.getString("id") != id) {
451
- filteredAlarms.put(existingAlarm)
452
- }
453
- }
454
-
455
- filteredAlarms.put(alarm)
456
- prefs.edit().putString(ALARMS_KEY, filteredAlarms.toString()).apply()
220
+ @ReactMethod
221
+ fun removeListeners(count: Int) {
222
+ // Required for NativeEventEmitter
457
223
  }
458
224
 
459
- private fun updateAlarm(alarm: JSONObject) {
460
- saveAlarm(alarm)
225
+ private fun saveAlarm(id: String, datetimeISO: String, title: String, body: String) {
226
+ val obj = JSONObject()
227
+ .put("id", id)
228
+ .put("datetimeISO", datetimeISO)
229
+ .put("title", title)
230
+ .put("body", body)
231
+ val prefs = reactApplicationContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
232
+ prefs.edit().putString(id, obj.toString()).apply()
461
233
  }
462
234
 
463
235
  private fun removeAlarm(id: String) {
464
- val alarms = getStoredAlarms()
465
- val filteredAlarms = JSONArray()
466
- for (i in 0 until alarms.length()) {
467
- val existingAlarm = alarms.getJSONObject(i)
468
- if (existingAlarm.getString("id") != id) {
469
- filteredAlarms.put(existingAlarm)
470
- }
471
- }
472
- prefs.edit().putString(ALARMS_KEY, filteredAlarms.toString()).apply()
236
+ val prefs = reactApplicationContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
237
+ prefs.edit().remove(id).apply()
473
238
  }
474
239
 
475
- private fun getAlarmById(id: String): JSONObject? {
476
- val alarms = getStoredAlarms()
477
- for (i in 0 until alarms.length()) {
478
- val alarm = alarms.getJSONObject(i)
479
- if (alarm.getString("id") == id) {
480
- return alarm
481
- }
240
+ private fun getAlarm(id: String): JSONObject? {
241
+ val prefs = reactApplicationContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
242
+ val jsonStr = prefs.getString(id, null) ?: return null
243
+ return try {
244
+ JSONObject(jsonStr)
245
+ } catch (_: Exception) {
246
+ null
482
247
  }
483
- return null
484
248
  }
485
249
  }
@@ -6,7 +6,6 @@ import com.facebook.react.bridge.ReactApplicationContext
6
6
  import com.facebook.react.uimanager.ViewManager
7
7
 
8
8
  class AlarmPackage : ReactPackage {
9
-
10
9
  override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
11
10
  return listOf(AlarmModule(reactContext))
12
11
  }