expo-notifications 1.0.0-canary-20240814-ce0f7d5 → 1.0.0-canary-20240912-1059f85

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 (32) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/android/build.gradle +11 -0
  3. package/android/src/main/java/expo/modules/notifications/Exceptions.kt +0 -13
  4. package/android/src/main/java/expo/modules/notifications/notifications/JSONNotificationContentBuilder.java +1 -1
  5. package/android/src/main/java/expo/modules/notifications/notifications/NotificationSerializer.java +25 -13
  6. package/android/src/main/java/expo/modules/notifications/notifications/debug/DebugLogging.kt +1 -0
  7. package/android/src/main/java/expo/modules/notifications/notifications/handling/NotificationsHandler.kt +1 -2
  8. package/android/src/main/java/expo/modules/notifications/notifications/handling/SingleNotificationHandlerTask.java +2 -2
  9. package/android/src/main/java/expo/modules/notifications/notifications/model/triggers/FirebaseNotificationTrigger.kt +43 -0
  10. package/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ChannelAwareNotificationBuilder.java +6 -7
  11. package/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ExpoNotificationBuilder.java +0 -3
  12. package/android/src/main/java/expo/modules/notifications/service/NotificationsService.kt +48 -44
  13. package/android/src/main/java/expo/modules/notifications/service/delegates/ExpoPresentationDelegate.kt +17 -3
  14. package/build/getAllScheduledNotificationsAsync.d.ts.map +1 -1
  15. package/build/getAllScheduledNotificationsAsync.js +2 -1
  16. package/build/getAllScheduledNotificationsAsync.js.map +1 -1
  17. package/build/getPresentedNotificationsAsync.d.ts.map +1 -1
  18. package/build/getPresentedNotificationsAsync.js +2 -1
  19. package/build/getPresentedNotificationsAsync.js.map +1 -1
  20. package/build/utils/mapNotificationResponse.d.ts +40 -7
  21. package/build/utils/mapNotificationResponse.d.ts.map +1 -1
  22. package/build/utils/mapNotificationResponse.js +31 -6
  23. package/build/utils/mapNotificationResponse.js.map +1 -1
  24. package/package.json +7 -7
  25. package/src/getAllScheduledNotificationsAsync.ts +4 -1
  26. package/src/getPresentedNotificationsAsync.ts +4 -1
  27. package/src/utils/mapNotificationResponse.ts +39 -9
  28. package/android/src/main/java/expo/modules/notifications/notifications/interfaces/NotificationPresentationEffect.java +0 -11
  29. package/android/src/main/java/expo/modules/notifications/notifications/interfaces/NotificationPresentationEffectsManager.java +0 -7
  30. package/android/src/main/java/expo/modules/notifications/notifications/model/triggers/FirebaseNotificationTrigger.java +0 -61
  31. package/android/src/main/java/expo/modules/notifications/notifications/presentation/ExpoNotificationPresentationEffectsManager.java +0 -63
  32. package/android/src/main/java/expo/modules/notifications/notifications/presentation/effects/BaseNotificationEffect.java +0 -53
package/CHANGELOG.md CHANGED
@@ -10,8 +10,11 @@
10
10
 
11
11
  ### 🐛 Bug fixes
12
12
 
13
+ - [Android] Take `channelId` into account when presenting notifications. ([#31201](https://github.com/expo/expo/pull/31201) by [@vonovak](https://github.com/vonovak))
13
14
  - Add missing `react` and `react-native` peer dependencies for isolated modules. ([#30478](https://github.com/expo/expo/pull/30478) by [@byCedric](https://github.com/byCedric))
14
15
  - [Android] Eliminate unsupported types when processing notification intents from onCreate/onNewIntent. ([#30750](https://github.com/expo/expo/pull/30750) by [@douglowder](https://github.com/douglowder))
16
+ - [Android] Fix content.data in scheduled notifications surfaced to JS. ([#31048](https://github.com/expo/expo/pull/31048) by [@douglowder](https://github.com/douglowder))
17
+ - [Android] fix local notifications with null trigger. ([#31157](https://github.com/expo/expo/pull/31157) by [@douglowder](https://github.com/douglowder))
15
18
 
16
19
  ### 💡 Others
17
20
 
@@ -20,6 +20,17 @@ android {
20
20
  buildFeatures {
21
21
  buildConfig true
22
22
  }
23
+ testOptions {
24
+ unitTests.all { test ->
25
+ testLogging {
26
+ outputs.upToDateWhen { false }
27
+ events "passed", "failed", "skipped", "standardError"
28
+ showCauses true
29
+ showExceptions true
30
+ showStandardStreams true
31
+ }
32
+ }
33
+ }
23
34
  }
24
35
 
25
36
  dependencies {
@@ -7,16 +7,3 @@ class ModuleNotFoundException(moduleClass: KClass<*>) :
7
7
  CodedException(message = "$moduleClass module not found")
8
8
 
9
9
  class NotificationWasAlreadyHandledException(val id: String) : CodedException("Failed to handle notification $id, it has already been handled.")
10
-
11
- fun expo.modules.kotlin.Promise.toLegacyPromise(): expo.modules.core.Promise {
12
- val newPromise = this
13
- return object : expo.modules.core.Promise {
14
- override fun resolve(value: Any?) {
15
- newPromise.resolve(value)
16
- }
17
-
18
- override fun reject(c: String?, m: String?, e: Throwable?) {
19
- newPromise.reject(c ?: "unknown", m, e)
20
- }
21
- }
22
- }
@@ -100,7 +100,7 @@ public class JSONNotificationContentBuilder extends NotificationContent.Builder
100
100
  // do nothing
101
101
  }
102
102
 
103
- // do not play a default sound only if the value is a valid Uri
103
+ // play a default sound only if the value is NOT a valid Uri
104
104
  return getSound(payload) == null;
105
105
  }
106
106
 
@@ -23,6 +23,7 @@ import java.util.Map;
23
23
  import java.util.Set;
24
24
 
25
25
  import expo.modules.notifications.notifications.interfaces.NotificationTrigger;
26
+ import expo.modules.notifications.notifications.interfaces.SchedulableNotificationTrigger;
26
27
  import expo.modules.notifications.notifications.model.Notification;
27
28
  import expo.modules.notifications.notifications.model.NotificationContent;
28
29
  import expo.modules.notifications.notifications.model.NotificationRequest;
@@ -60,19 +61,30 @@ public class NotificationSerializer {
60
61
  serializedRequest.putBundle("trigger", toBundle(request.getTrigger()));
61
62
  Bundle content = toBundle(request.getContent());
62
63
  Bundle existingContentData = content.getBundle("data");
63
- if (existingContentData == null && request.getTrigger() instanceof FirebaseNotificationTrigger trigger) {
64
- RemoteMessage message = trigger.getRemoteMessage();
65
- RemoteMessage.Notification notification = message.getNotification();
66
- Map<String, String> data = message.getData();
67
- String dataBody = data.get("body");
68
- String notificationBody = notification != null ? notification.getBody() : null;
69
- if (isValidJSONString(dataBody) && notificationBody != null && notificationBody.equals(data.get("message"))) {
70
- // Expo sends notification.body as data.message, and JSON stringifies data.body
71
- content.putString("dataString", dataBody);
72
- } else {
73
- // The message was sent directly from Firebase or some other service,
74
- // and we copy the data as is
75
- content.putBundle("data", toBundle(data));
64
+ if (existingContentData == null) {
65
+ if(request.getTrigger() instanceof FirebaseNotificationTrigger trigger) {
66
+ RemoteMessage message = trigger.getRemoteMessage();
67
+ RemoteMessage.Notification notification = message.getNotification();
68
+ Map<String, String> data = message.getData();
69
+ String dataBody = data.get("body");
70
+ String notificationBody = notification != null ? notification.getBody() : null;
71
+ if (isValidJSONString(dataBody) && notificationBody != null && notificationBody.equals(data.get("message"))) {
72
+ // Expo sends notification.body as data.message, and JSON stringifies data.body
73
+ content.putString("dataString", dataBody);
74
+ } else {
75
+ // The message was sent directly from Firebase or some other service,
76
+ // and we copy the data as is
77
+ content.putBundle("data", toBundle(data));
78
+ }
79
+ } else if(
80
+ request.getTrigger() instanceof SchedulableNotificationTrigger ||
81
+ request.getTrigger() == null
82
+ ) {
83
+ JSONObject body = request.getContent().getBody();
84
+ if (body != null) {
85
+ // Expo sends notification.body as data.message, and JSON stringifies data.body
86
+ content.putString("dataString", body.toString());
87
+ }
76
88
  }
77
89
  }
78
90
  serializedRequest.putBundle("content", content);
@@ -74,6 +74,7 @@ object DebugLogging {
74
74
  notification.notificationRequest.content.subtitle: ${notification.notificationRequest.content.subtitle}
75
75
  notification.notificationRequest.content.text: ${notification.notificationRequest.content.text}
76
76
  notification.notificationRequest.content.sound: ${notification.notificationRequest.content.sound}
77
+ notification.notificationRequest.content.channelID: ${notification.notificationRequest.trigger.notificationChannel}
77
78
  notification.notificationRequest.content.body: ${notification.notificationRequest.content.body}
78
79
  notification.notificationRequest.content.color: ${notification.notificationRequest.content.color}
79
80
  notification.notificationRequest.content.vibrationPattern: ${notification.notificationRequest.content.vibrationPattern.contentToString()}
@@ -13,7 +13,6 @@ import expo.modules.notifications.notifications.interfaces.NotificationListener
13
13
  import expo.modules.notifications.notifications.interfaces.NotificationManager
14
14
  import expo.modules.notifications.notifications.model.Notification
15
15
  import expo.modules.notifications.notifications.model.NotificationBehavior
16
- import expo.modules.notifications.toLegacyPromise
17
16
 
18
17
  class NotificationBehaviourRecord : Record {
19
18
  @Field
@@ -102,7 +101,7 @@ open class NotificationsHandler : Module(), NotificationListener {
102
101
  with(behavior) {
103
102
  task.handleResponse(
104
103
  NotificationBehavior(shouldShowAlert, shouldPlaySound, shouldSetBadge, priority),
105
- promise.toLegacyPromise()
104
+ promise
106
105
  )
107
106
  }
108
107
  }
@@ -5,7 +5,7 @@ import android.os.Bundle;
5
5
  import android.os.Handler;
6
6
  import android.os.ResultReceiver;
7
7
 
8
- import expo.modules.core.Promise;
8
+ import expo.modules.kotlin.Promise;
9
9
  import expo.modules.core.interfaces.services.EventEmitter;
10
10
  import expo.modules.notifications.notifications.NotificationSerializer;
11
11
  import expo.modules.notifications.notifications.model.Notification;
@@ -98,7 +98,7 @@ public class SingleNotificationHandlerTask {
98
98
  protected void onReceiveResult(int resultCode, Bundle resultData) {
99
99
  super.onReceiveResult(resultCode, resultData);
100
100
  if (resultCode == NotificationsService.SUCCESS_CODE) {
101
- promise.resolve(null);
101
+ promise.resolve();
102
102
  } else {
103
103
  Exception e = (Exception) resultData.getSerializable(NotificationsService.EXCEPTION_KEY);
104
104
  promise.reject("ERR_NOTIFICATION_PRESENTATION_FAILED", "Notification presentation failed.", e);
@@ -0,0 +1,43 @@
1
+ package expo.modules.notifications.notifications.model.triggers
2
+
3
+ import android.os.Build
4
+ import android.os.Parcel
5
+ import android.os.Parcelable
6
+ import androidx.annotation.RequiresApi
7
+ import com.google.firebase.messaging.RemoteMessage
8
+ import expo.modules.notifications.notifications.interfaces.NotificationTrigger
9
+
10
+ /**
11
+ * A trigger representing an incoming remote Firebase notification.
12
+ */
13
+ class FirebaseNotificationTrigger(private val remoteMessage: RemoteMessage) : NotificationTrigger {
14
+
15
+ private constructor(parcel: Parcel) : this(
16
+ parcel.readParcelable(FirebaseNotificationTrigger::class.java.classLoader)
17
+ ?: throw IllegalArgumentException("RemoteMessage from readParcelable must not be null")
18
+ )
19
+
20
+ fun getRemoteMessage(): RemoteMessage = remoteMessage
21
+
22
+ @RequiresApi(api = Build.VERSION_CODES.O)
23
+ override fun getNotificationChannel(): String? {
24
+ val channelId = remoteMessage.notification?.channelId ?: remoteMessage.data["channelId"]
25
+ return channelId ?: super.getNotificationChannel()
26
+ }
27
+
28
+ override fun describeContents(): Int {
29
+ return 0
30
+ }
31
+
32
+ override fun writeToParcel(dest: Parcel, flags: Int) {
33
+ dest.writeParcelable(remoteMessage, 0)
34
+ }
35
+
36
+ companion object {
37
+ @JvmField
38
+ val CREATOR = object : Parcelable.Creator<FirebaseNotificationTrigger> {
39
+ override fun createFromParcel(parcel: Parcel) = FirebaseNotificationTrigger(parcel)
40
+ override fun newArray(size: Int) = arrayOfNulls<FirebaseNotificationTrigger>(size)
41
+ }
42
+ }
43
+ }
@@ -53,19 +53,18 @@ public abstract class ChannelAwareNotificationBuilder extends BaseNotificationBu
53
53
  return getFallbackNotificationChannel().getId();
54
54
  }
55
55
 
56
- String channelId = trigger.getNotificationChannel();
57
- if (channelId == null) {
56
+ String requestedChannelId = trigger.getNotificationChannel();
57
+ if (requestedChannelId == null) {
58
58
  return getFallbackNotificationChannel().getId();
59
59
  }
60
60
 
61
- NotificationsChannelManager manager = getNotificationsChannelManager();
62
- NotificationChannel channel = manager.getNotificationChannel(channelId);
63
- if (channel == null) {
64
- Log.e("notifications", String.format("Channel '%s' doesn't exists. Fallback to '%s' channel", channelId, FALLBACK_CHANNEL_ID));
61
+ NotificationChannel channelForRequestedId = getNotificationsChannelManager().getNotificationChannel(requestedChannelId);
62
+ if (channelForRequestedId == null) {
63
+ Log.e("notifications", String.format("Channel '%s' doesn't exists. Fallback to '%s' channel", requestedChannelId, FALLBACK_CHANNEL_ID));
65
64
  return getFallbackNotificationChannel().getId();
66
65
  }
67
66
 
68
- return channel.getId();
67
+ return channelForRequestedId.getId();
69
68
  }
70
69
 
71
70
  @NonNull
@@ -7,7 +7,6 @@ import android.content.pm.PackageManager;
7
7
  import android.content.res.Resources;
8
8
  import android.graphics.Bitmap;
9
9
  import android.graphics.BitmapFactory;
10
- import android.os.Build;
11
10
  import android.os.Bundle;
12
11
  import android.os.Parcel;
13
12
  import android.provider.Settings;
@@ -34,8 +33,6 @@ public class ExpoNotificationBuilder extends ChannelAwareNotificationBuilder {
34
33
  public static final String EXTRAS_MARSHALLED_NOTIFICATION_REQUEST_KEY = "expo.notification_request";
35
34
  private static final String EXTRAS_BODY_KEY = "body";
36
35
 
37
- private static final long[] NO_VIBRATE_PATTERN = new long[]{0, 0};
38
-
39
36
  public ExpoNotificationBuilder(Context context) {
40
37
  super(context);
41
38
  }
@@ -10,6 +10,7 @@ import android.net.Uri
10
10
  import android.os.*
11
11
  import android.util.Log
12
12
  import androidx.core.app.RemoteInput
13
+ import expo.modules.core.interfaces.DoNotStrip
13
14
  import expo.modules.notifications.BuildConfig
14
15
  import expo.modules.notifications.notifications.model.*
15
16
  import expo.modules.notifications.service.delegates.ExpoCategoriesDelegate
@@ -86,9 +87,9 @@ open class NotificationsService : BroadcastReceiver() {
86
87
  fun getAllPresented(context: Context, receiver: ResultReceiver? = null) {
87
88
  doWork(
88
89
  context,
89
- Intent(NOTIFICATION_EVENT_ACTION, getUriBuilder().build()).also {
90
- it.putExtra(EVENT_TYPE_KEY, GET_ALL_DISPLAYED_TYPE)
91
- it.putExtra(RECEIVER_KEY, receiver)
90
+ Intent(NOTIFICATION_EVENT_ACTION, getUriBuilder().build()).apply {
91
+ putExtra(EVENT_TYPE_KEY, GET_ALL_DISPLAYED_TYPE)
92
+ putExtra(RECEIVER_KEY, receiver)
92
93
  }
93
94
  )
94
95
  }
@@ -105,11 +106,11 @@ open class NotificationsService : BroadcastReceiver() {
105
106
  val data = getUriBuilderForIdentifier(notification.notificationRequest.identifier).appendPath("present").build()
106
107
  doWork(
107
108
  context,
108
- Intent(NOTIFICATION_EVENT_ACTION, data).also { intent ->
109
- intent.putExtra(EVENT_TYPE_KEY, PRESENT_TYPE)
110
- intent.putExtra(NOTIFICATION_KEY, notification)
111
- intent.putExtra(NOTIFICATION_BEHAVIOR_KEY, behavior)
112
- intent.putExtra(RECEIVER_KEY, receiver)
109
+ Intent(NOTIFICATION_EVENT_ACTION, data).apply {
110
+ putExtra(EVENT_TYPE_KEY, PRESENT_TYPE)
111
+ putExtra(NOTIFICATION_KEY, notification)
112
+ putExtra(NOTIFICATION_BEHAVIOR_KEY, behavior)
113
+ putExtra(RECEIVER_KEY, receiver)
113
114
  }
114
115
  )
115
116
  }
@@ -125,10 +126,10 @@ open class NotificationsService : BroadcastReceiver() {
125
126
  val data = getUriBuilderForIdentifier(notification.notificationRequest.identifier).appendPath("receive").build()
126
127
  doWork(
127
128
  context,
128
- Intent(NOTIFICATION_EVENT_ACTION, data).also { intent ->
129
- intent.putExtra(EVENT_TYPE_KEY, RECEIVE_TYPE)
130
- intent.putExtra(NOTIFICATION_KEY, notification)
131
- intent.putExtra(RECEIVER_KEY, receiver)
129
+ Intent(NOTIFICATION_EVENT_ACTION, data).apply {
130
+ putExtra(EVENT_TYPE_KEY, RECEIVE_TYPE)
131
+ putExtra(NOTIFICATION_KEY, notification)
132
+ putExtra(RECEIVER_KEY, receiver)
132
133
  }
133
134
  )
134
135
  }
@@ -144,10 +145,10 @@ open class NotificationsService : BroadcastReceiver() {
144
145
  val data = getUriBuilder().appendPath("dismiss").build()
145
146
  doWork(
146
147
  context,
147
- Intent(NOTIFICATION_EVENT_ACTION, data).also { intent ->
148
- intent.putExtra(EVENT_TYPE_KEY, DISMISS_SELECTED_TYPE)
149
- intent.putExtra(IDENTIFIERS_KEY, identifiers)
150
- intent.putExtra(RECEIVER_KEY, receiver)
148
+ Intent(NOTIFICATION_EVENT_ACTION, data).apply {
149
+ putExtra(EVENT_TYPE_KEY, DISMISS_SELECTED_TYPE)
150
+ putExtra(IDENTIFIERS_KEY, identifiers)
151
+ putExtra(RECEIVER_KEY, receiver)
151
152
  }
152
153
  )
153
154
  }
@@ -162,9 +163,9 @@ open class NotificationsService : BroadcastReceiver() {
162
163
  val data = getUriBuilder().appendPath("dismiss").build()
163
164
  doWork(
164
165
  context,
165
- Intent(NOTIFICATION_EVENT_ACTION, data).also { intent ->
166
- intent.putExtra(EVENT_TYPE_KEY, DISMISS_ALL_TYPE)
167
- intent.putExtra(RECEIVER_KEY, receiver)
166
+ Intent(NOTIFICATION_EVENT_ACTION, data).apply {
167
+ putExtra(EVENT_TYPE_KEY, DISMISS_ALL_TYPE)
168
+ putExtra(RECEIVER_KEY, receiver)
168
169
  }
169
170
  )
170
171
  }
@@ -177,8 +178,8 @@ open class NotificationsService : BroadcastReceiver() {
177
178
  fun handleDropped(context: Context) {
178
179
  doWork(
179
180
  context,
180
- Intent(NOTIFICATION_EVENT_ACTION).also { intent ->
181
- intent.putExtra(EVENT_TYPE_KEY, DROPPED_TYPE)
181
+ Intent(NOTIFICATION_EVENT_ACTION).apply {
182
+ putExtra(EVENT_TYPE_KEY, DROPPED_TYPE)
182
183
  }
183
184
  )
184
185
  }
@@ -196,9 +197,9 @@ open class NotificationsService : BroadcastReceiver() {
196
197
  getUriBuilder()
197
198
  .appendPath("categories")
198
199
  .build()
199
- ).also {
200
- it.putExtra(EVENT_TYPE_KEY, GET_CATEGORIES_TYPE)
201
- it.putExtra(RECEIVER_KEY, receiver)
200
+ ).apply {
201
+ putExtra(EVENT_TYPE_KEY, GET_CATEGORIES_TYPE)
202
+ putExtra(RECEIVER_KEY, receiver)
202
203
  }
203
204
  )
204
205
  }
@@ -218,10 +219,10 @@ open class NotificationsService : BroadcastReceiver() {
218
219
  .appendPath("categories")
219
220
  .appendPath(category.identifier)
220
221
  .build()
221
- ).also {
222
- it.putExtra(EVENT_TYPE_KEY, SET_CATEGORY_TYPE)
223
- it.putExtra(NOTIFICATION_CATEGORY_KEY, category as Parcelable)
224
- it.putExtra(RECEIVER_KEY, receiver)
222
+ ).apply {
223
+ putExtra(EVENT_TYPE_KEY, SET_CATEGORY_TYPE)
224
+ putExtra(NOTIFICATION_CATEGORY_KEY, category as Parcelable)
225
+ putExtra(RECEIVER_KEY, receiver)
225
226
  }
226
227
  )
227
228
  }
@@ -241,10 +242,10 @@ open class NotificationsService : BroadcastReceiver() {
241
242
  .appendPath("categories")
242
243
  .appendPath(identifier)
243
244
  .build()
244
- ).also {
245
- it.putExtra(EVENT_TYPE_KEY, DELETE_CATEGORY_TYPE)
246
- it.putExtra(IDENTIFIER_KEY, identifier)
247
- it.putExtra(RECEIVER_KEY, receiver)
245
+ ).apply {
246
+ putExtra(EVENT_TYPE_KEY, DELETE_CATEGORY_TYPE)
247
+ putExtra(IDENTIFIER_KEY, identifier)
248
+ putExtra(RECEIVER_KEY, receiver)
248
249
  }
249
250
  )
250
251
  }
@@ -266,12 +267,13 @@ open class NotificationsService : BroadcastReceiver() {
266
267
  }
267
268
 
268
269
  /**
269
- * Fetches scheduled notification asynchronously.
270
+ * Fetches scheduled notification asynchronously. Used in Expo Go's ScopedNotificationScheduler.kt
270
271
  *
271
272
  * @param context Context this is being called from
272
273
  * @param identifier Identifier of the notification to be fetched
273
274
  * @param resultReceiver Receiver to be called with the results
274
275
  */
276
+ @DoNotStrip
275
277
  fun getScheduledNotification(context: Context, identifier: String, resultReceiver: ResultReceiver? = null) {
276
278
  doWork(
277
279
  context,
@@ -305,10 +307,10 @@ open class NotificationsService : BroadcastReceiver() {
305
307
  .appendPath("scheduled")
306
308
  .appendPath(notificationRequest.identifier)
307
309
  .build()
308
- ).also { intent ->
309
- intent.putExtra(EVENT_TYPE_KEY, SCHEDULE_TYPE)
310
- intent.putExtra(NOTIFICATION_REQUEST_KEY, notificationRequest as Parcelable)
311
- intent.putExtra(RECEIVER_KEY, resultReceiver)
310
+ ).apply {
311
+ putExtra(EVENT_TYPE_KEY, SCHEDULE_TYPE)
312
+ putExtra(NOTIFICATION_REQUEST_KEY, notificationRequest as Parcelable)
313
+ putExtra(RECEIVER_KEY, resultReceiver)
312
314
  }
313
315
  )
314
316
  }
@@ -338,10 +340,10 @@ open class NotificationsService : BroadcastReceiver() {
338
340
  getUriBuilder()
339
341
  .appendPath("scheduled")
340
342
  .build()
341
- ).also { intent ->
342
- intent.putExtra(EVENT_TYPE_KEY, REMOVE_SELECTED_TYPE)
343
- intent.putExtra(IDENTIFIERS_KEY, identifiers.toTypedArray())
344
- intent.putExtra(RECEIVER_KEY, resultReceiver)
343
+ ).apply {
344
+ putExtra(EVENT_TYPE_KEY, REMOVE_SELECTED_TYPE)
345
+ putExtra(IDENTIFIERS_KEY, identifiers.toTypedArray())
346
+ putExtra(RECEIVER_KEY, resultReceiver)
345
347
  }
346
348
  )
347
349
  }
@@ -355,9 +357,9 @@ open class NotificationsService : BroadcastReceiver() {
355
357
  fun removeAllScheduledNotifications(context: Context, resultReceiver: ResultReceiver? = null) {
356
358
  doWork(
357
359
  context,
358
- Intent(NOTIFICATION_EVENT_ACTION).also { intent ->
359
- intent.putExtra(EVENT_TYPE_KEY, REMOVE_ALL_TYPE)
360
- intent.putExtra(RECEIVER_KEY, resultReceiver)
360
+ Intent(NOTIFICATION_EVENT_ACTION).apply {
361
+ putExtra(EVENT_TYPE_KEY, REMOVE_ALL_TYPE)
362
+ putExtra(RECEIVER_KEY, resultReceiver)
361
363
  }
362
364
  )
363
365
  }
@@ -511,6 +513,8 @@ open class NotificationsService : BroadcastReceiver() {
511
513
  return response
512
514
  }
513
515
 
516
+ // this is used by Expo Go's Kernel.kt
517
+ @DoNotStrip
514
518
  fun getNotificationResponseFromOpenIntent(intent: Intent): NotificationResponse? {
515
519
  intent.getByteArrayExtra(NOTIFICATION_RESPONSE_KEY)?.let { return unmarshalObject(NotificationResponse.CREATOR, it) }
516
520
  intent.getByteArrayExtra(TEXT_INPUT_NOTIFICATION_RESPONSE_KEY)?.let { return unmarshalObject(TextInputNotificationResponse.CREATOR, it) }
@@ -4,6 +4,7 @@ import android.app.NotificationManager
4
4
  import android.content.Context
5
5
  import android.media.RingtoneManager
6
6
  import android.net.Uri
7
+ import android.os.Build
7
8
  import android.os.Bundle
8
9
  import android.os.Parcel
9
10
  import android.provider.Settings
@@ -21,11 +22,13 @@ import expo.modules.notifications.notifications.presentation.builders.ExpoNotifi
21
22
  import expo.modules.notifications.service.interfaces.PresentationDelegate
22
23
  import org.json.JSONException
23
24
  import org.json.JSONObject
24
- import java.util.*
25
+ import java.util.Date
25
26
 
26
27
  open class ExpoPresentationDelegate(
27
- protected val context: Context
28
+ protected val context: Context,
29
+ private val notificationManager: NotificationManagerCompat = NotificationManagerCompat.from(context)
28
30
  ) : PresentationDelegate {
31
+
29
32
  companion object {
30
33
  protected const val ANDROID_NOTIFICATION_ID = 0
31
34
 
@@ -87,9 +90,10 @@ open class ExpoPresentationDelegate(
87
90
  override fun presentNotification(notification: Notification, behavior: NotificationBehavior?) {
88
91
  if (behavior != null && !behavior.shouldShowAlert()) {
89
92
  if (behavior.shouldPlaySound()) {
93
+ val sound = getNotificationSoundUri(notification) ?: Settings.System.DEFAULT_NOTIFICATION_URI
90
94
  RingtoneManager.getRingtone(
91
95
  context,
92
- notification.notificationRequest.content.sound ?: Settings.System.DEFAULT_NOTIFICATION_URI
96
+ sound
93
97
  ).play()
94
98
  }
95
99
  return
@@ -101,6 +105,16 @@ open class ExpoPresentationDelegate(
101
105
  )
102
106
  }
103
107
 
108
+ private fun getNotificationSoundUri(notification: Notification): Uri? {
109
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
110
+ notification.notificationRequest.trigger.notificationChannel?.let {
111
+ notificationManager.getNotificationChannel(it)?.sound
112
+ }
113
+ } else {
114
+ notification.notificationRequest.content.sound
115
+ }
116
+ }
117
+
104
118
  protected open fun getNotifyId(request: NotificationRequest?): Int {
105
119
  return ANDROID_NOTIFICATION_ID
106
120
  }
@@ -1 +1 @@
1
- {"version":3,"file":"getAllScheduledNotificationsAsync.d.ts","sourceRoot":"","sources":["../src/getAllScheduledNotificationsAsync.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D;;;;GAIG;AACH,wBAA8B,iCAAiC,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAMhG"}
1
+ {"version":3,"file":"getAllScheduledNotificationsAsync.d.ts","sourceRoot":"","sources":["../src/getAllScheduledNotificationsAsync.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAG5D;;;;GAIG;AACH,wBAA8B,iCAAiC,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAQhG"}
@@ -1,5 +1,6 @@
1
1
  import { UnavailabilityError } from 'expo-modules-core';
2
2
  import NotificationScheduler from './NotificationScheduler';
3
+ import { mapNotificationRequest } from './utils/mapNotificationResponse';
3
4
  /**
4
5
  * Fetches information about all scheduled notifications.
5
6
  * @return Returns a Promise resolving to an array of objects conforming to the [`Notification`](#notification) interface.
@@ -9,6 +10,6 @@ export default async function getAllScheduledNotificationsAsync() {
9
10
  if (!NotificationScheduler.getAllScheduledNotificationsAsync) {
10
11
  throw new UnavailabilityError('Notifications', 'getAllScheduledNotificationsAsync');
11
12
  }
12
- return await NotificationScheduler.getAllScheduledNotificationsAsync();
13
+ return (await NotificationScheduler.getAllScheduledNotificationsAsync()).map((request) => mapNotificationRequest(request));
13
14
  }
14
15
  //# sourceMappingURL=getAllScheduledNotificationsAsync.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"getAllScheduledNotificationsAsync.js","sourceRoot":"","sources":["../src/getAllScheduledNotificationsAsync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,qBAAqB,MAAM,yBAAyB,CAAC;AAG5D;;;;GAIG;AACH,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,iCAAiC;IAC7D,IAAI,CAAC,qBAAqB,CAAC,iCAAiC,EAAE;QAC5D,MAAM,IAAI,mBAAmB,CAAC,eAAe,EAAE,mCAAmC,CAAC,CAAC;KACrF;IAED,OAAO,MAAM,qBAAqB,CAAC,iCAAiC,EAAE,CAAC;AACzE,CAAC","sourcesContent":["import { UnavailabilityError } from 'expo-modules-core';\n\nimport NotificationScheduler from './NotificationScheduler';\nimport { NotificationRequest } from './Notifications.types';\n\n/**\n * Fetches information about all scheduled notifications.\n * @return Returns a Promise resolving to an array of objects conforming to the [`Notification`](#notification) interface.\n * @header schedule\n */\nexport default async function getAllScheduledNotificationsAsync(): Promise<NotificationRequest[]> {\n if (!NotificationScheduler.getAllScheduledNotificationsAsync) {\n throw new UnavailabilityError('Notifications', 'getAllScheduledNotificationsAsync');\n }\n\n return await NotificationScheduler.getAllScheduledNotificationsAsync();\n}\n"]}
1
+ {"version":3,"file":"getAllScheduledNotificationsAsync.js","sourceRoot":"","sources":["../src/getAllScheduledNotificationsAsync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,qBAAqB,MAAM,yBAAyB,CAAC;AAE5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAEzE;;;;GAIG;AACH,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,iCAAiC;IAC7D,IAAI,CAAC,qBAAqB,CAAC,iCAAiC,EAAE;QAC5D,MAAM,IAAI,mBAAmB,CAAC,eAAe,EAAE,mCAAmC,CAAC,CAAC;KACrF;IAED,OAAO,CAAC,MAAM,qBAAqB,CAAC,iCAAiC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CACvF,sBAAsB,CAAC,OAAO,CAAC,CAChC,CAAC;AACJ,CAAC","sourcesContent":["import { UnavailabilityError } from 'expo-modules-core';\n\nimport NotificationScheduler from './NotificationScheduler';\nimport { NotificationRequest } from './Notifications.types';\nimport { mapNotificationRequest } from './utils/mapNotificationResponse';\n\n/**\n * Fetches information about all scheduled notifications.\n * @return Returns a Promise resolving to an array of objects conforming to the [`Notification`](#notification) interface.\n * @header schedule\n */\nexport default async function getAllScheduledNotificationsAsync(): Promise<NotificationRequest[]> {\n if (!NotificationScheduler.getAllScheduledNotificationsAsync) {\n throw new UnavailabilityError('Notifications', 'getAllScheduledNotificationsAsync');\n }\n\n return (await NotificationScheduler.getAllScheduledNotificationsAsync()).map((request) =>\n mapNotificationRequest(request)\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"getPresentedNotificationsAsync.d.ts","sourceRoot":"","sources":["../src/getPresentedNotificationsAsync.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD;;;;;GAKG;AACH,wBAA8B,8BAA8B,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAMtF"}
1
+ {"version":3,"file":"getPresentedNotificationsAsync.d.ts","sourceRoot":"","sources":["../src/getPresentedNotificationsAsync.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGrD;;;;;GAKG;AACH,wBAA8B,8BAA8B,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAQtF"}
@@ -1,5 +1,6 @@
1
1
  import { UnavailabilityError } from 'expo-modules-core';
2
2
  import NotificationPresenter from './NotificationPresenterModule';
3
+ import { mapNotification } from './utils/mapNotificationResponse';
3
4
  /**
4
5
  * Fetches information about all notifications present in the notification tray (Notification Center).
5
6
  * > This method is not supported on Android below 6.0 (API level 23) – on these devices it will resolve to an empty array.
@@ -10,6 +11,6 @@ export default async function getPresentedNotificationsAsync() {
10
11
  if (!NotificationPresenter.getPresentedNotificationsAsync) {
11
12
  throw new UnavailabilityError('Notifications', 'getPresentedNotificationsAsync');
12
13
  }
13
- return await NotificationPresenter.getPresentedNotificationsAsync();
14
+ return (await NotificationPresenter.getPresentedNotificationsAsync()).map((notification) => mapNotification(notification));
14
15
  }
15
16
  //# sourceMappingURL=getPresentedNotificationsAsync.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"getPresentedNotificationsAsync.js","sourceRoot":"","sources":["../src/getPresentedNotificationsAsync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,qBAAqB,MAAM,+BAA+B,CAAC;AAGlE;;;;;GAKG;AACH,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,8BAA8B;IAC1D,IAAI,CAAC,qBAAqB,CAAC,8BAA8B,EAAE;QACzD,MAAM,IAAI,mBAAmB,CAAC,eAAe,EAAE,gCAAgC,CAAC,CAAC;KAClF;IAED,OAAO,MAAM,qBAAqB,CAAC,8BAA8B,EAAE,CAAC;AACtE,CAAC","sourcesContent":["import { UnavailabilityError } from 'expo-modules-core';\n\nimport NotificationPresenter from './NotificationPresenterModule';\nimport { Notification } from './Notifications.types';\n\n/**\n * Fetches information about all notifications present in the notification tray (Notification Center).\n * > This method is not supported on Android below 6.0 (API level 23) – on these devices it will resolve to an empty array.\n * @return A Promise which resolves with a list of notifications ([`Notification`](#notification)) currently present in the notification tray (Notification Center).\n * @header dismiss\n */\nexport default async function getPresentedNotificationsAsync(): Promise<Notification[]> {\n if (!NotificationPresenter.getPresentedNotificationsAsync) {\n throw new UnavailabilityError('Notifications', 'getPresentedNotificationsAsync');\n }\n\n return await NotificationPresenter.getPresentedNotificationsAsync();\n}\n"]}
1
+ {"version":3,"file":"getPresentedNotificationsAsync.js","sourceRoot":"","sources":["../src/getPresentedNotificationsAsync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,qBAAqB,MAAM,+BAA+B,CAAC;AAElE,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAElE;;;;;GAKG;AACH,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,8BAA8B;IAC1D,IAAI,CAAC,qBAAqB,CAAC,8BAA8B,EAAE;QACzD,MAAM,IAAI,mBAAmB,CAAC,eAAe,EAAE,gCAAgC,CAAC,CAAC;KAClF;IAED,OAAO,CAAC,MAAM,qBAAqB,CAAC,8BAA8B,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CACzF,eAAe,CAAC,YAAY,CAAC,CAC9B,CAAC;AACJ,CAAC","sourcesContent":["import { UnavailabilityError } from 'expo-modules-core';\n\nimport NotificationPresenter from './NotificationPresenterModule';\nimport { Notification } from './Notifications.types';\nimport { mapNotification } from './utils/mapNotificationResponse';\n\n/**\n * Fetches information about all notifications present in the notification tray (Notification Center).\n * > This method is not supported on Android below 6.0 (API level 23) – on these devices it will resolve to an empty array.\n * @return A Promise which resolves with a list of notifications ([`Notification`](#notification)) currently present in the notification tray (Notification Center).\n * @header dismiss\n */\nexport default async function getPresentedNotificationsAsync(): Promise<Notification[]> {\n if (!NotificationPresenter.getPresentedNotificationsAsync) {\n throw new UnavailabilityError('Notifications', 'getPresentedNotificationsAsync');\n }\n\n return (await NotificationPresenter.getPresentedNotificationsAsync()).map((notification) =>\n mapNotification(notification)\n );\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { Notification, NotificationResponse } from '../Notifications.types';
1
+ import { Notification, NotificationContent, NotificationRequest, NotificationResponse } from '../Notifications.types';
2
2
  /**
3
3
  * @hidden
4
4
  *
@@ -10,12 +10,15 @@ import { Notification, NotificationResponse } from '../Notifications.types';
10
10
  * @returns the mapped response.
11
11
  */
12
12
  export declare const mapNotificationResponse: (response: NotificationResponse) => {
13
- notification: Notification & {
13
+ notification: {
14
14
  request: {
15
- content: {
16
- dataString?: string;
15
+ content: NotificationContent & {
16
+ dataString?: string | undefined;
17
17
  };
18
+ identifier: string;
19
+ trigger: import("../Notifications.types").NotificationTrigger;
18
20
  };
21
+ date: number;
19
22
  };
20
23
  actionIdentifier: string;
21
24
  userText?: string | undefined;
@@ -29,11 +32,41 @@ export declare const mapNotificationResponse: (response: NotificationResponse) =
29
32
  * @param notification The raw notification passed in from native code
30
33
  * @returns the mapped notification.
31
34
  */
32
- export declare const mapNotification: (notification: Notification) => Notification & {
35
+ export declare const mapNotification: (notification: Notification) => {
33
36
  request: {
34
- content: {
35
- dataString?: string;
37
+ content: NotificationContent & {
38
+ dataString?: string | undefined;
36
39
  };
40
+ identifier: string;
41
+ trigger: import("../Notifications.types").NotificationTrigger;
37
42
  };
43
+ date: number;
44
+ };
45
+ /**
46
+ * @hidden
47
+ *
48
+ * Does any required processing of a notification request from native code
49
+ * before it is passed to other JS code.
50
+ *
51
+ * @param request The raw request passed in from native code
52
+ * @returns the mapped request.
53
+ */
54
+ export declare const mapNotificationRequest: (request: NotificationRequest) => {
55
+ content: NotificationContent & {
56
+ dataString?: string | undefined;
57
+ };
58
+ identifier: string;
59
+ trigger: import("../Notifications.types").NotificationTrigger;
60
+ };
61
+ /**
62
+ * @hidden
63
+ * Does any required processing of notification content from native code
64
+ * before being passed to other JS code.
65
+ *
66
+ * @param content The raw content passed in from native code
67
+ * @returns the mapped content.
68
+ */
69
+ export declare const mapNotificationContent: (content: NotificationContent) => NotificationContent & {
70
+ dataString?: string | undefined;
38
71
  };
39
72
  //# sourceMappingURL=mapNotificationResponse.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mapNotificationResponse.d.ts","sourceRoot":"","sources":["../../src/utils/mapNotificationResponse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE5E;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,aAAc,oBAAoB;;iBAkBzD;YAAE,OAAO,EAAE;gBAAE,UAAU,CAAC,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE;;;;CAbhD,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,iBAAkB,YAAY;aAE7C;QAAE,OAAO,EAAE;YAAE,UAAU,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;CAYhD,CAAC"}
1
+ {"version":3,"file":"mapNotificationResponse.d.ts","sourceRoot":"","sources":["../../src/utils/mapNotificationResponse.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAEhC;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,aAAc,oBAAoB;;;;;;;;;;;;;CAKrE,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,iBAAkB,YAAY;;;;;;;;;CAGxD,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,YAAa,mBAAmB;;;;;;CAGjE,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,YAAa,mBAAmB;;CAYlE,CAAC"}
@@ -23,18 +23,43 @@ export const mapNotificationResponse = (response) => {
23
23
  * @param notification The raw notification passed in from native code
24
24
  * @returns the mapped notification.
25
25
  */
26
- export const mapNotification = (notification) => {
27
- const mappedNotification = { ...notification };
26
+ export const mapNotification = (notification) => ({
27
+ ...notification,
28
+ request: mapNotificationRequest(notification.request),
29
+ });
30
+ /**
31
+ * @hidden
32
+ *
33
+ * Does any required processing of a notification request from native code
34
+ * before it is passed to other JS code.
35
+ *
36
+ * @param request The raw request passed in from native code
37
+ * @returns the mapped request.
38
+ */
39
+ export const mapNotificationRequest = (request) => ({
40
+ ...request,
41
+ content: mapNotificationContent(request.content),
42
+ });
43
+ /**
44
+ * @hidden
45
+ * Does any required processing of notification content from native code
46
+ * before being passed to other JS code.
47
+ *
48
+ * @param content The raw content passed in from native code
49
+ * @returns the mapped content.
50
+ */
51
+ export const mapNotificationContent = (content) => {
52
+ const mappedContent = { ...content };
28
53
  try {
29
- const dataString = mappedNotification?.request?.content['dataString'];
54
+ const dataString = mappedContent['dataString'];
30
55
  if (typeof dataString === 'string') {
31
- mappedNotification.request.content.data = JSON.parse(dataString);
32
- delete mappedNotification.request.content.dataString;
56
+ mappedContent.data = JSON.parse(dataString);
57
+ delete mappedContent.dataString;
33
58
  }
34
59
  }
35
60
  catch (e) {
36
61
  console.log(`Error in notification: ${e}`);
37
62
  }
38
- return mappedNotification;
63
+ return mappedContent;
39
64
  };
40
65
  //# sourceMappingURL=mapNotificationResponse.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mapNotificationResponse.js","sourceRoot":"","sources":["../../src/utils/mapNotificationResponse.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,QAA8B,EAAE,EAAE;IACxE,OAAO;QACL,GAAG,QAAQ;QACX,YAAY,EAAE,eAAe,CAAC,QAAQ,CAAC,YAAY,CAAC;KACrD,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,YAA0B,EAAE,EAAE;IAC5D,MAAM,kBAAkB,GAEpB,EAAE,GAAG,YAAY,EAAE,CAAC;IACxB,IAAI;QACF,MAAM,UAAU,GAAG,kBAAkB,EAAE,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;QACtE,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE;YAClC,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YACjE,OAAO,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC;SACtD;KACF;IAAC,OAAO,CAAM,EAAE;QACf,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,EAAE,CAAC,CAAC;KAC5C;IACD,OAAO,kBAAkB,CAAC;AAC5B,CAAC,CAAC","sourcesContent":["import { Notification, NotificationResponse } from '../Notifications.types';\n\n/**\n * @hidden\n *\n * Does any required processing of a notification response from native code\n * before it is passed to a notification response listener, or to the\n * last notification response hook.\n *\n * @param response The raw response passed in from native code\n * @returns the mapped response.\n */\nexport const mapNotificationResponse = (response: NotificationResponse) => {\n return {\n ...response,\n notification: mapNotification(response.notification),\n };\n};\n\n/**\n * @hidden\n *\n * Does any required processing of a notification from native code\n * before it is passed to a notification listener.\n *\n * @param notification The raw notification passed in from native code\n * @returns the mapped notification.\n */\nexport const mapNotification = (notification: Notification) => {\n const mappedNotification: Notification & {\n request: { content: { dataString?: string } };\n } = { ...notification };\n try {\n const dataString = mappedNotification?.request?.content['dataString'];\n if (typeof dataString === 'string') {\n mappedNotification.request.content.data = JSON.parse(dataString);\n delete mappedNotification.request.content.dataString;\n }\n } catch (e: any) {\n console.log(`Error in notification: ${e}`);\n }\n return mappedNotification;\n};\n"]}
1
+ {"version":3,"file":"mapNotificationResponse.js","sourceRoot":"","sources":["../../src/utils/mapNotificationResponse.ts"],"names":[],"mappings":"AAOA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,QAA8B,EAAE,EAAE;IACxE,OAAO;QACL,GAAG,QAAQ;QACX,YAAY,EAAE,eAAe,CAAC,QAAQ,CAAC,YAAY,CAAC;KACrD,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,YAA0B,EAAE,EAAE,CAAC,CAAC;IAC9D,GAAG,YAAY;IACf,OAAO,EAAE,sBAAsB,CAAC,YAAY,CAAC,OAAO,CAAC;CACtD,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,OAA4B,EAAE,EAAE,CAAC,CAAC;IACvE,GAAG,OAAO;IACV,OAAO,EAAE,sBAAsB,CAAC,OAAO,CAAC,OAAO,CAAC;CACjD,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,OAA4B,EAAE,EAAE;IACrE,MAAM,aAAa,GAAkD,EAAE,GAAG,OAAO,EAAE,CAAC;IACpF,IAAI;QACF,MAAM,UAAU,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC;QAC/C,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE;YAClC,aAAa,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YAC5C,OAAO,aAAa,CAAC,UAAU,CAAC;SACjC;KACF;IAAC,OAAO,CAAM,EAAE;QACf,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,EAAE,CAAC,CAAC;KAC5C;IACD,OAAO,aAAa,CAAC;AACvB,CAAC,CAAC","sourcesContent":["import {\n Notification,\n NotificationContent,\n NotificationRequest,\n NotificationResponse,\n} from '../Notifications.types';\n\n/**\n * @hidden\n *\n * Does any required processing of a notification response from native code\n * before it is passed to a notification response listener, or to the\n * last notification response hook.\n *\n * @param response The raw response passed in from native code\n * @returns the mapped response.\n */\nexport const mapNotificationResponse = (response: NotificationResponse) => {\n return {\n ...response,\n notification: mapNotification(response.notification),\n };\n};\n\n/**\n * @hidden\n *\n * Does any required processing of a notification from native code\n * before it is passed to a notification listener.\n *\n * @param notification The raw notification passed in from native code\n * @returns the mapped notification.\n */\nexport const mapNotification = (notification: Notification) => ({\n ...notification,\n request: mapNotificationRequest(notification.request),\n});\n\n/**\n * @hidden\n *\n * Does any required processing of a notification request from native code\n * before it is passed to other JS code.\n *\n * @param request The raw request passed in from native code\n * @returns the mapped request.\n */\nexport const mapNotificationRequest = (request: NotificationRequest) => ({\n ...request,\n content: mapNotificationContent(request.content),\n});\n\n/**\n * @hidden\n * Does any required processing of notification content from native code\n * before being passed to other JS code.\n *\n * @param content The raw content passed in from native code\n * @returns the mapped content.\n */\nexport const mapNotificationContent = (content: NotificationContent) => {\n const mappedContent: NotificationContent & { dataString?: string } = { ...content };\n try {\n const dataString = mappedContent['dataString'];\n if (typeof dataString === 'string') {\n mappedContent.data = JSON.parse(dataString);\n delete mappedContent.dataString;\n }\n } catch (e: any) {\n console.log(`Error in notification: ${e}`);\n }\n return mappedContent;\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-notifications",
3
- "version": "1.0.0-canary-20240814-ce0f7d5",
3
+ "version": "1.0.0-canary-20240912-1059f85",
4
4
  "description": "Provides an API to fetch push notification tokens and to present, schedule, receive, and respond to notifications.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -38,25 +38,25 @@
38
38
  "preset": "expo-module-scripts/ios"
39
39
  },
40
40
  "dependencies": {
41
- "@expo/image-utils": "0.5.2-canary-20240814-ce0f7d5",
41
+ "@expo/image-utils": "0.5.2-canary-20240912-1059f85",
42
42
  "@ide/backoff": "^1.0.0",
43
43
  "abort-controller": "^3.0.0",
44
44
  "assert": "^2.0.0",
45
45
  "badgin": "^1.1.5",
46
- "expo-application": "6.0.0-canary-20240814-ce0f7d5",
47
- "expo-constants": "17.0.0-canary-20240814-ce0f7d5",
46
+ "expo-application": "6.0.0-canary-20240912-1059f85",
47
+ "expo-constants": "17.0.0-canary-20240912-1059f85",
48
48
  "fs-extra": "^9.1.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node-fetch": "^2.5.7",
52
- "expo-module-scripts": "3.6.0-canary-20240814-ce0f7d5",
52
+ "expo-module-scripts": "3.6.0-canary-20240912-1059f85",
53
53
  "memfs": "^3.2.0",
54
54
  "node-fetch": "^2.6.7"
55
55
  },
56
56
  "peerDependencies": {
57
- "expo": "*",
57
+ "expo": "52.0.0-canary-20240912-1059f85",
58
58
  "react": "*",
59
59
  "react-native": "*"
60
60
  },
61
- "gitHead": "ce0f7d5c7eaec2c8d06ee4e0dc0e58cd6c1612ed"
61
+ "gitHead": "1059f8556047a3e02fa319e8b2459274571f4e6f"
62
62
  }
@@ -2,6 +2,7 @@ import { UnavailabilityError } from 'expo-modules-core';
2
2
 
3
3
  import NotificationScheduler from './NotificationScheduler';
4
4
  import { NotificationRequest } from './Notifications.types';
5
+ import { mapNotificationRequest } from './utils/mapNotificationResponse';
5
6
 
6
7
  /**
7
8
  * Fetches information about all scheduled notifications.
@@ -13,5 +14,7 @@ export default async function getAllScheduledNotificationsAsync(): Promise<Notif
13
14
  throw new UnavailabilityError('Notifications', 'getAllScheduledNotificationsAsync');
14
15
  }
15
16
 
16
- return await NotificationScheduler.getAllScheduledNotificationsAsync();
17
+ return (await NotificationScheduler.getAllScheduledNotificationsAsync()).map((request) =>
18
+ mapNotificationRequest(request)
19
+ );
17
20
  }
@@ -2,6 +2,7 @@ import { UnavailabilityError } from 'expo-modules-core';
2
2
 
3
3
  import NotificationPresenter from './NotificationPresenterModule';
4
4
  import { Notification } from './Notifications.types';
5
+ import { mapNotification } from './utils/mapNotificationResponse';
5
6
 
6
7
  /**
7
8
  * Fetches information about all notifications present in the notification tray (Notification Center).
@@ -14,5 +15,7 @@ export default async function getPresentedNotificationsAsync(): Promise<Notifica
14
15
  throw new UnavailabilityError('Notifications', 'getPresentedNotificationsAsync');
15
16
  }
16
17
 
17
- return await NotificationPresenter.getPresentedNotificationsAsync();
18
+ return (await NotificationPresenter.getPresentedNotificationsAsync()).map((notification) =>
19
+ mapNotification(notification)
20
+ );
18
21
  }
@@ -1,4 +1,9 @@
1
- import { Notification, NotificationResponse } from '../Notifications.types';
1
+ import {
2
+ Notification,
3
+ NotificationContent,
4
+ NotificationRequest,
5
+ NotificationResponse,
6
+ } from '../Notifications.types';
2
7
 
3
8
  /**
4
9
  * @hidden
@@ -26,18 +31,43 @@ export const mapNotificationResponse = (response: NotificationResponse) => {
26
31
  * @param notification The raw notification passed in from native code
27
32
  * @returns the mapped notification.
28
33
  */
29
- export const mapNotification = (notification: Notification) => {
30
- const mappedNotification: Notification & {
31
- request: { content: { dataString?: string } };
32
- } = { ...notification };
34
+ export const mapNotification = (notification: Notification) => ({
35
+ ...notification,
36
+ request: mapNotificationRequest(notification.request),
37
+ });
38
+
39
+ /**
40
+ * @hidden
41
+ *
42
+ * Does any required processing of a notification request from native code
43
+ * before it is passed to other JS code.
44
+ *
45
+ * @param request The raw request passed in from native code
46
+ * @returns the mapped request.
47
+ */
48
+ export const mapNotificationRequest = (request: NotificationRequest) => ({
49
+ ...request,
50
+ content: mapNotificationContent(request.content),
51
+ });
52
+
53
+ /**
54
+ * @hidden
55
+ * Does any required processing of notification content from native code
56
+ * before being passed to other JS code.
57
+ *
58
+ * @param content The raw content passed in from native code
59
+ * @returns the mapped content.
60
+ */
61
+ export const mapNotificationContent = (content: NotificationContent) => {
62
+ const mappedContent: NotificationContent & { dataString?: string } = { ...content };
33
63
  try {
34
- const dataString = mappedNotification?.request?.content['dataString'];
64
+ const dataString = mappedContent['dataString'];
35
65
  if (typeof dataString === 'string') {
36
- mappedNotification.request.content.data = JSON.parse(dataString);
37
- delete mappedNotification.request.content.dataString;
66
+ mappedContent.data = JSON.parse(dataString);
67
+ delete mappedContent.dataString;
38
68
  }
39
69
  } catch (e: any) {
40
70
  console.log(`Error in notification: ${e}`);
41
71
  }
42
- return mappedNotification;
72
+ return mappedContent;
43
73
  };
@@ -1,11 +0,0 @@
1
- package expo.modules.notifications.notifications.interfaces;
2
-
3
- import android.app.Notification;
4
-
5
- import androidx.annotation.Nullable;
6
-
7
- public interface NotificationPresentationEffect {
8
- boolean onNotificationPresented(@Nullable String tag, int id, Notification notification);
9
-
10
- boolean onNotificationPresentationFailed(@Nullable String tag, int id, Notification notification);
11
- }
@@ -1,7 +0,0 @@
1
- package expo.modules.notifications.notifications.interfaces;
2
-
3
- public interface NotificationPresentationEffectsManager extends NotificationPresentationEffect {
4
- void addEffect(NotificationPresentationEffect effector);
5
-
6
- void removeEffect(NotificationPresentationEffect effector);
7
- }
@@ -1,61 +0,0 @@
1
- package expo.modules.notifications.notifications.model.triggers;
2
-
3
- import android.os.Build;
4
- import android.os.Parcel;
5
-
6
- import com.google.firebase.messaging.RemoteMessage;
7
-
8
- import androidx.annotation.Nullable;
9
- import androidx.annotation.RequiresApi;
10
- import expo.modules.notifications.notifications.interfaces.NotificationTrigger;
11
-
12
- /**
13
- * A trigger representing an incoming remote Firebase notification.
14
- */
15
- public class FirebaseNotificationTrigger implements NotificationTrigger {
16
- private RemoteMessage mRemoteMessage;
17
-
18
- public FirebaseNotificationTrigger(RemoteMessage remoteMessage) {
19
- mRemoteMessage = remoteMessage;
20
- }
21
-
22
- private FirebaseNotificationTrigger(Parcel in) {
23
- mRemoteMessage = in.readParcelable(getClass().getClassLoader());
24
- }
25
-
26
- public RemoteMessage getRemoteMessage() {
27
- return mRemoteMessage;
28
- }
29
-
30
- @Override
31
- public int describeContents() {
32
- return 0;
33
- }
34
-
35
- @Override
36
- public void writeToParcel(Parcel dest, int flags) {
37
- dest.writeParcelable(mRemoteMessage, 0);
38
- }
39
-
40
- public static final Creator<FirebaseNotificationTrigger> CREATOR = new Creator<FirebaseNotificationTrigger>() {
41
- @Override
42
- public FirebaseNotificationTrigger createFromParcel(Parcel in) {
43
- return new FirebaseNotificationTrigger(in);
44
- }
45
-
46
- @Override
47
- public FirebaseNotificationTrigger[] newArray(int size) {
48
- return new FirebaseNotificationTrigger[size];
49
- }
50
- };
51
-
52
- @Nullable
53
- @Override
54
- @RequiresApi(api = Build.VERSION_CODES.O)
55
- public String getNotificationChannel() {
56
- if (getRemoteMessage().getData().containsKey("channelId")) {
57
- return getRemoteMessage().getData().get("channelId");
58
- }
59
- return NotificationTrigger.super.getNotificationChannel();
60
- }
61
- }
@@ -1,63 +0,0 @@
1
- package expo.modules.notifications.notifications.presentation;
2
-
3
- import android.app.Notification;
4
- import android.util.Log;
5
-
6
- import expo.modules.core.interfaces.InternalModule;
7
-
8
- import java.util.ArrayList;
9
- import java.util.Collection;
10
- import java.util.Collections;
11
- import java.util.List;
12
-
13
- import androidx.annotation.Nullable;
14
- import expo.modules.notifications.notifications.interfaces.NotificationPresentationEffect;
15
- import expo.modules.notifications.notifications.interfaces.NotificationPresentationEffectsManager;
16
-
17
- public class ExpoNotificationPresentationEffectsManager implements InternalModule, NotificationPresentationEffectsManager {
18
- private Collection<NotificationPresentationEffect> mEffects = new ArrayList<>();
19
-
20
- @Override
21
- public List<? extends Class> getExportedInterfaces() {
22
- return Collections.singletonList(NotificationPresentationEffectsManager.class);
23
- }
24
-
25
- @Override
26
- public void addEffect(NotificationPresentationEffect effect) {
27
- removeEffect(effect);
28
- mEffects.add(effect);
29
- }
30
-
31
- @Override
32
- public void removeEffect(NotificationPresentationEffect effect) {
33
- mEffects.remove(effect);
34
- }
35
-
36
- @Override
37
- public boolean onNotificationPresented(@Nullable String tag, int id, Notification notification) {
38
- boolean anyActed = false;
39
- for (NotificationPresentationEffect effect : mEffects) {
40
- try {
41
- anyActed = effect.onNotificationPresented(tag, id, notification) || anyActed;
42
- } catch (Exception e) {
43
- Log.w("expo-notifications", String.format("Notification presentation effector %s threw an exception for notification (%s, %d): %s", effect.getClass(), tag, id, e.getMessage()));
44
- e.printStackTrace();
45
- }
46
- }
47
- return anyActed;
48
- }
49
-
50
- @Override
51
- public boolean onNotificationPresentationFailed(@Nullable String tag, int id, Notification notification) {
52
- boolean anyActed = false;
53
- for (NotificationPresentationEffect effect : mEffects) {
54
- try {
55
- anyActed = effect.onNotificationPresentationFailed(tag, id, notification) || anyActed;
56
- } catch (Exception e) {
57
- Log.w("expo-notifications", String.format("Notification presentation effector %s threw an exception for notification (%s, %d): %s", effect.getClass(), tag, id, e.getMessage()));
58
- e.printStackTrace();
59
- }
60
- }
61
- return anyActed;
62
- }
63
- }
@@ -1,53 +0,0 @@
1
- package expo.modules.notifications.notifications.presentation.effects;
2
-
3
- import android.app.Notification;
4
- import android.content.Context;
5
-
6
- import expo.modules.core.ModuleRegistry;
7
- import expo.modules.core.interfaces.InternalModule;
8
-
9
- import java.util.Collections;
10
- import java.util.List;
11
-
12
- import androidx.annotation.Nullable;
13
- import expo.modules.notifications.notifications.interfaces.NotificationPresentationEffect;
14
- import expo.modules.notifications.notifications.interfaces.NotificationPresentationEffectsManager;
15
-
16
- public abstract class BaseNotificationEffect implements InternalModule, NotificationPresentationEffect {
17
- private Context mContext;
18
- private NotificationPresentationEffectsManager mManager;
19
-
20
- public BaseNotificationEffect(Context context) {
21
- mContext = context;
22
- }
23
-
24
- protected Context getContext() {
25
- return mContext;
26
- }
27
-
28
- @Override
29
- public List<? extends Class> getExportedInterfaces() {
30
- return Collections.singletonList(getClass());
31
- }
32
-
33
- @Override
34
- public void onCreate(ModuleRegistry moduleRegistry) {
35
- mManager = moduleRegistry.getModule(NotificationPresentationEffectsManager.class);
36
- mManager.addEffect(this);
37
- }
38
-
39
- @Override
40
- public void onDestroy() {
41
- mManager.removeEffect(this);
42
- }
43
-
44
- @Override
45
- public boolean onNotificationPresented(@Nullable String tag, int id, Notification notification) {
46
- return false;
47
- }
48
-
49
- @Override
50
- public boolean onNotificationPresentationFailed(@Nullable String tag, int id, Notification notification) {
51
- return false;
52
- }
53
- }