expo-notifications 0.29.5 → 0.29.6

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/CHANGELOG.md CHANGED
@@ -10,6 +10,13 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 0.29.6 — 2024-11-10
14
+
15
+ ### 🎉 New features
16
+
17
+ - [android] run notification tasks from killed state ([#32531](https://github.com/expo/expo/pull/32531) by [@vonovak](https://github.com/vonovak))
18
+ - add `enableBackgroundRemoteNotifications` option to config plugin ([#32716](https://github.com/expo/expo/pull/32716) by [@vonovak](https://github.com/vonovak))
19
+
13
20
  ## 0.29.5 — 2024-11-07
14
21
 
15
22
  ### 🛠 Breaking changes
@@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
2
2
  apply plugin: 'kotlin-parcelize'
3
3
 
4
4
  group = 'host.exp.exponent'
5
- version = '0.29.5'
5
+ version = '0.29.6'
6
6
 
7
7
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
8
8
  apply from: expoModulesCorePlugin
@@ -15,7 +15,7 @@ android {
15
15
  namespace "expo.modules.notifications"
16
16
  defaultConfig {
17
17
  versionCode 21
18
- versionName '0.29.5'
18
+ versionName '0.29.6'
19
19
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
20
20
  }
21
21
 
@@ -6,6 +6,7 @@ import com.google.firebase.messaging.RemoteMessage;
6
6
 
7
7
  import java.util.Map;
8
8
 
9
+ import androidx.annotation.NonNull;
9
10
  import androidx.annotation.Nullable;
10
11
 
11
12
  /**
@@ -19,7 +20,7 @@ public class RemoteMessageSerializer {
19
20
  * @param message {@link RemoteMessage} to serialize
20
21
  * @return Serialized message
21
22
  */
22
- public static Bundle toBundle(RemoteMessage message) {
23
+ public static @NonNull Bundle toBundle(RemoteMessage message) {
23
24
  Bundle serializedMessage = new Bundle();
24
25
  serializedMessage.putString("collapseKey", message.getCollapseKey());
25
26
  serializedMessage.putBundle("data", toBundle(message.getData()));
@@ -6,6 +6,7 @@ import android.content.Context;
6
6
  import android.os.Bundle;
7
7
  import android.os.PersistableBundle;
8
8
  import android.util.Log;
9
+ import androidx.annotation.NonNull;
9
10
 
10
11
  import org.json.JSONException;
11
12
  import org.json.JSONObject;
@@ -120,5 +121,9 @@ public class BackgroundRemoteNotificationTaskConsumer extends TaskConsumer imple
120
121
  return bundle;
121
122
  }
122
123
 
124
+ public void executeTask(@NonNull Bundle bundle) {
125
+ mTask.execute(bundle, null);
126
+ }
127
+
123
128
  //endregion
124
129
  }
@@ -45,7 +45,7 @@ class ExpoHandlingDelegate(protected val context: Context) : HandlingDelegate {
45
45
  }
46
46
 
47
47
  sListenersReferences[listener] = WeakReference(listener)
48
- if (!sPendingNotificationResponses.isEmpty()) {
48
+ if (sPendingNotificationResponses.isNotEmpty()) {
49
49
  val responseIterator = sPendingNotificationResponses.iterator()
50
50
  while (responseIterator.hasNext()) {
51
51
  listener.onNotificationResponseReceived(responseIterator.next())
@@ -139,11 +139,11 @@ class ExpoHandlingDelegate(protected val context: Context) : HandlingDelegate {
139
139
  if (notificationResponse.action.opensAppToForeground()) {
140
140
  openAppToForeground(context, notificationResponse)
141
141
  }
142
-
143
- if (getListeners().isEmpty()) {
142
+ val listeners = getListeners()
143
+ if (listeners.isEmpty()) {
144
144
  sPendingNotificationResponses.add(notificationResponse)
145
145
  } else {
146
- getListeners().forEach {
146
+ listeners.forEach {
147
147
  it.onNotificationResponseReceived(notificationResponse)
148
148
  }
149
149
  }
@@ -2,6 +2,7 @@ package expo.modules.notifications.service.delegates
2
2
 
3
3
  import android.content.Context
4
4
  import com.google.firebase.messaging.RemoteMessage
5
+ import expo.modules.interfaces.taskManager.TaskServiceProviderHelper
5
6
  import expo.modules.notifications.notifications.RemoteMessageSerializer
6
7
  import expo.modules.notifications.notifications.background.BackgroundRemoteNotificationTaskConsumer
7
8
  import expo.modules.notifications.notifications.debug.DebugLogging
@@ -94,9 +95,16 @@ open class FirebaseMessagingDelegate(protected val context: Context) : FirebaseM
94
95
  val notification = createNotification(remoteMessage)
95
96
  DebugLogging.logNotification("FirebaseMessagingDelegate.onMessageReceived: notification", notification)
96
97
  NotificationsService.receive(context, notification)
97
- // background tasks are separate from the main notification handling flow; they are executed through task-manager
98
+ runTaskManagerTasks(remoteMessage)
99
+ }
100
+
101
+ private fun runTaskManagerTasks(remoteMessage: RemoteMessage) {
102
+ // getTaskServiceImpl() has a side effect:
103
+ // the TaskService constructor calls restoreTasks which then constructs a BackgroundRemoteNotificationTaskConsumer,
104
+ // and the getBackgroundTasks() call below doesn't return an empty collection.
105
+ TaskServiceProviderHelper.getTaskServiceImpl(context.applicationContext)
98
106
  getBackgroundTasks().forEach {
99
- it.scheduleJob(RemoteMessageSerializer.toBundle(remoteMessage))
107
+ it.executeTask(RemoteMessageSerializer.toBundle(remoteMessage))
100
108
  }
101
109
  }
102
110
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-notifications",
3
- "version": "0.29.5",
3
+ "version": "0.29.6",
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",
@@ -55,5 +55,5 @@
55
55
  "react": "*",
56
56
  "react-native": "*"
57
57
  },
58
- "gitHead": "c0899e56c5be08b0142f06f05aa7bac8d6bc18b8"
58
+ "gitHead": "1f84b4c952ed9785c41ee65c9877e44f1df27af9"
59
59
  }
@@ -28,6 +28,14 @@ export type NotificationsPluginProps = {
28
28
  * @platform ios
29
29
  */
30
30
  mode?: 'development' | 'production';
31
+ /**
32
+ * Whether to enable background remote notifications, as described in [Apple documentation](https://developer.apple.com/documentation/usernotifications/pushing-background-updates-to-your-app).
33
+ *
34
+ * This sets the `UIBackgroundModes` key in the `Info.plist` to include `remote-notification`.
35
+ * @default false
36
+ * @platform ios
37
+ */
38
+ enableBackgroundRemoteNotifications?: boolean;
31
39
  };
32
40
  declare const _default: ConfigPlugin<void | NotificationsPluginProps>;
33
41
  export default _default;
@@ -1,9 +1,6 @@
1
1
  import { ConfigPlugin, XcodeProject } from 'expo/config-plugins';
2
2
  import { NotificationsPluginProps } from './withNotifications';
3
3
  export declare const withNotificationsIOS: ConfigPlugin<NotificationsPluginProps>;
4
- export declare const withNotificationSounds: ConfigPlugin<{
5
- sounds: string[];
6
- }>;
7
4
  /**
8
5
  * Save sound files to the Xcode project root and add them to the Xcode project.
9
6
  */
@@ -1,21 +1,42 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setNotificationSounds = exports.withNotificationSounds = exports.withNotificationsIOS = void 0;
3
+ exports.setNotificationSounds = exports.withNotificationsIOS = void 0;
4
4
  const config_plugins_1 = require("expo/config-plugins");
5
5
  const fs_1 = require("fs");
6
6
  const path_1 = require("path");
7
7
  const ERROR_MSG_PREFIX = 'An error occurred while configuring iOS notifications. ';
8
- const withNotificationsIOS = (config, { mode = 'development', sounds = [] }) => {
8
+ const withNotificationsIOS = (config, { mode = 'development', sounds = [], enableBackgroundRemoteNotifications }) => {
9
9
  config = (0, config_plugins_1.withEntitlementsPlist)(config, (config) => {
10
10
  if (!config.modResults['aps-environment']) {
11
11
  config.modResults['aps-environment'] = mode;
12
12
  }
13
13
  return config;
14
14
  });
15
- config = (0, exports.withNotificationSounds)(config, { sounds });
15
+ config = withNotificationSounds(config, { sounds });
16
+ config = withBackgroundRemoteNotifications(config, enableBackgroundRemoteNotifications);
16
17
  return config;
17
18
  };
18
19
  exports.withNotificationsIOS = withNotificationsIOS;
20
+ const withBackgroundRemoteNotifications = (config, enableBackgroundRemoteNotifications) => {
21
+ if (!(enableBackgroundRemoteNotifications === undefined ||
22
+ typeof enableBackgroundRemoteNotifications === 'boolean')) {
23
+ throw new Error(ERROR_MSG_PREFIX +
24
+ `"enableBackgroundRemoteNotifications" has an invalid value: ${enableBackgroundRemoteNotifications}. Expected a boolean.`);
25
+ }
26
+ if (!enableBackgroundRemoteNotifications) {
27
+ return config;
28
+ }
29
+ return (0, config_plugins_1.withInfoPlist)(config, (config) => {
30
+ if (!Array.isArray(config.modResults.UIBackgroundModes)) {
31
+ config.modResults.UIBackgroundModes = [];
32
+ }
33
+ const notificationBackgroundMode = 'remote-notification';
34
+ if (!config.modResults.UIBackgroundModes.includes(notificationBackgroundMode)) {
35
+ config.modResults.UIBackgroundModes.push(notificationBackgroundMode);
36
+ }
37
+ return config;
38
+ });
39
+ };
19
40
  const withNotificationSounds = (config, { sounds }) => {
20
41
  return (0, config_plugins_1.withXcodeProject)(config, (config) => {
21
42
  setNotificationSounds(config.modRequest.projectRoot, {
@@ -26,7 +47,6 @@ const withNotificationSounds = (config, { sounds }) => {
26
47
  return config;
27
48
  });
28
49
  };
29
- exports.withNotificationSounds = withNotificationSounds;
30
50
  /**
31
51
  * Save sound files to the Xcode project root and add them to the Xcode project.
32
52
  */
@@ -34,6 +34,15 @@ export type NotificationsPluginProps = {
34
34
  * @platform ios
35
35
  */
36
36
  mode?: 'development' | 'production';
37
+
38
+ /**
39
+ * Whether to enable background remote notifications, as described in [Apple documentation](https://developer.apple.com/documentation/usernotifications/pushing-background-updates-to-your-app).
40
+ *
41
+ * This sets the `UIBackgroundModes` key in the `Info.plist` to include `remote-notification`.
42
+ * @default false
43
+ * @platform ios
44
+ */
45
+ enableBackgroundRemoteNotifications?: boolean;
37
46
  };
38
47
 
39
48
  const withNotifications: ConfigPlugin<NotificationsPluginProps | void> = (config, props) => {
@@ -4,6 +4,7 @@ import {
4
4
  IOSConfig,
5
5
  withXcodeProject,
6
6
  XcodeProject,
7
+ withInfoPlist,
7
8
  } from 'expo/config-plugins';
8
9
  import { copyFileSync } from 'fs';
9
10
  import { basename, resolve } from 'path';
@@ -14,7 +15,7 @@ const ERROR_MSG_PREFIX = 'An error occurred while configuring iOS notifications.
14
15
 
15
16
  export const withNotificationsIOS: ConfigPlugin<NotificationsPluginProps> = (
16
17
  config,
17
- { mode = 'development', sounds = [] }
18
+ { mode = 'development', sounds = [], enableBackgroundRemoteNotifications }
18
19
  ) => {
19
20
  config = withEntitlementsPlist(config, (config) => {
20
21
  if (!config.modResults['aps-environment']) {
@@ -23,10 +24,42 @@ export const withNotificationsIOS: ConfigPlugin<NotificationsPluginProps> = (
23
24
  return config;
24
25
  });
25
26
  config = withNotificationSounds(config, { sounds });
27
+ config = withBackgroundRemoteNotifications(config, enableBackgroundRemoteNotifications);
28
+
26
29
  return config;
27
30
  };
28
31
 
29
- export const withNotificationSounds: ConfigPlugin<{ sounds: string[] }> = (config, { sounds }) => {
32
+ const withBackgroundRemoteNotifications: ConfigPlugin<boolean | undefined> = (
33
+ config,
34
+ enableBackgroundRemoteNotifications
35
+ ) => {
36
+ if (
37
+ !(
38
+ enableBackgroundRemoteNotifications === undefined ||
39
+ typeof enableBackgroundRemoteNotifications === 'boolean'
40
+ )
41
+ ) {
42
+ throw new Error(
43
+ ERROR_MSG_PREFIX +
44
+ `"enableBackgroundRemoteNotifications" has an invalid value: ${enableBackgroundRemoteNotifications}. Expected a boolean.`
45
+ );
46
+ }
47
+ if (!enableBackgroundRemoteNotifications) {
48
+ return config;
49
+ }
50
+ return withInfoPlist(config, (config) => {
51
+ if (!Array.isArray(config.modResults.UIBackgroundModes)) {
52
+ config.modResults.UIBackgroundModes = [];
53
+ }
54
+ const notificationBackgroundMode = 'remote-notification';
55
+ if (!config.modResults.UIBackgroundModes.includes(notificationBackgroundMode)) {
56
+ config.modResults.UIBackgroundModes.push(notificationBackgroundMode);
57
+ }
58
+ return config;
59
+ });
60
+ };
61
+
62
+ const withNotificationSounds: ConfigPlugin<{ sounds: string[] }> = (config, { sounds }) => {
30
63
  return withXcodeProject(config, (config) => {
31
64
  setNotificationSounds(config.modRequest.projectRoot, {
32
65
  sounds,