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,183 +1,344 @@
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
+ activeAlarmId = id
56
66
 
57
- Log.d(TAG, "Alarm triggered: id=$alarmId, title=$title")
67
+ emitActiveAlarmId(activeAlarmId)
58
68
 
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()
69
+ acquireWakeLock(context)
70
+ setupAudio(context)
71
+ playAlarm(context, id)
72
+ showNotificationWithActions(context, id, title, body)
73
+ }
62
74
 
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)
75
+ private fun acquireWakeLock(context: Context) {
76
+ if (wakeLock?.isHeld == true) return
77
+ val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
78
+ wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "rnalarmmodule:AlarmWakeLock").apply {
79
+ setReferenceCounted(false)
80
+ // Acquire for a bounded time (65s) – alarm auto-stops at 60s.
81
+ acquire((AUTO_STOP_SECONDS + 5) * 1000L)
73
82
  }
83
+ }
74
84
 
75
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
76
- context.startForegroundService(serviceIntent)
77
- } else {
78
- context.startService(serviceIntent)
85
+ private fun releaseWakeLock() {
86
+ try {
87
+ if (wakeLock?.isHeld == true) wakeLock?.release()
88
+ } catch (_: Exception) {
79
89
  }
90
+ wakeLock = null
91
+ }
80
92
 
81
- // Try to emit event to React Native
82
- AlarmModule.emitActiveAlarmId(alarmId)
93
+ private fun setupAudio(context: Context) {
94
+ if (audioManager == null) {
95
+ audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
96
+ }
97
+ audioManager?.let { am ->
98
+ originalVolume = am.getStreamVolume(AudioManager.STREAM_ALARM)
99
+ val maxVolume = am.getStreamMaxVolume(AudioManager.STREAM_ALARM)
100
+ am.setStreamVolume(
101
+ AudioManager.STREAM_ALARM,
102
+ maxVolume,
103
+ AudioManager.FLAG_SHOW_UI or AudioManager.FLAG_PLAY_SOUND
104
+ )
83
105
 
84
- // Launch full-screen activity to wake up screen
85
- launchAlarmActivity(context, alarmId, title, body, snoozeMinutes)
86
- }
106
+ val attributes = AudioAttributes.Builder()
107
+ .setUsage(AudioAttributes.USAGE_ALARM)
108
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
109
+ .build()
110
+
111
+ val focusRequest = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
112
+ AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
113
+ .setAudioAttributes(attributes)
114
+ .setOnAudioFocusChangeListener {}
115
+ .build()
116
+ } else null
87
117
 
88
- private fun handleAlarmStop(context: Context, intent: Intent) {
89
- val alarmId = intent.getStringExtra("alarmId") ?: return
90
- Log.d(TAG, "Stopping alarm: id=$alarmId")
118
+ @Suppress("DEPRECATION")
119
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
120
+ am.requestAudioFocus(focusRequest!!)
121
+ } else {
122
+ am.requestAudioFocus(null, AudioManager.STREAM_ALARM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
123
+ }
124
+ }
125
+ }
91
126
 
92
- // Stop the alarm service
93
- val serviceIntent = Intent(context, AlarmService::class.java).apply {
94
- action = AlarmService.ACTION_STOP
95
- putExtra("alarmId", alarmId)
127
+ private fun playAlarm(context: Context, id: String) {
128
+ player?.let {
129
+ try {
130
+ if (it.isPlaying) it.stop()
131
+ } catch (_: Exception) {
132
+ }
133
+ it.release()
96
134
  }
97
- context.stopService(serviceIntent)
98
135
 
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()
136
+ player = MediaPlayer().apply {
137
+ try {
138
+ // Try to use custom alarm sound from host app's res/raw folder
139
+ val resId = context.resources.getIdentifier(RAW_RES, "raw", context.packageName)
140
+ if (resId != 0) {
141
+ setDataSource(
142
+ context,
143
+ android.net.Uri.parse("android.resource://${context.packageName}/raw/$RAW_RES")
144
+ )
145
+ } else {
146
+ // Fallback to default alarm sound
147
+ val alarmUri = android.media.RingtoneManager.getDefaultUri(android.media.RingtoneManager.TYPE_ALARM)
148
+ ?: android.media.RingtoneManager.getDefaultUri(android.media.RingtoneManager.TYPE_NOTIFICATION)
149
+ setDataSource(context, alarmUri)
150
+ }
151
+
152
+ setAudioAttributes(
153
+ AudioAttributes.Builder()
154
+ .setUsage(AudioAttributes.USAGE_ALARM)
155
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
156
+ .build()
157
+ )
158
+ @Suppress("DEPRECATION")
159
+ setAudioStreamType(AudioManager.STREAM_ALARM)
160
+ isLooping = true
161
+ prepare()
162
+ start()
163
+ Log.d(TAG, "Alarm sound started for id=$id")
164
+ } catch (e: Exception) {
165
+ Log.e(TAG, "Failed to play alarm sound: ${e.message}")
166
+ e.printStackTrace()
167
+ }
168
+ }
102
169
 
103
- // Emit null to indicate alarm stopped
104
- AlarmModule.emitActiveAlarmId(null)
170
+ // Auto-stop after configured time
171
+ Thread {
172
+ try {
173
+ Thread.sleep(AUTO_STOP_SECONDS * 1000L)
174
+ } catch (_: InterruptedException) {
175
+ } finally {
176
+ if (activeAlarmId == id) {
177
+ Log.d(TAG, "Auto-stopping alarm id=$id after ${AUTO_STOP_SECONDS}s")
178
+ stopAlarm(context, id)
179
+ }
180
+ }
181
+ }.start()
105
182
  }
106
183
 
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")
184
+ private fun showNotificationWithActions(context: Context, id: String, title: String, body: String) {
185
+ val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
111
186
 
112
- // Stop current alarm
113
- handleAlarmStop(context, intent)
187
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
188
+ val channel = NotificationChannel(
189
+ CHANNEL_ID, "Alarms", NotificationManager.IMPORTANCE_HIGH
190
+ ).apply {
191
+ setSound(null, null)
192
+ description = "Alarm notifications"
193
+ }
194
+ nm.createNotificationChannel(channel)
195
+ }
114
196
 
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
197
+ val stopIntent = Intent(context, AlarmReceiver::class.java).apply {
198
+ action = ACTION_STOP
199
+ putExtra("id", id)
200
+ }
201
+ val stopPendingIntent = PendingIntent.getBroadcast(
202
+ context, id.hashCode() + 1, stopIntent,
203
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
134
204
  )
135
205
 
136
- val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as android.app.AlarmManager
206
+ val snoozeIntent = Intent(context, AlarmReceiver::class.java).apply {
207
+ action = ACTION_SNOOZE
208
+ putExtra("id", id)
209
+ putExtra("title", title)
210
+ putExtra("body", "$body (Snoozed)")
211
+ putExtra("snoozeMinutes", SNOOZE_MINUTES)
212
+ }
213
+ val snoozePendingIntent = PendingIntent.getBroadcast(
214
+ context, id.hashCode() + 2, snoozeIntent,
215
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
216
+ )
217
+
218
+ // Try to get the host app's launcher activity
219
+ val launchIntent = context.packageManager.getLaunchIntentForPackage(context.packageName)?.apply {
220
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
221
+ putExtra("alarm_id", id)
222
+ }
137
223
 
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
- )
224
+ val fullScreenPendingIntent = if (launchIntent != null) {
225
+ PendingIntent.getActivity(
226
+ context, 0, launchIntent,
227
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
228
+ )
229
+ } else null
230
+
231
+ val builder = NotificationCompat.Builder(context, CHANNEL_ID)
232
+ .setSmallIcon(android.R.drawable.ic_lock_idle_alarm)
233
+ .setContentTitle(title)
234
+ .setContentText(body)
235
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
236
+ .setCategory(NotificationCompat.CATEGORY_ALARM)
237
+ .addAction(android.R.drawable.ic_menu_close_clear_cancel, "Stop", stopPendingIntent)
238
+ .addAction(android.R.drawable.ic_menu_recent_history, "Snooze", snoozePendingIntent)
239
+ .setOngoing(true)
240
+ .setSound(null)
241
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
242
+
243
+ if (fullScreenPendingIntent != null) {
244
+ builder.setFullScreenIntent(fullScreenPendingIntent, true)
245
+ builder.setContentIntent(fullScreenPendingIntent)
246
+ }
247
+
248
+ val notification = builder.build().apply {
249
+ flags = flags or Notification.FLAG_NO_CLEAR
250
+ }
251
+
252
+ nm.notify(id.hashCode(), notification)
253
+ Log.d(TAG, "Notification shown for alarm id=$id")
254
+ }
255
+
256
+ private fun stopAlarm(context: Context, id: String?) {
257
+ Log.d(TAG, "Stopping alarm id=$id")
258
+
259
+ player?.apply {
260
+ try {
261
+ if (isPlaying) stop()
262
+ } catch (_: Exception) {
263
+ }
264
+ release()
265
+ }
266
+ player = null
267
+
268
+ audioManager?.let { am ->
269
+ try {
270
+ am.setStreamVolume(AudioManager.STREAM_ALARM, originalVolume, 0)
271
+ } catch (_: Exception) {
272
+ }
273
+ @Suppress("DEPRECATION")
274
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
275
+ try {
276
+ am.abandonAudioFocusRequest(
277
+ AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT).build()
278
+ )
279
+ } catch (_: Exception) {
280
+ }
144
281
  } else {
145
- alarmManager.setAndAllowWhileIdle(
146
- android.app.AlarmManager.RTC_WAKEUP,
147
- snoozeTime,
148
- pendingIntent
149
- )
282
+ am.abandonAudioFocus(null)
150
283
  }
151
- } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
152
- alarmManager.setAlarmClock(
153
- android.app.AlarmManager.AlarmClockInfo(snoozeTime, pendingIntent),
154
- pendingIntent
155
- )
284
+ }
285
+
286
+ releaseWakeLock()
287
+
288
+ val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
289
+ id?.let { nm.cancel(it.hashCode()) }
290
+
291
+ if (activeAlarmId == id) {
292
+ activeAlarmId = null
293
+ emitActiveAlarmId(null)
294
+ }
295
+ }
296
+
297
+ private fun scheduleSnooze(context: Context, originalIntent: Intent) {
298
+ val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
299
+ val id = originalIntent.getStringExtra("id") ?: return
300
+ val title = originalIntent.getStringExtra("title") ?: "Alarm"
301
+ val body = originalIntent.getStringExtra("body") ?: ""
302
+ val minutes = originalIntent.getIntExtra("snoozeMinutes", SNOOZE_MINUTES)
303
+
304
+ val snoozeIntent = Intent(context, AlarmReceiver::class.java).apply {
305
+ putExtra("id", id)
306
+ putExtra("title", title)
307
+ putExtra("body", body)
308
+ }
309
+
310
+ val pendingIntent = PendingIntent.getBroadcast(
311
+ context, id.hashCode(), snoozeIntent,
312
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
313
+ )
314
+
315
+ val triggerTime = System.currentTimeMillis() + minutes * 60 * 1000
316
+
317
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
318
+ alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent)
156
319
  } else {
157
- alarmManager.setExact(
158
- android.app.AlarmManager.RTC_WAKEUP,
159
- snoozeTime,
160
- pendingIntent
161
- )
320
+ alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent)
162
321
  }
163
322
 
164
- Log.d(TAG, "Alarm snoozed and rescheduled for: $snoozeTime")
323
+ Toast.makeText(context, "Snoozed for $minutes minutes", Toast.LENGTH_SHORT).show()
324
+ Log.d(TAG, "Alarm $id snoozed for $minutes minutes")
165
325
  }
166
326
 
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)
327
+ private fun emitActiveAlarmId(id: String?) {
328
+ // Use AlarmModule's static context reference instead of MainApplication
329
+ val reactContext = AlarmModule.getReactContext()
330
+
331
+ if (reactContext != null && reactContext.hasActiveCatalystInstance()) {
332
+ try {
333
+ reactContext
334
+ .getJSModule(RCTDeviceEventEmitter::class.java)
335
+ .emit("activeAlarmId", id)
336
+ Log.d(TAG, "Emitted activeAlarmId event: $id")
337
+ } catch (e: Exception) {
338
+ Log.e(TAG, "Failed to emit activeAlarmId event: ${e.message}")
177
339
  }
178
- context.startActivity(activityIntent)
179
- } catch (e: Exception) {
180
- Log.e(TAG, "Failed to launch alarm activity: ${e.message}", e)
340
+ } else {
341
+ Log.w(TAG, "Cannot emit event: ReactContext not available")
181
342
  }
182
343
  }
183
344
  }