react-native-alarmageddon 1.1.1 → 2.1.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 (150) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +169 -187
  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/.transforms/4aeb440f8cdf777de34ab1099140d239/results.bin +1 -0
  6. package/android/build/.transforms/4aeb440f8cdf777de34ab1099140d239/transformed/classes/classes_dex/classes.dex +0 -0
  7. package/android/build/generated/source/buildConfig/debug/com/rnalarmmodule/BuildConfig.java +10 -0
  8. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +41 -0
  9. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +18 -0
  10. package/android/build/intermediates/aar_metadata/debug/writeDebugAarMetadata/aar-metadata.properties +6 -0
  11. package/android/build/intermediates/annotation_processor_list/debug/javaPreCompileDebug/annotationProcessors.json +1 -0
  12. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  13. package/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
  14. package/android/build/intermediates/compile_symbol_list/debug/generateDebugRFile/R.txt +1 -0
  15. package/android/build/intermediates/compiled_local_resources/debug/compileDebugLibraryResources/out/raw_alarm_default.wav.flat +0 -0
  16. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +2 -0
  17. package/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +2 -0
  18. package/android/build/intermediates/incremental/mergeDebugAssets/merger.xml +2 -0
  19. package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +2 -0
  20. package/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +2 -0
  21. package/android/build/intermediates/java_res/debug/processDebugJavaRes/out/META-INF/react-native-alarmageddon_debug.kotlin_module +0 -0
  22. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/rnalarmmodule/BuildConfig.class +0 -0
  23. package/android/build/intermediates/local_only_symbol_list/debug/parseDebugLocalResources/R-def.txt +3 -0
  24. package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +68 -0
  25. package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +41 -0
  26. package/android/build/intermediates/navigation_json/debug/extractDeepLinksDebug/navigation.json +1 -0
  27. package/android/build/intermediates/nested_resources_validation_report/debug/generateDebugResources/nestedResourcesValidationReport.txt +1 -0
  28. package/android/build/intermediates/packaged_res/debug/packageDebugResources/raw/alarm_default.wav +0 -0
  29. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  30. package/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +2 -0
  31. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
  32. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream +0 -0
  33. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream.len +0 -0
  34. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.len +0 -0
  35. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
  36. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i +0 -0
  37. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i.len +0 -0
  38. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
  39. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
  40. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
  41. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.len +0 -0
  42. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
  43. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
  44. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i.len +0 -0
  45. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
  46. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
  47. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
  48. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len +0 -0
  49. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
  50. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
  51. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i.len +0 -0
  52. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab +0 -0
  53. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream +0 -0
  54. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream.len +0 -0
  55. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.len +0 -0
  56. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.values.at +0 -0
  57. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab_i +0 -0
  58. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab_i.len +0 -0
  59. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
  60. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
  61. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
  62. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
  63. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
  64. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
  65. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i.len +0 -0
  66. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
  67. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
  68. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
  69. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.len +0 -0
  70. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
  71. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
  72. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i.len +0 -0
  73. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
  74. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream +0 -0
  75. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len +0 -0
  76. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.len +0 -0
  77. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
  78. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i +0 -0
  79. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i.len +0 -0
  80. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
  81. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream +0 -0
  82. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream.len +0 -0
  83. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.len +0 -0
  84. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
  85. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab_i +0 -0
  86. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab_i.len +0 -0
  87. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
  88. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream +0 -0
  89. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream.len +0 -0
  90. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.len +0 -0
  91. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.values.at +0 -0
  92. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i +0 -0
  93. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i.len +0 -0
  94. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/counters.tab +2 -0
  95. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab +0 -0
  96. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream +0 -0
  97. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream.len +0 -0
  98. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.len +0 -0
  99. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at +0 -0
  100. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i +0 -0
  101. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i.len +0 -0
  102. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab +0 -0
  103. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream +0 -0
  104. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len +0 -0
  105. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len +0 -0
  106. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at +0 -0
  107. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i +0 -0
  108. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i.len +0 -0
  109. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
  110. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
  111. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
  112. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
  113. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
  114. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
  115. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i.len +0 -0
  116. package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
  117. package/android/build/kotlin/compileDebugKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin +0 -0
  118. package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
  119. package/android/build/outputs/logs/manifest-merger-debug-report.txt +71 -0
  120. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  121. package/android/build/tmp/kotlin-classes/debug/META-INF/react-native-alarmageddon_debug.kotlin_module +0 -0
  122. package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/AlarmModule$Companion.class +0 -0
  123. package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/AlarmModule.class +0 -0
  124. package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/AlarmPackage.class +0 -0
  125. package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/AlarmReceiver$Companion.class +0 -0
  126. package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/AlarmReceiver.class +0 -0
  127. package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/BootReceiver$Companion.class +0 -0
  128. package/android/build/tmp/kotlin-classes/debug/com/rnalarmmodule/BootReceiver.class +0 -0
  129. package/android/build.gradle +11 -21
  130. package/android/src/main/AndroidManifest.xml +16 -35
  131. package/android/src/main/java/com/rnalarmmodule/AlarmModule.kt +163 -387
  132. package/android/src/main/java/com/rnalarmmodule/AlarmPackage.kt +0 -1
  133. package/android/src/main/java/com/rnalarmmodule/AlarmReceiver.kt +300 -129
  134. package/android/src/main/java/com/rnalarmmodule/BootReceiver.kt +66 -124
  135. package/android/src/main/res/raw/alarm_default.wav +0 -0
  136. package/ios/{RNAlarmModule.m → AlarmModule.m} +9 -20
  137. package/ios/AlarmModule.swift +241 -36
  138. package/ios/RNAlarmModule-Bridging-Header.h +5 -1
  139. package/lib/index.d.ts +51 -85
  140. package/lib/index.d.ts.map +1 -1
  141. package/lib/index.js +60 -120
  142. package/lib/index.js.map +1 -1
  143. package/package.json +14 -23
  144. package/react-native-alarmageddon.podspec +10 -8
  145. package/react-native.config.js +3 -1
  146. package/src/index.ts +98 -201
  147. package/android/gradle.properties +0 -13
  148. package/android/src/main/java/com/rnalarmmodule/AlarmActivity.kt +0 -79
  149. package/android/src/main/java/com/rnalarmmodule/AlarmService.kt +0 -290
  150. package/android/src/main/res/raw/README.md +0 -36
@@ -1,183 +1,354 @@
1
1
  package com.rnalarmmodule
2
2
 
3
+ import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
4
+
5
+ import android.app.*
3
6
  import android.content.BroadcastReceiver
4
7
  import android.content.Context
5
8
  import android.content.Intent
9
+ import android.media.*
6
10
  import android.os.Build
7
11
  import android.os.PowerManager
12
+ import android.widget.Toast
13
+ import androidx.core.app.NotificationCompat
8
14
  import android.util.Log
9
15
 
10
16
  class AlarmReceiver : BroadcastReceiver() {
11
17
 
12
18
  companion object {
13
19
  private const val TAG = "AlarmReceiver"
14
- const val ACTION_TRIGGER = "com.rnalarmmodule.ALARM_TRIGGER"
15
- const val ACTION_STOP = "com.rnalarmmodule.ALARM_STOP"
16
- const val ACTION_SNOOZE = "com.rnalarmmodule.ALARM_SNOOZE"
20
+ const val ACTION_STOP = "com.rnalarmmodule.ACTION_STOP_ALARM"
21
+ const val ACTION_SNOOZE = "com.rnalarmmodule.ACTION_SNOOZE_ALARM"
22
+ private const val CHANNEL_ID = "rn_alarm_module_channel"
23
+ private const val RAW_RES = "alarm_default"
24
+ private const val SNOOZE_MINUTES = 5
25
+ private const val AUTO_STOP_SECONDS = 60
26
+
27
+ @Volatile
28
+ private var player: MediaPlayer? = null
29
+
30
+ @Volatile
31
+ private var audioManager: AudioManager? = null
32
+
33
+ @Volatile
34
+ private var originalVolume: Int = 0
35
+
36
+ @Volatile
37
+ var activeAlarmId: String? = null
38
+
39
+ @Volatile
40
+ private var wakeLock: PowerManager.WakeLock? = null
17
41
  }
18
42
 
19
- override fun onReceive(context: Context, intent: Intent) {
20
- Log.d(TAG, "onReceive: action=${intent.action}")
43
+ override fun onReceive(context: Context, intent: Intent?) {
44
+ intent ?: return
45
+ val action = intent.action
46
+ val id = intent.getStringExtra("id") ?: return
21
47
 
22
- // Acquire wake lock to ensure device stays awake
23
- val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
24
- val wakeLock = powerManager.newWakeLock(
25
- PowerManager.PARTIAL_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP,
26
- "AlarmModule:AlarmWakeLock"
27
- )
28
- wakeLock.acquire(60 * 1000L) // 60 seconds max
48
+ Log.d(TAG, "onReceive: action=$action id=$id")
29
49
 
30
- try {
31
- when (intent.action) {
32
- ACTION_TRIGGER -> handleAlarmTrigger(context, intent)
33
- ACTION_STOP -> handleAlarmStop(context, intent)
34
- ACTION_SNOOZE -> handleAlarmSnooze(context, intent)
35
- else -> {
36
- Log.w(TAG, "Unknown action: ${intent.action}")
37
- }
50
+ when (action) {
51
+ ACTION_STOP -> {
52
+ stopAlarm(context, id)
53
+ return
38
54
  }
39
- } catch (e: Exception) {
40
- Log.e(TAG, "Error handling alarm: ${e.message}", e)
41
- } finally {
42
- if (wakeLock.isHeld) {
43
- wakeLock.release()
55
+
56
+ ACTION_SNOOZE -> {
57
+ stopAlarm(context, id)
58
+ scheduleSnooze(context, intent)
59
+ return
44
60
  }
45
61
  }
46
- }
47
62
 
48
- private fun handleAlarmTrigger(context: Context, intent: Intent) {
49
- val alarmId = intent.getStringExtra("alarmId") ?: return
50
63
  val title = intent.getStringExtra("title") ?: "Alarm"
51
64
  val body = intent.getStringExtra("body") ?: ""
52
- val soundUri = intent.getStringExtra("soundUri") ?: ""
53
- val vibrate = intent.getBooleanExtra("vibrate", true)
54
- val snoozeMinutes = intent.getIntExtra("snoozeMinutes", 5)
55
- val autoStopSeconds = intent.getIntExtra("autoStopSeconds", 60)
65
+ val snoozeEnabled = intent.getBooleanExtra("snoozeEnabled", true)
66
+ val snoozeInterval = intent.getIntExtra("snoozeInterval", SNOOZE_MINUTES)
67
+ activeAlarmId = id
56
68
 
57
- Log.d(TAG, "Alarm triggered: id=$alarmId, title=$title")
69
+ emitActiveAlarmId(activeAlarmId)
58
70
 
59
- // Store active alarm ID in SharedPreferences
60
- val prefs = context.getSharedPreferences("rn_alarm_module_alarms", Context.MODE_PRIVATE)
61
- prefs.edit().putString("active_alarm_id", alarmId).apply()
71
+ acquireWakeLock(context)
72
+ setupAudio(context)
73
+ playAlarm(context, id)
74
+ showNotificationWithActions(context, id, title, body, snoozeEnabled, snoozeInterval)
75
+ }
62
76
 
63
- // Start the alarm service
64
- val serviceIntent = Intent(context, AlarmService::class.java).apply {
65
- action = AlarmService.ACTION_START
66
- putExtra("alarmId", alarmId)
67
- putExtra("title", title)
68
- putExtra("body", body)
69
- putExtra("soundUri", soundUri)
70
- putExtra("vibrate", vibrate)
71
- putExtra("snoozeMinutes", snoozeMinutes)
72
- putExtra("autoStopSeconds", autoStopSeconds)
77
+ private fun acquireWakeLock(context: Context) {
78
+ if (wakeLock?.isHeld == true) return
79
+ val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
80
+ wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "rnalarmmodule:AlarmWakeLock").apply {
81
+ setReferenceCounted(false)
82
+ // Acquire for a bounded time (65s) – alarm auto-stops at 60s.
83
+ acquire((AUTO_STOP_SECONDS + 5) * 1000L)
73
84
  }
85
+ }
74
86
 
75
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
76
- context.startForegroundService(serviceIntent)
77
- } else {
78
- context.startService(serviceIntent)
87
+ private fun releaseWakeLock() {
88
+ try {
89
+ if (wakeLock?.isHeld == true) wakeLock?.release()
90
+ } catch (_: Exception) {
91
+ }
92
+ wakeLock = null
93
+ }
94
+
95
+ private fun setupAudio(context: Context) {
96
+ if (audioManager == null) {
97
+ audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
79
98
  }
99
+ audioManager?.let { am ->
100
+ originalVolume = am.getStreamVolume(AudioManager.STREAM_ALARM)
101
+ val maxVolume = am.getStreamMaxVolume(AudioManager.STREAM_ALARM)
102
+ am.setStreamVolume(
103
+ AudioManager.STREAM_ALARM,
104
+ maxVolume,
105
+ AudioManager.FLAG_SHOW_UI or AudioManager.FLAG_PLAY_SOUND
106
+ )
80
107
 
81
- // Try to emit event to React Native
82
- AlarmModule.emitActiveAlarmId(alarmId)
108
+ val attributes = AudioAttributes.Builder()
109
+ .setUsage(AudioAttributes.USAGE_ALARM)
110
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
111
+ .build()
83
112
 
84
- // Launch full-screen activity to wake up screen
85
- launchAlarmActivity(context, alarmId, title, body, snoozeMinutes)
86
- }
113
+ val focusRequest = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
114
+ AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
115
+ .setAudioAttributes(attributes)
116
+ .setOnAudioFocusChangeListener {}
117
+ .build()
118
+ } else null
87
119
 
88
- private fun handleAlarmStop(context: Context, intent: Intent) {
89
- val alarmId = intent.getStringExtra("alarmId") ?: return
90
- Log.d(TAG, "Stopping alarm: id=$alarmId")
120
+ @Suppress("DEPRECATION")
121
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
122
+ am.requestAudioFocus(focusRequest!!)
123
+ } else {
124
+ am.requestAudioFocus(null, AudioManager.STREAM_ALARM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
125
+ }
126
+ }
127
+ }
91
128
 
92
- // Stop the alarm service
93
- val serviceIntent = Intent(context, AlarmService::class.java).apply {
94
- action = AlarmService.ACTION_STOP
95
- putExtra("alarmId", alarmId)
129
+ private fun playAlarm(context: Context, id: String) {
130
+ player?.let {
131
+ try {
132
+ if (it.isPlaying) it.stop()
133
+ } catch (_: Exception) {
134
+ }
135
+ it.release()
96
136
  }
97
- context.stopService(serviceIntent)
98
137
 
99
- // Clear active alarm state
100
- val prefs = context.getSharedPreferences("rn_alarm_module_alarms", Context.MODE_PRIVATE)
101
- prefs.edit().remove("active_alarm_id").apply()
138
+ player = MediaPlayer().apply {
139
+ try {
140
+ // Try to use custom alarm sound from host app's res/raw folder
141
+ val resId = context.resources.getIdentifier(RAW_RES, "raw", context.packageName)
142
+ if (resId != 0) {
143
+ setDataSource(
144
+ context,
145
+ android.net.Uri.parse("android.resource://${context.packageName}/raw/$RAW_RES")
146
+ )
147
+ } else {
148
+ // Fallback to default alarm sound
149
+ val alarmUri = android.media.RingtoneManager.getDefaultUri(android.media.RingtoneManager.TYPE_ALARM)
150
+ ?: android.media.RingtoneManager.getDefaultUri(android.media.RingtoneManager.TYPE_NOTIFICATION)
151
+ setDataSource(context, alarmUri)
152
+ }
153
+
154
+ setAudioAttributes(
155
+ AudioAttributes.Builder()
156
+ .setUsage(AudioAttributes.USAGE_ALARM)
157
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
158
+ .build()
159
+ )
160
+ @Suppress("DEPRECATION")
161
+ setAudioStreamType(AudioManager.STREAM_ALARM)
162
+ isLooping = true
163
+ prepare()
164
+ start()
165
+ Log.d(TAG, "Alarm sound started for id=$id")
166
+ } catch (e: Exception) {
167
+ Log.e(TAG, "Failed to play alarm sound: ${e.message}")
168
+ e.printStackTrace()
169
+ }
170
+ }
102
171
 
103
- // Emit null to indicate alarm stopped
104
- AlarmModule.emitActiveAlarmId(null)
172
+ // Auto-stop after configured time
173
+ Thread {
174
+ try {
175
+ Thread.sleep(AUTO_STOP_SECONDS * 1000L)
176
+ } catch (_: InterruptedException) {
177
+ } finally {
178
+ if (activeAlarmId == id) {
179
+ Log.d(TAG, "Auto-stopping alarm id=$id after ${AUTO_STOP_SECONDS}s")
180
+ stopAlarm(context, id)
181
+ }
182
+ }
183
+ }.start()
105
184
  }
106
185
 
107
- private fun handleAlarmSnooze(context: Context, intent: Intent) {
108
- val alarmId = intent.getStringExtra("alarmId") ?: return
109
- val snoozeMinutes = intent.getIntExtra("snoozeMinutes", 5)
110
- Log.d(TAG, "Snoozing alarm: id=$alarmId, minutes=$snoozeMinutes")
186
+ private fun showNotificationWithActions(context: Context, id: String, title: String, body: String, snoozeEnabled: Boolean = true, snoozeInterval: Int = SNOOZE_MINUTES) {
187
+ val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
111
188
 
112
- // Stop current alarm
113
- handleAlarmStop(context, intent)
189
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
190
+ val channel = NotificationChannel(
191
+ CHANNEL_ID, "Alarms", NotificationManager.IMPORTANCE_HIGH
192
+ ).apply {
193
+ setSound(null, null)
194
+ description = "Alarm notifications"
195
+ }
196
+ nm.createNotificationChannel(channel)
197
+ }
114
198
 
115
- // Reschedule the alarm
116
- val snoozeTime = System.currentTimeMillis() + (snoozeMinutes * 60 * 1000L)
117
-
118
- val triggerIntent = Intent(context, AlarmReceiver::class.java).apply {
119
- action = ACTION_TRIGGER
120
- putExtra("alarmId", alarmId)
121
- putExtra("title", intent.getStringExtra("title") ?: "Alarm")
122
- putExtra("body", intent.getStringExtra("body") ?: "")
123
- putExtra("soundUri", intent.getStringExtra("soundUri") ?: "")
124
- putExtra("vibrate", intent.getBooleanExtra("vibrate", true))
125
- putExtra("snoozeMinutes", snoozeMinutes)
126
- putExtra("autoStopSeconds", intent.getIntExtra("autoStopSeconds", 60))
127
- }
128
-
129
- val pendingIntent = android.app.PendingIntent.getBroadcast(
130
- context,
131
- alarmId.hashCode(),
132
- triggerIntent,
133
- android.app.PendingIntent.FLAG_UPDATE_CURRENT or android.app.PendingIntent.FLAG_IMMUTABLE
199
+ val stopIntent = Intent(context, AlarmReceiver::class.java).apply {
200
+ action = ACTION_STOP
201
+ putExtra("id", id)
202
+ }
203
+ val stopPendingIntent = PendingIntent.getBroadcast(
204
+ context, id.hashCode() + 1, stopIntent,
205
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
134
206
  )
135
207
 
136
- val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as android.app.AlarmManager
208
+ val snoozePendingIntent = if (snoozeEnabled) {
209
+ val snoozeIntent = Intent(context, AlarmReceiver::class.java).apply {
210
+ action = ACTION_SNOOZE
211
+ putExtra("id", id)
212
+ putExtra("title", title)
213
+ putExtra("body", "$body (Snoozed)")
214
+ putExtra("snoozeMinutes", snoozeInterval)
215
+ putExtra("snoozeEnabled", snoozeEnabled)
216
+ putExtra("snoozeInterval", snoozeInterval)
217
+ }
218
+ PendingIntent.getBroadcast(
219
+ context, id.hashCode() + 2, snoozeIntent,
220
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
221
+ )
222
+ } else null
223
+
224
+ // Try to get the host app's launcher activity
225
+ val launchIntent = context.packageManager.getLaunchIntentForPackage(context.packageName)?.apply {
226
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
227
+ putExtra("alarm_id", id)
228
+ }
137
229
 
138
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
139
- if (alarmManager.canScheduleExactAlarms()) {
140
- alarmManager.setAlarmClock(
141
- android.app.AlarmManager.AlarmClockInfo(snoozeTime, pendingIntent),
142
- pendingIntent
143
- )
230
+ val fullScreenPendingIntent = if (launchIntent != null) {
231
+ PendingIntent.getActivity(
232
+ context, 0, launchIntent,
233
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
234
+ )
235
+ } else null
236
+
237
+ val builder = NotificationCompat.Builder(context, CHANNEL_ID)
238
+ .setSmallIcon(android.R.drawable.ic_lock_idle_alarm)
239
+ .setContentTitle(title)
240
+ .setContentText(body)
241
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
242
+ .setCategory(NotificationCompat.CATEGORY_ALARM)
243
+ .addAction(android.R.drawable.ic_menu_close_clear_cancel, "Stop", stopPendingIntent)
244
+ .setOngoing(true)
245
+ .setSound(null)
246
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
247
+
248
+ // Only add snooze action if snooze is enabled
249
+ if (snoozeEnabled && snoozePendingIntent != null) {
250
+ builder.addAction(android.R.drawable.ic_menu_recent_history, "Snooze", snoozePendingIntent)
251
+ }
252
+
253
+ if (fullScreenPendingIntent != null) {
254
+ builder.setFullScreenIntent(fullScreenPendingIntent, true)
255
+ builder.setContentIntent(fullScreenPendingIntent)
256
+ }
257
+
258
+ val notification = builder.build().apply {
259
+ flags = flags or Notification.FLAG_NO_CLEAR
260
+ }
261
+
262
+ nm.notify(id.hashCode(), notification)
263
+ Log.d(TAG, "Notification shown for alarm id=$id")
264
+ }
265
+
266
+ private fun stopAlarm(context: Context, id: String?) {
267
+ Log.d(TAG, "Stopping alarm id=$id")
268
+
269
+ player?.apply {
270
+ try {
271
+ if (isPlaying) stop()
272
+ } catch (_: Exception) {
273
+ }
274
+ release()
275
+ }
276
+ player = null
277
+
278
+ audioManager?.let { am ->
279
+ try {
280
+ am.setStreamVolume(AudioManager.STREAM_ALARM, originalVolume, 0)
281
+ } catch (_: Exception) {
282
+ }
283
+ @Suppress("DEPRECATION")
284
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
285
+ try {
286
+ am.abandonAudioFocusRequest(
287
+ AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT).build()
288
+ )
289
+ } catch (_: Exception) {
290
+ }
144
291
  } else {
145
- alarmManager.setAndAllowWhileIdle(
146
- android.app.AlarmManager.RTC_WAKEUP,
147
- snoozeTime,
148
- pendingIntent
149
- )
292
+ am.abandonAudioFocus(null)
150
293
  }
151
- } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
152
- alarmManager.setAlarmClock(
153
- android.app.AlarmManager.AlarmClockInfo(snoozeTime, pendingIntent),
154
- pendingIntent
155
- )
294
+ }
295
+
296
+ releaseWakeLock()
297
+
298
+ val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
299
+ id?.let { nm.cancel(it.hashCode()) }
300
+
301
+ if (activeAlarmId == id) {
302
+ activeAlarmId = null
303
+ emitActiveAlarmId(null)
304
+ }
305
+ }
306
+
307
+ private fun scheduleSnooze(context: Context, originalIntent: Intent) {
308
+ val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
309
+ val id = originalIntent.getStringExtra("id") ?: return
310
+ val title = originalIntent.getStringExtra("title") ?: "Alarm"
311
+ val body = originalIntent.getStringExtra("body") ?: ""
312
+ val minutes = originalIntent.getIntExtra("snoozeMinutes", SNOOZE_MINUTES)
313
+
314
+ val snoozeIntent = Intent(context, AlarmReceiver::class.java).apply {
315
+ putExtra("id", id)
316
+ putExtra("title", title)
317
+ putExtra("body", body)
318
+ }
319
+
320
+ val pendingIntent = PendingIntent.getBroadcast(
321
+ context, id.hashCode(), snoozeIntent,
322
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
323
+ )
324
+
325
+ val triggerTime = System.currentTimeMillis() + minutes * 60 * 1000
326
+
327
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
328
+ alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent)
156
329
  } else {
157
- alarmManager.setExact(
158
- android.app.AlarmManager.RTC_WAKEUP,
159
- snoozeTime,
160
- pendingIntent
161
- )
330
+ alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent)
162
331
  }
163
332
 
164
- Log.d(TAG, "Alarm snoozed and rescheduled for: $snoozeTime")
333
+ Toast.makeText(context, "Snoozed for $minutes minutes", Toast.LENGTH_SHORT).show()
334
+ Log.d(TAG, "Alarm $id snoozed for $minutes minutes")
165
335
  }
166
336
 
167
- private fun launchAlarmActivity(context: Context, alarmId: String, title: String, body: String, snoozeMinutes: Int) {
168
- try {
169
- val activityIntent = Intent(context, AlarmActivity::class.java).apply {
170
- flags = Intent.FLAG_ACTIVITY_NEW_TASK or
171
- Intent.FLAG_ACTIVITY_CLEAR_TOP or
172
- Intent.FLAG_ACTIVITY_SINGLE_TOP
173
- putExtra("alarmId", alarmId)
174
- putExtra("title", title)
175
- putExtra("body", body)
176
- putExtra("snoozeMinutes", snoozeMinutes)
337
+ private fun emitActiveAlarmId(id: String?) {
338
+ // Use AlarmModule's static context reference instead of MainApplication
339
+ val reactContext = AlarmModule.getReactContext()
340
+
341
+ if (reactContext != null && reactContext.hasActiveCatalystInstance()) {
342
+ try {
343
+ reactContext
344
+ .getJSModule(RCTDeviceEventEmitter::class.java)
345
+ .emit("activeAlarmId", id)
346
+ Log.d(TAG, "Emitted activeAlarmId event: $id")
347
+ } catch (e: Exception) {
348
+ Log.e(TAG, "Failed to emit activeAlarmId event: ${e.message}")
177
349
  }
178
- context.startActivity(activityIntent)
179
- } catch (e: Exception) {
180
- Log.e(TAG, "Failed to launch alarm activity: ${e.message}", e)
350
+ } else {
351
+ Log.w(TAG, "Cannot emit event: ReactContext not available")
181
352
  }
182
353
  }
183
354
  }