react-native-alarms 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AlarmService.ts CHANGED
@@ -25,36 +25,60 @@
25
25
  * });
26
26
  * ```
27
27
  */
28
- import { NativeEventEmitter, NativeModules } from 'react-native';
28
+ import { NativeEventEmitter, type EmitterSubscription } from 'react-native';
29
29
  import type {
30
30
  AlarmConfig,
31
31
  AlarmProp,
32
32
  AlarmPermission,
33
33
  AlarmEvent,
34
+ AlarmEventMap,
35
+ AlarmEventName,
34
36
  } from './src/types';
35
37
  import { AlarmRegistry } from './src/utils';
36
-
37
- const { Alarm: NativeAlarm } = NativeModules;
38
+ import Alarm, { getNativeAlarmModule } from './specs/NativeAlarm';
38
39
 
39
40
  /**
40
- * Resolves the native alarm module when it is linked.
41
- *
42
- * The module is loaded conditionally so importing the service does not
43
- * immediately fail when the native module is unavailable.
41
+ * Resolves the native module when available and keeps the lazy proxy as a
42
+ * fallback so missing native linking is reported when an operation is called.
44
43
  */
45
- const alarm = NativeAlarm ? require('./specs/NativeAlarm').default : null;
44
+ const nativeAlarm = getNativeAlarmModule();
45
+ const alarm = nativeAlarm ?? Alarm;
46
46
 
47
47
  /**
48
48
  * Native event emitter used to subscribe to alarm lifecycle events.
49
49
  */
50
- const alarmEmitter = NativeAlarm ? new NativeEventEmitter(NativeAlarm) : null;
50
+ const alarmEmitter = nativeAlarm ? new NativeEventEmitter(nativeAlarm) : null;
51
51
 
52
52
  /**
53
53
  * Callback invoked when a native alarm event is received.
54
54
  *
55
55
  * @param alarm Event data emitted by the native alarm module.
56
56
  */
57
- type AlarmEventListener = (alarm: AlarmEvent) => void;
57
+ type AlarmEventListener<K extends AlarmEventName = AlarmEventName> = (
58
+ alarm: AlarmEventMap[K],
59
+ ) => void;
60
+
61
+ const ALARM_EVENT_NAMES = [
62
+ 'onAlarmFired',
63
+ 'onAlarmDismissed',
64
+ 'onAlarmSnoozed',
65
+ ] as const satisfies readonly AlarmEventName[];
66
+
67
+ function isAlarmEvent(value: Object): value is AlarmEvent {
68
+ return (
69
+ 'alarmId' in value &&
70
+ typeof value.alarmId === 'string' &&
71
+ 'timestamp' in value &&
72
+ typeof value.timestamp === 'number' &&
73
+ 'title' in value &&
74
+ typeof value.title === 'string' &&
75
+ 'body' in value &&
76
+ typeof value.body === 'string' &&
77
+ 'data' in value &&
78
+ typeof value.data === 'string' &&
79
+ (!('image' in value) || typeof value.image === 'string')
80
+ );
81
+ }
58
82
 
59
83
  /**
60
84
  * High-level service for scheduling and managing native alarms.
@@ -83,19 +107,17 @@ export class AlarmService {
83
107
  * more than once for the same event.
84
108
  */
85
109
  private static listeners: {
86
- fired: Set<AlarmEventListener>;
87
- dismissed: Set<AlarmEventListener>;
88
- snoozed: Set<AlarmEventListener>;
110
+ [K in AlarmEventName]: Set<AlarmEventListener<K>>;
89
111
  } = {
90
- fired: new Set(),
91
- dismissed: new Set(),
92
- snoozed: new Set(),
112
+ onAlarmFired: new Set(),
113
+ onAlarmDismissed: new Set(),
114
+ onAlarmSnoozed: new Set(),
93
115
  };
94
116
 
95
117
  /**
96
118
  * Native event subscriptions created by {@link initialize}.
97
119
  */
98
- private static subscriptions: any[] = [];
120
+ private static subscriptions: EmitterSubscription[] = [];
99
121
 
100
122
  /**
101
123
  * Initializes native event forwarding.
@@ -107,25 +129,18 @@ export class AlarmService {
107
129
  * This method is called automatically by the event subscription helpers.
108
130
  */
109
131
  static initialize(): void {
110
- if (!alarmEmitter || !this.subscriptions.length) {
111
- this.subscriptions = [
112
- alarmEmitter?.addListener('onAlarmFired', (data: any) => {
113
- this.listeners.fired.forEach(listener =>
114
- listener(data as AlarmEvent),
132
+ if (!alarmEmitter || this.subscriptions.length) return;
133
+ this.subscriptions = ALARM_EVENT_NAMES.map(eventName =>
134
+ alarmEmitter.addListener(eventName, (event: Object) => {
135
+ if (!isAlarmEvent(event)) {
136
+ console.error(
137
+ `[react-native-alarms] Ignoring invalid ${eventName} event payload`,
115
138
  );
116
- }),
117
- alarmEmitter?.addListener('onAlarmDismissed', (data: any) => {
118
- this.listeners.dismissed.forEach(listener =>
119
- listener(data as AlarmEvent),
120
- );
121
- }),
122
- alarmEmitter?.addListener('onAlarmSnoozed', (data: any) => {
123
- this.listeners.snoozed.forEach(listener =>
124
- listener(data as AlarmEvent),
125
- );
126
- }),
127
- ].filter(Boolean);
128
- }
139
+ return;
140
+ }
141
+ this.listeners[eventName].forEach(listener => listener(event));
142
+ }),
143
+ );
129
144
  }
130
145
 
131
146
  /**
@@ -139,7 +154,7 @@ export class AlarmService {
139
154
  * remove individual callbacks.
140
155
  */
141
156
  static cleanup(): void {
142
- this.subscriptions.forEach(sub => sub?.remove());
157
+ this.subscriptions.forEach(sub => sub.remove());
143
158
  this.subscriptions = [];
144
159
  }
145
160
 
@@ -161,11 +176,10 @@ export class AlarmService {
161
176
  * unsubscribe();
162
177
  * ```
163
178
  */
164
- static onAlarmFired(listener: AlarmEventListener): () => boolean {
165
- this.initialize();
166
- this.listeners.fired.add(listener);
167
-
168
- return () => this.listeners.fired.delete(listener);
179
+ static onAlarmFired(
180
+ listener: AlarmEventListener<'onAlarmFired'>,
181
+ ): () => void {
182
+ return this.subscribe('onAlarmFired', listener);
169
183
  }
170
184
 
171
185
  /**
@@ -174,11 +188,10 @@ export class AlarmService {
174
188
  * @param listener Callback invoked with the dismissed alarm's event data.
175
189
  * @returns A function that unregisters this callback.
176
190
  */
177
- static onAlarmDismissed(listener: AlarmEventListener): () => boolean {
178
- this.initialize();
179
- this.listeners.dismissed.add(listener);
180
-
181
- return () => this.listeners.dismissed.delete(listener);
191
+ static onAlarmDismissed(
192
+ listener: AlarmEventListener<'onAlarmDismissed'>,
193
+ ): () => void {
194
+ return this.subscribe('onAlarmDismissed', listener);
182
195
  }
183
196
 
184
197
  /**
@@ -187,11 +200,21 @@ export class AlarmService {
187
200
  * @param listener Callback invoked with the snoozed alarm's event data.
188
201
  * @returns A function that unregisters this callback.
189
202
  */
190
- static onAlarmSnoozed(listener: AlarmEventListener): () => boolean {
191
- this.initialize();
192
- this.listeners.snoozed.add(listener);
203
+ static onAlarmSnoozed(
204
+ listener: AlarmEventListener<'onAlarmSnoozed'>,
205
+ ): () => void {
206
+ return this.subscribe('onAlarmSnoozed', listener);
207
+ }
193
208
 
194
- return () => this.listeners.snoozed.delete(listener);
209
+ private static subscribe<K extends AlarmEventName>(
210
+ eventName: K,
211
+ listener: AlarmEventListener<K>,
212
+ ): () => void {
213
+ this.initialize();
214
+ this.listeners[eventName].add(listener);
215
+ return () => {
216
+ this.listeners[eventName].delete(listener);
217
+ };
195
218
  }
196
219
 
197
220
  /**
@@ -201,7 +224,6 @@ export class AlarmService {
201
224
  * @throws Error if the native alarm module is not linked.
202
225
  */
203
226
  static async checkPermission(): Promise<AlarmPermission> {
204
- if (!alarm) throw new Error('Alarm module not linked');
205
227
  return alarm.checkPermission();
206
228
  }
207
229
 
@@ -214,7 +236,6 @@ export class AlarmService {
214
236
  * @throws Error if the native alarm module is not linked.
215
237
  */
216
238
  static async requestPermission(): Promise<AlarmPermission> {
217
- if (!alarm) throw new Error('Alarm module not linked');
218
239
  return alarm.requestPermission();
219
240
  }
220
241
 
@@ -237,15 +258,7 @@ export class AlarmService {
237
258
  * ```
238
259
  */
239
260
  static async scheduleAlarm(config: AlarmConfig): Promise<string> {
240
- if (!alarm) {
241
- throw new Error('Alarm module not linked');
242
- }
243
- const componentName = AlarmRegistry.resolve(config.presentation?.type);
244
- return alarm.scheduleAlarm({
245
- ...config,
246
- componentName,
247
- componentProps: config.presentation?.props ?? {},
248
- });
261
+ return alarm.scheduleAlarm(this.toNativeConfig(config));
249
262
  }
250
263
 
251
264
  /**
@@ -260,8 +273,17 @@ export class AlarmService {
260
273
  alarmId: string,
261
274
  config: AlarmConfig,
262
275
  ): Promise<boolean> {
263
- if (!alarm) throw new Error('Alarm module not linked');
264
- return alarm.updateAlarm(alarmId, config);
276
+ return alarm.updateAlarm(alarmId, this.toNativeConfig(config));
277
+ }
278
+
279
+ private static toNativeConfig<T extends AlarmConfig>(
280
+ config: T,
281
+ ): T & { componentName: string; componentProps: Record<string, unknown> } {
282
+ return {
283
+ ...config,
284
+ componentName: AlarmRegistry.resolve(config.presentation?.type),
285
+ componentProps: config.presentation?.props ?? {},
286
+ };
265
287
  }
266
288
 
267
289
  /**
@@ -272,7 +294,6 @@ export class AlarmService {
272
294
  * @throws Error if the native alarm module is not linked.
273
295
  */
274
296
  static async cancelAlarm(alarmId: string): Promise<boolean> {
275
- if (!alarm) throw new Error('Alarm module not linked');
276
297
  return alarm.cancelAlarm(alarmId);
277
298
  }
278
299
 
@@ -283,7 +304,6 @@ export class AlarmService {
283
304
  * @throws Error if the native alarm module is not linked.
284
305
  */
285
306
  static async cancelAllAlarms(): Promise<void> {
286
- if (!alarm) throw new Error('Alarm module not linked');
287
307
  return alarm.cancelAllAlarms();
288
308
  }
289
309
 
@@ -295,7 +315,6 @@ export class AlarmService {
295
315
  * @throws Error if the native alarm module is not linked.
296
316
  */
297
317
  static async stopAlarm(alarmId: string): Promise<void> {
298
- if (!alarm) throw new Error('Alarm module not linked');
299
318
  return alarm.stopAlarm(alarmId);
300
319
  }
301
320
 
@@ -313,7 +332,6 @@ export class AlarmService {
313
332
  * ```
314
333
  */
315
334
  static async snoozeAlarm(alarmId: string, minutes?: number): Promise<void> {
316
- if (!alarm) throw new Error('Alarm module not linked');
317
335
  return alarm.snoozeAlarm(alarmId, minutes ?? 5);
318
336
  }
319
337
 
@@ -325,7 +343,6 @@ export class AlarmService {
325
343
  * @throws Error if the native alarm module is not linked.
326
344
  */
327
345
  static async getRingingAlarm(): Promise<AlarmProp | null> {
328
- if (!alarm) throw new Error('Alarm module not linked');
329
346
  return alarm.getRingingAlarm();
330
347
  }
331
348
 
@@ -337,7 +354,6 @@ export class AlarmService {
337
354
  * @throws Error if the native alarm module is not linked.
338
355
  */
339
356
  static async getAlarm(alarmId: string): Promise<AlarmProp | null> {
340
- if (!alarm) throw new Error('Alarm module not linked');
341
357
  return alarm.getAlarm(alarmId);
342
358
  }
343
359
 
@@ -348,7 +364,6 @@ export class AlarmService {
348
364
  * @throws Error if the native alarm module is not linked.
349
365
  */
350
366
  static async getAllAlarms(): Promise<AlarmProp[]> {
351
- if (!alarm) throw new Error('Alarm module not linked');
352
367
  return alarm.getAllAlarms();
353
368
  }
354
369
 
@@ -360,7 +375,6 @@ export class AlarmService {
360
375
  * @throws Error if the native alarm module is not linked.
361
376
  */
362
377
  static async isScheduled(alarmId: string): Promise<boolean> {
363
- if (!alarm) throw new Error('Alarm module not linked');
364
378
  return alarm.isScheduled(alarmId);
365
379
  }
366
380
 
@@ -377,7 +391,6 @@ export class AlarmService {
377
391
  * @throws Error if the native alarm module is not linked.
378
392
  */
379
393
  static async openAutoStartSettings(): Promise<boolean> {
380
- if (!alarm) throw new Error('Alarm module not linked');
381
394
  return alarm.openAutoStartSettings();
382
395
  }
383
396
  }
package/README.md CHANGED
@@ -44,7 +44,7 @@ yarn add react-native-alarms
44
44
 
45
45
  ### Native Module Linking
46
46
 
47
- Since this is a native module, you'll need to link it:
47
+ For a bare React Native app, install the iOS pods and rebuild:
48
48
 
49
49
  ```bash
50
50
  cd ios && pod install && cd ..
@@ -54,11 +54,32 @@ For Android, the native code auto-links, including the permissions, ring activit
54
54
  service and receivers in its manifest. No `MainApplication` or `AndroidManifest.xml`
55
55
  changes are needed. Rebuild the app after installing.
56
56
 
57
+ ### Expo
58
+
59
+ The library works with Expo projects that generate and build native projects
60
+ (prebuild, EAS Build, or a development build). It uses React Native autolinking and
61
+ does not require an Expo config plugin:
62
+
63
+ ```bash
64
+ npm install react-native-alarms
65
+ npx expo prebuild
66
+ npx expo run:android
67
+ # or
68
+ npx expo run:ios
69
+ ```
70
+
71
+ Rebuild the native app after installing or upgrading the library. **Expo Go is not
72
+ supported** because it cannot load this library's native alarm module. Existing bare
73
+ React Native installation and setup remain unchanged.
74
+ Register the `AlarmRing` component in your app's JavaScript entry point as described
75
+ below; Expo Router apps should do this from a custom entry point before loading
76
+ `expo-router/entry`.
77
+
57
78
  ### Register the ring screen
58
79
 
59
80
  When an alarm rings, both platforms show a full-screen React component registered as
60
81
  `AlarmRing`, with the alarm (`alarmId`, `title`, `body`, `snoozeEnabled`,
61
- `snoozeMinutes`, `data`) as props. Register your own component in `index.js`:
82
+ `snoozeMinutes`, `data`, and optional `image`) as props. Register your own component in `index.js`:
62
83
 
63
84
  ```js
64
85
  import { AppRegistry } from 'react-native';
@@ -216,6 +237,7 @@ type AlarmConfig = {
216
237
  timestamp: number; // Time in milliseconds (Date.now() format)
217
238
  title: string; // Alarm title/label
218
239
  body?: string; // Alarm description
240
+ image?: string; // file:// or https:// image URI for ring screen/notifications
219
241
  sound?: string; // 'default' or custom sound file name
220
242
  vibrate?: boolean; // Enable vibration (Android only)
221
243
  snoozeEnabled?: boolean; // Allow snoozing
@@ -236,6 +258,24 @@ type AlarmPresentation = {
236
258
  };
237
259
  ```
238
260
 
261
+ Pass a URI string for `image`; for example:
262
+
263
+ ```ts
264
+ await AlarmService.scheduleAlarm({
265
+ timestamp: Date.now() + 60_000,
266
+ title: 'Appointment',
267
+ image: 'https://example.com/appointment.jpg',
268
+ });
269
+ ```
270
+
271
+ When provided, `image` is returned with queried alarms and alarm events, passed to
272
+ the registered ring screen as an initial prop, and used for the native notification
273
+ image where supported. Android accepts `https://`, `file://`, and `content://` URIs.
274
+ iOS notification attachments accept `https://` and `file://` URIs; the iOS 26+
275
+ AlarmKit system alert itself does not display custom images, but the image URI is
276
+ available to the React Native ring screen when it opens. Render it in that screen
277
+ with React Native's `Image` component and `source={{ uri: image }}`.
278
+
239
279
  > **How the native layer stores these:** `AlarmConfig.presentation` is resolved by `AlarmService` before being handed off to the native module. The resulting fields stored in the native `AlarmData` model are:
240
280
  >
241
281
  > | Field | Type | Default | Description |
@@ -264,6 +304,7 @@ type Alarm = {
264
304
  timestamp: number;
265
305
  title: string;
266
306
  body: string;
307
+ image?: string;
267
308
  sound: string;
268
309
  vibrate: boolean;
269
310
  snoozeEnabled: boolean;
@@ -602,4 +643,3 @@ await AlarmService.scheduleAlarm({
602
643
  ```
603
644
 
604
645
  **Full Guide:** See [CUSTOM_UI_GUIDE.md](./CUSTOM_UI_GUIDE.md) for complete examples and best practices.
605
-
@@ -14,6 +14,7 @@ data class AlarmData(
14
14
  val timestamp: Long,
15
15
  val title: String,
16
16
  val body: String,
17
+ val image: String,
17
18
  val sound: String,
18
19
  val vibrate: Boolean,
19
20
  val snoozeEnabled: Boolean,
@@ -77,6 +78,7 @@ data class AlarmData(
77
78
 
78
79
  fun toJson(): String = JSONObject().apply {
79
80
  put("id", id); put("timestamp", timestamp); put("title", title); put("body", body)
81
+ put("image", image)
80
82
  put("sound", sound); put("vibrate", vibrate); put("snoozeEnabled", snoozeEnabled)
81
83
  put("snoozeMinutes", snoozeMinutes); put("repeatType", repeatType)
82
84
  put("weekdays", JSONArray(weekdays)); put("intervalMs", intervalMs)
@@ -90,6 +92,7 @@ data class AlarmData(
90
92
  putDouble("timestamp", timestamp.toDouble())
91
93
  putString("title", title)
92
94
  putString("body", body)
95
+ putString("image", image)
93
96
  putString("sound", sound)
94
97
  putBoolean("vibrate", vibrate)
95
98
  putBoolean("snoozeEnabled", snoozeEnabled)
@@ -117,6 +120,7 @@ data class AlarmData(
117
120
  putDouble("timestamp", timestamp.toDouble())
118
121
  putString("title", title)
119
122
  putString("body", body)
123
+ putString("image", image)
120
124
  putBoolean("snoozeEnabled", snoozeEnabled)
121
125
  putInt("snoozeMinutes", snoozeMinutes)
122
126
  putString("data", data)
@@ -173,6 +177,7 @@ data class AlarmData(
173
177
  timestamp = o.getLong("timestamp"),
174
178
  title = o.optString("title"),
175
179
  body = o.optString("body"),
180
+ image = o.optString("image"),
176
181
  sound = o.optString("sound", "default"),
177
182
  vibrate = o.optBoolean("vibrate", true),
178
183
  snoozeEnabled = o.optBoolean("snoozeEnabled", true),
@@ -214,6 +219,7 @@ data class AlarmData(
214
219
  timestamp = m.getDouble("timestamp").toLong(),
215
220
  title = m.str("title"),
216
221
  body = m.str("body"),
222
+ image = m.str("image"),
217
223
  sound = m.str("sound", "default"),
218
224
  vibrate = m.bool("vibrate", true),
219
225
  snoozeEnabled = m.bool("snoozeEnabled", true),
@@ -241,6 +241,7 @@ class AlarmModule(private val ctx: ReactApplicationContext) : ReactContextBaseJa
241
241
  putDouble("timestamp", System.currentTimeMillis().toDouble())
242
242
  putString("title", a.title)
243
243
  putString("body", a.body)
244
+ putString("image", a.image)
244
245
  putString("data", a.data)
245
246
  }
246
247
  ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java).emit(eventName, map)
@@ -8,6 +8,8 @@ import android.app.Service
8
8
  import android.content.Context
9
9
  import android.content.Intent
10
10
  import android.content.pm.ServiceInfo
11
+ import android.graphics.Bitmap
12
+ import android.graphics.BitmapFactory
11
13
  import android.media.AudioAttributes
12
14
  import android.media.MediaPlayer
13
15
  import android.media.RingtoneManager
@@ -22,6 +24,9 @@ import android.os.Vibrator
22
24
  import android.util.Log
23
25
  import androidx.core.app.NotificationCompat
24
26
  import androidx.core.app.ServiceCompat
27
+ import java.io.InputStream
28
+ import java.net.URL
29
+ import java.util.concurrent.Executors
25
30
 
26
31
  /**
27
32
  * Foreground service that actually rings: looping sound, vibration, a partial wake
@@ -39,6 +44,7 @@ class AlarmService : Service() {
39
44
  private var player: MediaPlayer? = null
40
45
  private var vibrator: Vibrator? = null
41
46
  private var wakeLock: PowerManager.WakeLock? = null
47
+ private val imageExecutor = Executors.newSingleThreadExecutor()
42
48
 
43
49
  override fun onBind(intent: Intent?): IBinder? = null
44
50
 
@@ -70,6 +76,7 @@ class AlarmService : Service() {
70
76
  Log.i(TAG, "Ringing alarm ${alarm.id}")
71
77
 
72
78
  startInForeground(buildNotification(alarm))
79
+ loadNotificationImage(alarm)
73
80
  acquireWakeLock()
74
81
  startSound(alarm)
75
82
  if (alarm.vibrate) startVibration()
@@ -139,6 +146,7 @@ class AlarmService : Service() {
139
146
 
140
147
  override fun onDestroy() {
141
148
  stopRinging()
149
+ imageExecutor.shutdownNow()
142
150
  super.onDestroy()
143
151
  }
144
152
 
@@ -217,7 +225,7 @@ class AlarmService : Service() {
217
225
  .build()
218
226
  }
219
227
 
220
- private fun buildNotification(alarm: AlarmData): Notification {
228
+ private fun buildNotification(alarm: AlarmData, image: Bitmap? = null): Notification {
221
229
  ensureChannel()
222
230
  val ringIntent = Intent(this, AlarmRingActivity::class.java)
223
231
  .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -235,12 +243,58 @@ class AlarmService : Service() {
235
243
  .setFullScreenIntent(fullScreen, true)
236
244
  .setContentIntent(fullScreen)
237
245
  .addAction(0, "Stop", commandPI(ACTION_DISMISS, 2))
246
+ if (image != null) {
247
+ b.setStyle(
248
+ NotificationCompat.BigPictureStyle()
249
+ .bigPicture(image)
250
+ .setBigContentTitle(alarm.title.ifBlank { "Alarm" })
251
+ .setSummaryText(alarm.body.ifBlank { "Alarm is ringing" })
252
+ )
253
+ }
238
254
  if (alarm.snoozeEnabled) {
239
255
  b.addAction(0, "Snooze", commandPI(ACTION_SNOOZE, 3))
240
256
  }
241
257
  return b.build()
242
258
  }
243
259
 
260
+ private fun loadNotificationImage(alarm: AlarmData) {
261
+ if (alarm.image.isBlank()) return
262
+ imageExecutor.execute {
263
+ try {
264
+ val bitmap = decodeImage(Uri.parse(alarm.image)) ?: return@execute
265
+ handler.post {
266
+ if (current?.id == alarm.id) {
267
+ ensureChannel().notify(NOTIFICATION_ID, buildNotification(alarm, bitmap))
268
+ }
269
+ }
270
+ } catch (e: Exception) {
271
+ Log.w(TAG, "Could not load notification image for alarm ${alarm.id}", e)
272
+ }
273
+ }
274
+ }
275
+
276
+ private fun decodeImage(uri: Uri): Bitmap? {
277
+ fun openStream(): InputStream? = when (uri.scheme?.lowercase()) {
278
+ "https" -> URL(uri.toString()).openConnection().apply {
279
+ connectTimeout = 5_000
280
+ readTimeout = 5_000
281
+ }.getInputStream()
282
+ "file", "content", "android.resource" -> contentResolver.openInputStream(uri)
283
+ else -> throw IllegalArgumentException("Unsupported alarm image URI: $uri")
284
+ }
285
+
286
+ val bounds = BitmapFactory.Options().apply { inJustDecodeBounds = true }
287
+ openStream()?.use { BitmapFactory.decodeStream(it, null, bounds) }
288
+ if (bounds.outWidth <= 0 || bounds.outHeight <= 0) return null
289
+
290
+ var sampleSize = 1
291
+ while (bounds.outWidth / sampleSize > 1024 || bounds.outHeight / sampleSize > 1024) {
292
+ sampleSize *= 2
293
+ }
294
+ val options = BitmapFactory.Options().apply { inSampleSize = sampleSize }
295
+ return openStream()?.use { BitmapFactory.decodeStream(it, null, options) }
296
+ }
297
+
244
298
  private fun commandPI(action: String, code: Int): PendingIntent =
245
299
  PendingIntent.getService(
246
300
  this, code, Intent(this, AlarmService::class.java).setAction(action),
@@ -1,5 +1,6 @@
1
1
  <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
2
 
3
+ <uses-permission android:name="android.permission.INTERNET" />
3
4
  <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
4
5
  <uses-permission android:name="android.permission.USE_EXACT_ALARM" />
5
6
  <uses-permission
package/index.ts CHANGED
@@ -11,6 +11,8 @@ export type {
11
11
  AlarmProp,
12
12
  AlarmPermission,
13
13
  AlarmEvent,
14
+ AlarmEventMap,
15
+ AlarmEventName,
14
16
  } from './src/types';
15
17
 
16
18
  export { AlarmService } from './AlarmService';
@@ -1 +1 @@
1
- export NODE_BINARY=/opt/homebrew/Cellar/node/26.10.0_1/bin/node
1
+ export NODE_BINARY=/opt/homebrew/bin/node
@@ -26,6 +26,8 @@
26
26
  <string>$(CURRENT_PROJECT_VERSION)</string>
27
27
  <key>LSRequiresIPhoneOS</key>
28
28
  <true/>
29
+ <key>NSAlarmKitUsageDescription</key>
30
+ <string>This app needs access to alarms to wake you up on time.</string>
29
31
  <key>NSAppTransportSecurity</key>
30
32
  <dict>
31
33
  <key>NSAllowsArbitraryLoads</key>
@@ -73,7 +75,5 @@
73
75
  </array>
74
76
  <key>UIViewControllerBasedStatusBarAppearance</key>
75
77
  <false/>
76
- <key>NSAlarmKitUsageDescription</key>
77
- <string>This app needs access to alarms to wake you up on time.</string>
78
78
  </dict>
79
79
  </plist>
@@ -223,10 +223,14 @@
223
223
  inputFileListPaths = (
224
224
  "${PODS_ROOT}/Target Support Files/Pods-ReactNativeAlarm/Pods-ReactNativeAlarm-frameworks-${CONFIGURATION}-input-files.xcfilelist",
225
225
  );
226
+ inputPaths = (
227
+ );
226
228
  name = "[CP] Embed Pods Frameworks";
227
229
  outputFileListPaths = (
228
230
  "${PODS_ROOT}/Target Support Files/Pods-ReactNativeAlarm/Pods-ReactNativeAlarm-frameworks-${CONFIGURATION}-output-files.xcfilelist",
229
231
  );
232
+ outputPaths = (
233
+ );
230
234
  runOnlyForDeploymentPostprocessing = 0;
231
235
  shellPath = /bin/sh;
232
236
  shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeAlarm/Pods-ReactNativeAlarm-frameworks.sh\"\n";
@@ -262,10 +266,14 @@
262
266
  inputFileListPaths = (
263
267
  "${PODS_ROOT}/Target Support Files/Pods-ReactNativeAlarm/Pods-ReactNativeAlarm-resources-${CONFIGURATION}-input-files.xcfilelist",
264
268
  );
269
+ inputPaths = (
270
+ );
265
271
  name = "[CP] Copy Pods Resources";
266
272
  outputFileListPaths = (
267
273
  "${PODS_ROOT}/Target Support Files/Pods-ReactNativeAlarm/Pods-ReactNativeAlarm-resources-${CONFIGURATION}-output-files.xcfilelist",
268
274
  );
275
+ outputPaths = (
276
+ );
269
277
  runOnlyForDeploymentPostprocessing = 0;
270
278
  shellPath = /bin/sh;
271
279
  shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeAlarm/Pods-ReactNativeAlarm-resources.sh\"\n";
@@ -7,6 +7,7 @@ struct AlarmData: Codable {
7
7
  var timestamp: Double // ms since epoch
8
8
  var title: String
9
9
  var body: String
10
+ var image: String? = nil
10
11
  var sound: String
11
12
  var vibrate: Bool
12
13
  var snoozeEnabled: Bool
@@ -78,6 +79,7 @@ struct AlarmData: Codable {
78
79
  "timestamp": timestamp,
79
80
  "title": title,
80
81
  "body": body,
82
+ "image": image ?? "",
81
83
  "sound": sound,
82
84
  "vibrate": vibrate,
83
85
  "snoozeEnabled": snoozeEnabled,
@@ -97,6 +99,7 @@ struct AlarmData: Codable {
97
99
  timestamp: (m["timestamp"] as? NSNumber)?.doubleValue ?? 0,
98
100
  title: m["title"] as? String ?? "",
99
101
  body: m["body"] as? String ?? "",
102
+ image: m["image"] as? String,
100
103
  sound: m["sound"] as? String ?? "default",
101
104
  vibrate: (m["vibrate"] as? NSNumber)?.boolValue ?? true,
102
105
  snoozeEnabled: (m["snoozeEnabled"] as? NSNumber)?.boolValue ?? true,
@@ -81,6 +81,7 @@ final class AlarmModule: RCTEventEmitter {
81
81
  "timestamp": AlarmData.now(),
82
82
  "title": a.title,
83
83
  "body": a.body,
84
+ "image": a.image ?? "",
84
85
  "data": a.data,
85
86
  ])
86
87
  }
@@ -1,5 +1,6 @@
1
1
  import Foundation
2
2
  import UserNotifications
3
+ import UniformTypeIdentifiers
3
4
 
4
5
  /// Fallback backend for devices without AlarmKit (iOS < 26, or AlarmKit denied):
5
6
  /// local notifications with Stop / Snooze actions.
@@ -78,12 +79,13 @@ final class NotificationBackend: NSObject, UNUserNotificationCenterDelegate {
78
79
  + (0..<Self.intervalOccurrences).map { base + ":i\($0)" }
79
80
  }
80
81
 
81
- private func content(for a: AlarmData) -> UNMutableNotificationContent {
82
+ private func content(for a: AlarmData, attachment: UNNotificationAttachment? = nil) -> UNMutableNotificationContent {
82
83
  let c = UNMutableNotificationContent()
83
84
  c.title = a.title.isEmpty ? "Alarm" : a.title
84
85
  c.body = a.body.isEmpty ? "Alarm is ringing" : a.body
85
86
  c.categoryIdentifier = Self.category
86
- c.userInfo = ["alarmId": a.id]
87
+ c.userInfo = ["alarmId": a.id, "image": a.image ?? ""]
88
+ if let attachment { c.attachments = [attachment] }
87
89
  c.sound = (a.sound.isEmpty || a.sound == "default")
88
90
  ? .default
89
91
  : UNNotificationSound(named: UNNotificationSoundName(rawValue: a.sound))
@@ -93,51 +95,96 @@ final class NotificationBackend: NSObject, UNUserNotificationCenterDelegate {
93
95
  return c
94
96
  }
95
97
 
96
- private func request(_ id: String, _ a: AlarmData, _ trigger: UNNotificationTrigger) -> UNNotificationRequest {
97
- UNNotificationRequest(identifier: id, content: content(for: a), trigger: trigger)
98
+ private func request(_ id: String, _ a: AlarmData, _ trigger: UNNotificationTrigger,
99
+ _ attachment: UNNotificationAttachment? = nil) -> UNNotificationRequest {
100
+ UNNotificationRequest(identifier: id, content: content(for: a, attachment: attachment), trigger: trigger)
98
101
  }
99
102
 
100
103
  func schedule(_ a: AlarmData, completion: @escaping (Error?) -> Void) {
101
104
  cancel(a.id)
102
- let cal = Calendar.current
103
- let base = Self.prefix + a.id
104
- let hm = cal.dateComponents([.hour, .minute], from: a.date)
105
- var requests: [UNNotificationRequest] = []
106
-
107
- switch a.repeatType {
108
- case "daily":
109
- var dc = DateComponents()
110
- dc.hour = hm.hour
111
- dc.minute = hm.minute
112
- requests.append(request(base, a, UNCalendarNotificationTrigger(dateMatching: dc, repeats: true)))
113
-
114
- case "weekly":
115
- let days = a.weekdays.isEmpty ? Array(1...7) : a.weekdays.filter { (1...7).contains($0) }
116
- for d in days {
105
+ prepareAttachment(for: a.image) { attachment in
106
+ let cal = Calendar.current
107
+ let base = Self.prefix + a.id
108
+ let hm = cal.dateComponents([.hour, .minute], from: a.date)
109
+ var requests: [UNNotificationRequest] = []
110
+
111
+ switch a.repeatType {
112
+ case "daily":
117
113
  var dc = DateComponents()
118
- dc.weekday = d
119
114
  dc.hour = hm.hour
120
115
  dc.minute = hm.minute
121
- requests.append(request(base + ":wd\(d)", a, UNCalendarNotificationTrigger(dateMatching: dc, repeats: true)))
116
+ requests.append(self.request(base, a, UNCalendarNotificationTrigger(dateMatching: dc, repeats: true), attachment))
117
+
118
+ case "weekly":
119
+ let days = a.weekdays.isEmpty ? Array(1...7) : a.weekdays.filter { (1...7).contains($0) }
120
+ for d in days {
121
+ var dc = DateComponents()
122
+ dc.weekday = d
123
+ dc.hour = hm.hour
124
+ dc.minute = hm.minute
125
+ requests.append(self.request(base + ":wd\(d)", a, UNCalendarNotificationTrigger(dateMatching: dc, repeats: true), attachment))
126
+ }
127
+
128
+ case "interval":
129
+ // iOS repeating interval triggers count from "now", not from `timestamp`, so
130
+ // pre-schedule the next N exact occurrences instead.
131
+ var t = a.nextTrigger()
132
+ guard t > 0 else { return completion(nil) }
133
+ for k in 0..<Self.intervalOccurrences {
134
+ requests.append(self.request(base + ":i\(k)", a, self.exactTrigger(ms: t), attachment))
135
+ t += a.intervalMs
136
+ }
137
+
138
+ default:
139
+ let next = a.nextTrigger()
140
+ guard next > 0 else { return completion(nil) } // nothing in the future, same as Android
141
+ requests.append(self.request(base, a, self.exactTrigger(ms: next), attachment))
122
142
  }
123
-
124
- case "interval":
125
- // iOS repeating interval triggers count from "now", not from `timestamp`, so
126
- // pre-schedule the next N exact occurrences instead.
127
- var t = a.nextTrigger()
128
- guard t > 0 else { return completion(nil) }
129
- for k in 0..<Self.intervalOccurrences {
130
- requests.append(request(base + ":i\(k)", a, exactTrigger(ms: t)))
131
- t += a.intervalMs
143
+
144
+ self.add(requests, completion: completion)
145
+ }
146
+ }
147
+
148
+ private func prepareAttachment(for image: String?, completion: @escaping (UNNotificationAttachment?) -> Void) {
149
+ guard let image, !image.isEmpty else { return completion(nil) }
150
+ guard let url = URL(string: image) else {
151
+ NSLog("[react-native-alarms] Invalid image URI: %@", image)
152
+ return completion(nil)
153
+ }
154
+ if url.isFileURL {
155
+ do {
156
+ completion(try UNNotificationAttachment(identifier: "alarm-image", url: url, options: nil))
157
+ } catch {
158
+ NSLog("[react-native-alarms] Could not attach alarm image: %@", error.localizedDescription)
159
+ completion(nil)
132
160
  }
133
-
134
- default:
135
- let next = a.nextTrigger()
136
- guard next > 0 else { return completion(nil) } // nothing in the future, same as Android
137
- requests.append(request(base, a, exactTrigger(ms: next)))
161
+ return
138
162
  }
139
-
140
- add(requests, completion: completion)
163
+ guard url.scheme?.lowercased() == "https" else {
164
+ NSLog("[react-native-alarms] Alarm image URI must use file:// or https://: %@", image)
165
+ return completion(nil)
166
+ }
167
+
168
+ URLSession.shared.downloadTask(with: url) { temporaryURL, response, error in
169
+ guard let temporaryURL, error == nil else {
170
+ NSLog("[react-native-alarms] Could not download alarm image: %@", error?.localizedDescription ?? "empty response")
171
+ return completion(nil)
172
+ }
173
+ let mimeType = response?.mimeType
174
+ let ext = url.pathExtension.isEmpty
175
+ ? (mimeType.flatMap { UTType(mimeType: $0)?.preferredFilenameExtension } ?? "jpg")
176
+ : url.pathExtension
177
+ let destination = FileManager.default.temporaryDirectory
178
+ .appendingPathComponent(UUID().uuidString)
179
+ .appendingPathExtension(ext)
180
+ do {
181
+ try FileManager.default.moveItem(at: temporaryURL, to: destination)
182
+ completion(try UNNotificationAttachment(identifier: "alarm-image", url: destination, options: nil))
183
+ } catch {
184
+ NSLog("[react-native-alarms] Could not attach downloaded alarm image: %@", error.localizedDescription)
185
+ completion(nil)
186
+ }
187
+ }.resume()
141
188
  }
142
189
 
143
190
  private func exactTrigger(ms: Double) -> UNCalendarNotificationTrigger {
@@ -165,8 +212,10 @@ final class NotificationBackend: NSObject, UNUserNotificationCenterDelegate {
165
212
  func snooze(_ a: AlarmData, minutes: Int) {
166
213
  center.removeDeliveredNotifications(withIdentifiers: ids(for: a.id))
167
214
  let trigger = UNTimeIntervalNotificationTrigger(timeInterval: Double(max(1, minutes)) * 60, repeats: false)
168
- center.add(request(Self.prefix + a.id + ":snooze", a, trigger))
169
- if ringing?.id == a.id { ringing = nil }
215
+ prepareAttachment(for: a.image) { attachment in
216
+ self.center.add(self.request(Self.prefix + a.id + ":snooze", a, trigger, attachment))
217
+ if self.ringing?.id == a.id { self.ringing = nil }
218
+ }
170
219
  }
171
220
 
172
221
  /// Stops the notification's sound (by clearing the delivered notification) while the
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "react-native-alarms",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "A powerful, cross-platform alarm management library for React Native",
5
5
  "private": false,
6
6
  "main": "index.ts",
7
- "types": "index.d.ts",
7
+ "types": "index.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "types": "./index.d.ts",
10
+ "types": "./index.ts",
11
11
  "default": "./index.ts"
12
12
  },
13
13
  "./service": {
14
- "types": "./AlarmService.d.ts",
14
+ "types": "./AlarmService.ts",
15
15
  "default": "./AlarmService.ts"
16
16
  },
17
17
  "./package.json": "./package.json"
@@ -1,6 +1,6 @@
1
1
  import type { TurboModule } from 'react-native';
2
2
  import { NativeModules, Platform, TurboModuleRegistry } from 'react-native';
3
- import { AlarmPermission, AlarmConfig, AlarmProp } from '../src/types';
3
+ import type { AlarmPermission, AlarmConfig, AlarmProp } from '../src/types';
4
4
 
5
5
  /**
6
6
  * Native Alarm module interface.
@@ -129,9 +129,14 @@ const LINKING_ERROR =
129
129
  "react-native-alarms: the native 'Alarm' module is not linked. " +
130
130
  Platform.select({
131
131
  ios: "Run 'pod install' in the ios folder, then rebuild the app.",
132
- default: 'Rebuild the app after installing the package.',
132
+ default:
133
+ 'Rebuild the app after installing the package. Expo Go is unsupported; use a development build.',
133
134
  });
134
135
 
136
+ export function getNativeAlarmModule(): Spec | null {
137
+ return TurboModuleRegistry.get<Spec>('Alarm') ?? NativeModules.Alarm ?? null;
138
+ }
139
+
135
140
  /**
136
141
  * Cross-platform native Alarm module.
137
142
  *
@@ -151,8 +156,7 @@ const LINKING_ERROR =
151
156
  * ```
152
157
  */
153
158
  const Alarm: Spec =
154
- TurboModuleRegistry.get<Spec>('Alarm') ??
155
- NativeModules.Alarm ??
159
+ getNativeAlarmModule() ??
156
160
  new Proxy({} as Spec, {
157
161
  get() {
158
162
  throw new Error(LINKING_ERROR);
@@ -91,6 +91,12 @@ export type AlarmConfig = {
91
91
  */
92
92
  body?: string;
93
93
 
94
+ /**
95
+ * Optional image URI (for example, a `file://` path or an `https://` URL).
96
+ * Passed to the ringing screen and shown in native notifications when supported.
97
+ */
98
+ image?: string;
99
+
94
100
  /**
95
101
  * Alarm sound.
96
102
  *
@@ -152,6 +158,9 @@ export type AlarmProp = {
152
158
  /** Alarm body. */
153
159
  body: string;
154
160
 
161
+ /** Optional image URI. */
162
+ image?: string;
163
+
155
164
  /** Sound being used. */
156
165
  sound: string;
157
166
 
@@ -241,6 +250,17 @@ export type AlarmEvent = {
241
250
  /** Alarm body. */
242
251
  body: string;
243
252
 
253
+ /** Optional image URI. */
254
+ image?: string;
255
+
244
256
  /** Custom JSON payload. */
245
257
  data: string;
246
258
  };
259
+
260
+ export type AlarmEventMap = {
261
+ onAlarmFired: AlarmEvent;
262
+ onAlarmDismissed: AlarmEvent;
263
+ onAlarmSnoozed: AlarmEvent;
264
+ };
265
+
266
+ export type AlarmEventName = keyof AlarmEventMap;
package/.bundle/config DELETED
@@ -1,2 +0,0 @@
1
- BUNDLE_PATH: "vendor/bundle"
2
- BUNDLE_FORCE_RUBY_PLATFORM: 1
package/.husky/commit-msg DELETED
@@ -1 +0,0 @@
1
- npx --no -- commitlint --edit $1
package/.husky/pre-commit DELETED
File without changes
package/AlarmService.d.ts DELETED
@@ -1,221 +0,0 @@
1
- /**
2
- * High-level alarm management API.
3
- *
4
- * Provides convenient wrapper methods for managing native alarms,
5
- * including scheduling, updating, cancelling, snoozing, and monitoring
6
- * alarm lifecycle events.
7
- *
8
- * This service can be used for alarms, scheduled reminders, and other
9
- * time-sensitive experiences supported by the native alarm module.
10
- *
11
- * @example
12
- * ```typescript
13
- * import { AlarmService } from 'react-native-alarms';
14
- *
15
- * const permission = await AlarmService.checkPermission();
16
- *
17
- * if (permission.status !== 'granted') {
18
- * await AlarmService.requestPermission();
19
- * }
20
- *
21
- * const alarmId = await AlarmService.scheduleAlarm({
22
- * timestamp: Date.now() + 60_000,
23
- * title: 'Reminder',
24
- * body: 'Your scheduled reminder is ready.',
25
- * });
26
- * ```
27
- */
28
-
29
- import type {
30
- AlarmConfig,
31
- AlarmProp,
32
- AlarmPermission,
33
- AlarmEvent,
34
- } from './src/types';
35
-
36
- /**
37
- * Callback invoked when an alarm lifecycle event occurs.
38
- *
39
- * @param alarm - Information about the alarm associated with the event.
40
- */
41
- type AlarmEventListener = (alarm: AlarmEvent) => void;
42
-
43
- /**
44
- * Static service for managing native alarms.
45
- *
46
- * All methods can be called directly without creating an instance.
47
- */
48
- export class AlarmService {
49
- /**
50
- * Initializes the alarm service and registers native event listeners.
51
- *
52
- * Call this method to begin forwarding native alarm events to
53
- * registered JavaScript listeners. Event registration methods may
54
- * also initialize the service automatically.
55
- */
56
- static initialize(): void;
57
-
58
- /**
59
- * Removes native event subscriptions created by the service.
60
- *
61
- * Registered JavaScript callbacks may remain registered, depending
62
- * on the implementation. Reinitialize the service before expecting
63
- * to receive native events again.
64
- */
65
- static cleanup(): void;
66
-
67
- /**
68
- * Registers a listener for when an alarm fires.
69
- *
70
- * @param listener - Callback invoked with the fired alarm's event data.
71
- * @returns A function that unregisters this listener.
72
- *
73
- * @example
74
- * ```typescript
75
- * const unsubscribe = AlarmService.onAlarmFired((alarm) => {
76
- * console.log('Alarm fired:', alarm);
77
- * });
78
- *
79
- * // Unsubscribe when no longer needed.
80
- * unsubscribe();
81
- * ```
82
- */
83
- static onAlarmFired(listener: AlarmEventListener): () => void;
84
-
85
- /**
86
- * Registers a listener for when an alarm is dismissed.
87
- *
88
- * @param listener - Callback invoked with the dismissed alarm's event data.
89
- * @returns A function that unregisters this listener.
90
- */
91
- static onAlarmDismissed(listener: AlarmEventListener): () => void;
92
-
93
- /**
94
- * Registers a listener for when an alarm is snoozed.
95
- *
96
- * @param listener - Callback invoked with the snoozed alarm's event data.
97
- * @returns A function that unregisters this listener.
98
- */
99
- static onAlarmSnoozed(listener: AlarmEventListener): () => void;
100
-
101
- /**
102
- * Checks the current alarm permission status.
103
- *
104
- * @returns A promise resolving to the current permission information.
105
- * @throws If the native alarm module is unavailable.
106
- */
107
- static checkPermission(): Promise<AlarmPermission>;
108
-
109
- /**
110
- * Requests the permissions required by the native alarm functionality.
111
- *
112
- * @returns A promise resolving to the resulting permission information.
113
- * @throws If the native alarm module is unavailable.
114
- */
115
- static requestPermission(): Promise<AlarmPermission>;
116
-
117
- /**
118
- * Schedules a new alarm.
119
- *
120
- * @param config - Configuration describing the alarm to schedule.
121
- * @returns A promise resolving to the ID assigned to the scheduled alarm.
122
- * @throws If the native alarm module is unavailable or scheduling fails.
123
- */
124
- static scheduleAlarm(config: AlarmConfig): Promise<string>;
125
-
126
- /**
127
- * Updates an existing scheduled alarm.
128
- *
129
- * @param alarmId - ID of the alarm to update.
130
- * @param config - Updated configuration for the alarm.
131
- * @returns A promise resolving to `true` if the update succeeds.
132
- * @throws If the native alarm module is unavailable or the update fails.
133
- */
134
- static updateAlarm(alarmId: string, config: AlarmConfig): Promise<boolean>;
135
-
136
- /**
137
- * Cancels a scheduled alarm.
138
- *
139
- * @param alarmId - ID of the alarm to cancel.
140
- * @returns A promise resolving to `true` if the alarm was cancelled.
141
- * @throws If the native alarm module is unavailable.
142
- */
143
- static cancelAlarm(alarmId: string): Promise<boolean>;
144
-
145
- /**
146
- * Cancels all scheduled alarms.
147
- *
148
- * @returns A promise that resolves when the cancellation operation completes.
149
- * @throws If the native alarm module is unavailable.
150
- */
151
- static cancelAllAlarms(): Promise<void>;
152
-
153
- /**
154
- * Stops a currently ringing alarm.
155
- *
156
- * @param alarmId - ID of the ringing alarm to stop.
157
- * @returns A promise that resolves when the stop operation completes.
158
- * @throws If the native alarm module is unavailable.
159
- */
160
- static stopAlarm(alarmId: string): Promise<void>;
161
-
162
- /**
163
- * Snoozes a currently ringing alarm.
164
- *
165
- * @param alarmId - ID of the alarm to snooze.
166
- * @param minutes - Optional snooze duration in minutes. If omitted,
167
- * the native implementation or alarm configuration determines the default.
168
- * @returns A promise that resolves when the snooze operation completes.
169
- * @throws If the native alarm module is unavailable or snoozing fails.
170
- */
171
- static snoozeAlarm(alarmId: string, minutes?: number): Promise<void>;
172
-
173
- /**
174
- * Retrieves the currently ringing alarm, if one exists.
175
- *
176
- * @returns A promise resolving to the ringing alarm, or `null` if none exists.
177
- * @throws If the native alarm module is unavailable.
178
- */
179
- static getRingingAlarm(): Promise<AlarmProp | null>;
180
-
181
- /**
182
- * Retrieves a specific alarm by its ID.
183
- *
184
- * @param alarmId - ID of the alarm to retrieve.
185
- * @returns A promise resolving to the alarm, or `null` if it is not found.
186
- * @throws If the native alarm module is unavailable.
187
- */
188
- static getAlarm(alarmId: string): Promise<AlarmProp | null>;
189
-
190
- /**
191
- * Retrieves all alarms known to the native alarm module.
192
- *
193
- * @returns A promise resolving to an array of alarm records.
194
- * @throws If the native alarm module is unavailable.
195
- */
196
- static getAllAlarms(): Promise<AlarmProp[]>;
197
-
198
- /**
199
- * Checks whether an alarm is currently scheduled.
200
- *
201
- * @param alarmId - ID of the alarm to check.
202
- * @returns A promise resolving to `true` if the alarm is scheduled;
203
- * otherwise, `false`.
204
- * @throws If the native alarm module is unavailable.
205
- */
206
- static isScheduled(alarmId: string): Promise<boolean>;
207
-
208
- /**
209
- * Opens the device manufacturer's Auto Start settings on Android,
210
- * where supported, to help users configure background launch behavior.
211
- *
212
- * On iOS, this method resolves to `false`.
213
- *
214
- * @returns A promise resolving to `true` if the settings screen was opened;
215
- * otherwise, `false`.
216
- * @throws If the native alarm module is unavailable.
217
- */
218
- static openAutoStartSettings(): Promise<boolean>;
219
- }
220
-
221
- export default AlarmService;
@@ -1 +0,0 @@
1
- export default { extends: ['@commitlint/config-conventional'] };
package/index.d.ts DELETED
@@ -1,20 +0,0 @@
1
- /**
2
- * React Native Alarms - Type definitions
3
- * Cross-platform alarm management for React Native
4
- */
5
-
6
- import type { Spec } from './specs/NativeAlarm';
7
-
8
- export type {
9
- AlarmRepeat,
10
- AlarmConfig,
11
- AlarmProp,
12
- AlarmPermission,
13
- AlarmEvent,
14
- } from './src/types';
15
-
16
- export { AlarmService } from './AlarmService';
17
-
18
- declare const Alarm: Spec;
19
-
20
- export default Alarm;
Binary file
Binary file
Binary file
Binary file