expo-notifications 0.28.16 → 0.28.18
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 +18 -0
- package/android/build.gradle +23 -9
- package/android/src/main/java/expo/modules/notifications/Exceptions.kt +0 -13
- package/android/src/main/java/expo/modules/notifications/notifications/NotificationSerializer.java +10 -11
- package/android/src/main/java/expo/modules/notifications/notifications/SoundResolver.java +1 -1
- package/android/src/main/java/expo/modules/notifications/notifications/debug/DebugLogging.kt +3 -4
- package/android/src/main/java/expo/modules/notifications/notifications/emitting/NotificationsEmitter.kt +5 -0
- package/android/src/main/java/expo/modules/notifications/notifications/handling/NotificationsHandler.kt +1 -2
- package/android/src/main/java/expo/modules/notifications/notifications/handling/SingleNotificationHandlerTask.java +2 -2
- package/android/src/main/java/expo/modules/notifications/notifications/interfaces/INotificationContent.kt +38 -0
- package/android/src/main/java/expo/modules/notifications/notifications/interfaces/NotificationBuilder.kt +34 -0
- package/android/src/main/java/expo/modules/notifications/notifications/interfaces/NotificationTrigger.java +0 -3
- package/android/src/main/java/expo/modules/notifications/notifications/model/Notification.java +7 -7
- package/android/src/main/java/expo/modules/notifications/notifications/model/NotificationContent.java +43 -7
- package/android/src/main/java/expo/modules/notifications/notifications/model/NotificationRequest.java +4 -3
- package/android/src/main/java/expo/modules/notifications/notifications/model/RemoteNotificationContent.kt +99 -0
- package/android/src/main/java/expo/modules/notifications/notifications/model/triggers/FirebaseNotificationTrigger.kt +43 -0
- package/android/src/main/java/expo/modules/notifications/notifications/presentation/ExpoNotificationPresentationModule.kt +1 -11
- package/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/BaseNotificationBuilder.java +2 -2
- package/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/CategoryAwareNotificationBuilder.java +3 -2
- package/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ChannelAwareNotificationBuilder.java +6 -7
- package/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/DownloadImage.kt +23 -0
- package/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ExpoNotificationBuilder.kt +326 -0
- package/android/src/main/java/expo/modules/notifications/service/NotificationsService.kt +48 -44
- package/android/src/main/java/expo/modules/notifications/service/delegates/ExpoPresentationDelegate.kt +36 -16
- package/android/src/main/java/expo/modules/notifications/service/delegates/FirebaseMessagingDelegate.kt +5 -28
- package/build/NotificationsEmitter.d.ts +11 -1
- package/build/NotificationsEmitter.d.ts.map +1 -1
- package/build/NotificationsEmitter.js +31 -2
- package/build/NotificationsEmitter.js.map +1 -1
- package/build/useLastNotificationResponse.d.ts.map +1 -1
- package/build/useLastNotificationResponse.js +5 -1
- package/build/useLastNotificationResponse.js.map +1 -1
- package/ios/EXNotifications/Notifications/Emitter/EXNotificationsEmitter.h +1 -0
- package/ios/EXNotifications/Notifications/Emitter/EXNotificationsEmitter.m +8 -1
- package/package.json +2 -2
- package/plugin/build/withNotificationsAndroid.d.ts +5 -5
- package/plugin/build/withNotificationsAndroid.js +18 -16
- package/plugin/src/withNotificationsAndroid.ts +18 -18
- package/src/NotificationsEmitter.ts +33 -2
- package/src/useLastNotificationResponse.ts +5 -0
- package/android/src/main/java/expo/modules/notifications/notifications/JSONNotificationContentBuilder.java +0 -203
- package/android/src/main/java/expo/modules/notifications/notifications/interfaces/NotificationBuilder.java +0 -35
- package/android/src/main/java/expo/modules/notifications/notifications/interfaces/NotificationPresentationEffect.java +0 -11
- package/android/src/main/java/expo/modules/notifications/notifications/interfaces/NotificationPresentationEffectsManager.java +0 -7
- package/android/src/main/java/expo/modules/notifications/notifications/model/triggers/FirebaseNotificationTrigger.java +0 -61
- package/android/src/main/java/expo/modules/notifications/notifications/presentation/ExpoNotificationPresentationEffectsManager.java +0 -63
- package/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ExpoNotificationBuilder.java +0 -295
- package/android/src/main/java/expo/modules/notifications/notifications/presentation/effects/BaseNotificationEffect.java +0 -53
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,24 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 0.28.18 — 2024-10-01
|
|
14
|
+
|
|
15
|
+
### 🎉 New features
|
|
16
|
+
|
|
17
|
+
- Add clearLastNotificationResponseAsync to API. ([#31607](https://github.com/expo/expo/pull/31607) by [@douglowder](https://github.com/douglowder))
|
|
18
|
+
|
|
19
|
+
### 🐛 Bug fixes
|
|
20
|
+
|
|
21
|
+
- [android] fix notifications actions not being presented ([#31795](https://github.com/expo/expo/pull/31795) by [@vonovak](https://github.com/vonovak))
|
|
22
|
+
|
|
23
|
+
## 0.28.17 — 2024-09-17
|
|
24
|
+
|
|
25
|
+
### 🐛 Bug fixes
|
|
26
|
+
|
|
27
|
+
- [Android] image was missing on android when in foreground ([#31405](https://github.com/expo/expo/pull/31405) by [@vonovak](https://github.com/vonovak))
|
|
28
|
+
- [Android] fix local notifications with null trigger. ([#31157](https://github.com/expo/expo/pull/31157) by [@douglowder](https://github.com/douglowder))
|
|
29
|
+
- [Android] Take `channelId` into account when presenting notifications. ([#31201](https://github.com/expo/expo/pull/31201) by [@vonovak](https://github.com/vonovak))
|
|
30
|
+
|
|
13
31
|
## 0.28.16 — 2024-08-21
|
|
14
32
|
|
|
15
33
|
### 🐛 Bug fixes
|
package/android/build.gradle
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
apply plugin: 'com.android.library'
|
|
2
2
|
|
|
3
3
|
group = 'host.exp.exponent'
|
|
4
|
-
version = '0.28.
|
|
4
|
+
version = '0.28.18'
|
|
5
5
|
|
|
6
6
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
7
7
|
apply from: expoModulesCorePlugin
|
|
@@ -14,26 +14,40 @@ android {
|
|
|
14
14
|
namespace "expo.modules.notifications"
|
|
15
15
|
defaultConfig {
|
|
16
16
|
versionCode 21
|
|
17
|
-
versionName '0.28.
|
|
17
|
+
versionName '0.28.18'
|
|
18
|
+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
buildFeatures {
|
|
21
22
|
buildConfig true
|
|
22
23
|
}
|
|
24
|
+
testOptions {
|
|
25
|
+
unitTests.all { test ->
|
|
26
|
+
testLogging {
|
|
27
|
+
outputs.upToDateWhen { false }
|
|
28
|
+
events "passed", "failed", "skipped", "standardError"
|
|
29
|
+
showCauses true
|
|
30
|
+
showExceptions true
|
|
31
|
+
showStandardStreams true
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
23
35
|
}
|
|
24
36
|
|
|
25
37
|
dependencies {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
38
|
+
implementation 'androidx.core:core:1.6.0'
|
|
39
|
+
implementation 'androidx.lifecycle:lifecycle-runtime:2.2.0'
|
|
40
|
+
implementation 'androidx.lifecycle:lifecycle-process:2.2.0'
|
|
41
|
+
implementation 'androidx.lifecycle:lifecycle-common-java8:2.2.0'
|
|
42
|
+
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
|
|
31
43
|
|
|
32
|
-
|
|
44
|
+
// release notes in https://firebase.google.com/support/release-notes/android - cmd + f "Cloud Messaging version"
|
|
45
|
+
implementation 'com.google.firebase:firebase-messaging:24.0.1'
|
|
33
46
|
|
|
34
|
-
|
|
47
|
+
implementation 'me.leolin:ShortcutBadger:1.1.22@aar'
|
|
35
48
|
|
|
36
49
|
if (project.findProject(':expo-modules-test-core')) {
|
|
37
50
|
testImplementation project(':expo-modules-test-core')
|
|
51
|
+
androidTestImplementation project(':expo-modules-test-core')
|
|
38
52
|
}
|
|
39
53
|
}
|
|
@@ -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
|
-
}
|
package/android/src/main/java/expo/modules/notifications/notifications/NotificationSerializer.java
CHANGED
|
@@ -3,7 +3,6 @@ package expo.modules.notifications.notifications;
|
|
|
3
3
|
import static expo.modules.notifications.UtilsKt.filteredBundleForJSTypeConverter;
|
|
4
4
|
import static expo.modules.notifications.UtilsKt.isValidJSONString;
|
|
5
5
|
|
|
6
|
-
import android.os.Build;
|
|
7
6
|
import android.os.Bundle;
|
|
8
7
|
|
|
9
8
|
import androidx.annotation.Nullable;
|
|
@@ -22,10 +21,10 @@ import java.util.List;
|
|
|
22
21
|
import java.util.Map;
|
|
23
22
|
import java.util.Set;
|
|
24
23
|
|
|
24
|
+
import expo.modules.notifications.notifications.interfaces.INotificationContent;
|
|
25
25
|
import expo.modules.notifications.notifications.interfaces.NotificationTrigger;
|
|
26
26
|
import expo.modules.notifications.notifications.interfaces.SchedulableNotificationTrigger;
|
|
27
27
|
import expo.modules.notifications.notifications.model.Notification;
|
|
28
|
-
import expo.modules.notifications.notifications.model.NotificationContent;
|
|
29
28
|
import expo.modules.notifications.notifications.model.NotificationRequest;
|
|
30
29
|
import expo.modules.notifications.notifications.model.NotificationResponse;
|
|
31
30
|
import expo.modules.notifications.notifications.model.TextInputNotificationResponse;
|
|
@@ -51,7 +50,7 @@ public class NotificationSerializer {
|
|
|
51
50
|
public static Bundle toBundle(Notification notification) {
|
|
52
51
|
Bundle serializedNotification = new Bundle();
|
|
53
52
|
serializedNotification.putBundle("request", toBundle(notification.getNotificationRequest()));
|
|
54
|
-
serializedNotification.putLong("date", notification.
|
|
53
|
+
serializedNotification.putLong("date", notification.getOriginDate().getTime());
|
|
55
54
|
return serializedNotification;
|
|
56
55
|
}
|
|
57
56
|
|
|
@@ -76,7 +75,10 @@ public class NotificationSerializer {
|
|
|
76
75
|
// and we copy the data as is
|
|
77
76
|
content.putBundle("data", toBundle(data));
|
|
78
77
|
}
|
|
79
|
-
} else if(
|
|
78
|
+
} else if(
|
|
79
|
+
request.getTrigger() instanceof SchedulableNotificationTrigger ||
|
|
80
|
+
request.getTrigger() == null
|
|
81
|
+
) {
|
|
80
82
|
JSONObject body = request.getContent().getBody();
|
|
81
83
|
if (body != null) {
|
|
82
84
|
// Expo sends notification.body as data.message, and JSON stringifies data.body
|
|
@@ -96,7 +98,7 @@ public class NotificationSerializer {
|
|
|
96
98
|
return result;
|
|
97
99
|
}
|
|
98
100
|
|
|
99
|
-
public static Bundle toBundle(
|
|
101
|
+
public static Bundle toBundle(INotificationContent content) {
|
|
100
102
|
Bundle serializedContent = new Bundle();
|
|
101
103
|
serializedContent.putString("title", content.getTitle());
|
|
102
104
|
serializedContent.putString("subtitle", content.getSubtitle());
|
|
@@ -110,9 +112,9 @@ public class NotificationSerializer {
|
|
|
110
112
|
} else {
|
|
111
113
|
serializedContent.putString("badge", null);
|
|
112
114
|
}
|
|
113
|
-
if (content.
|
|
115
|
+
if (content.getShouldPlayDefaultSound()) {
|
|
114
116
|
serializedContent.putString("sound", "default");
|
|
115
|
-
} else if (content.
|
|
117
|
+
} else if (content.getSoundName() != null) {
|
|
116
118
|
serializedContent.putString("sound", "custom");
|
|
117
119
|
} else {
|
|
118
120
|
serializedContent.putString("sound", null);
|
|
@@ -225,10 +227,7 @@ public class NotificationSerializer {
|
|
|
225
227
|
|
|
226
228
|
@Nullable
|
|
227
229
|
private static String getChannelId(NotificationTrigger trigger) {
|
|
228
|
-
|
|
229
|
-
return trigger.getNotificationChannel();
|
|
230
|
-
}
|
|
231
|
-
return null;
|
|
230
|
+
return trigger.getNotificationChannel();
|
|
232
231
|
}
|
|
233
232
|
|
|
234
233
|
@NotNull
|
|
@@ -9,7 +9,7 @@ import androidx.annotation.Nullable;
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* A shared logic between ContentBuilders ({@link ArgumentsNotificationContentBuilder}
|
|
12
|
-
* and {@link
|
|
12
|
+
* and {@link RemoteNotificationContent}) for resolving sounds based on the "soundName" property.
|
|
13
13
|
*/
|
|
14
14
|
public class SoundResolver {
|
|
15
15
|
private Context mContext;
|
package/android/src/main/java/expo/modules/notifications/notifications/debug/DebugLogging.kt
CHANGED
|
@@ -61,7 +61,6 @@ object DebugLogging {
|
|
|
61
61
|
Log.i("expo-notifications", logMessage)
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
@RequiresApi(Build.VERSION_CODES.O)
|
|
65
64
|
fun logNotification(caller: String, notification: Notification) {
|
|
66
65
|
if (!BuildConfig.DEBUG) {
|
|
67
66
|
// Do not log for release/production builds
|
|
@@ -73,11 +72,11 @@ object DebugLogging {
|
|
|
73
72
|
notification.notificationRequest.content.title: ${notification.notificationRequest.content.title}
|
|
74
73
|
notification.notificationRequest.content.subtitle: ${notification.notificationRequest.content.subtitle}
|
|
75
74
|
notification.notificationRequest.content.text: ${notification.notificationRequest.content.text}
|
|
76
|
-
notification.notificationRequest.content.sound: ${notification.notificationRequest.content.
|
|
75
|
+
notification.notificationRequest.content.sound: ${notification.notificationRequest.content.soundName}
|
|
76
|
+
notification.notificationRequest.content.channelID: ${notification.notificationRequest.trigger.notificationChannel}
|
|
77
77
|
notification.notificationRequest.content.body: ${notification.notificationRequest.content.body}
|
|
78
78
|
notification.notificationRequest.content.color: ${notification.notificationRequest.content.color}
|
|
79
|
-
notification.notificationRequest.content.vibrationPattern: ${notification.notificationRequest.content.vibrationPattern
|
|
80
|
-
notification.notificationRequest.trigger.notificationChannel: ${notification.notificationRequest.trigger.notificationChannel}
|
|
79
|
+
notification.notificationRequest.content.vibrationPattern: ${notification.notificationRequest.content.vibrationPattern?.contentToString()}
|
|
81
80
|
notification.notificationRequest.identifier: ${notification.notificationRequest.identifier}
|
|
82
81
|
""".trimIndent()
|
|
83
82
|
|
|
@@ -41,6 +41,11 @@ open class NotificationsEmitter : Module(), NotificationListener {
|
|
|
41
41
|
AsyncFunction<Bundle?>("getLastNotificationResponseAsync") {
|
|
42
42
|
lastNotificationResponseBundle
|
|
43
43
|
}
|
|
44
|
+
|
|
45
|
+
AsyncFunction("clearLastNotificationResponseAsync") {
|
|
46
|
+
lastNotificationResponseBundle = null
|
|
47
|
+
null
|
|
48
|
+
}
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
/**
|
|
@@ -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
|
|
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.
|
|
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(
|
|
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,38 @@
|
|
|
1
|
+
package expo.modules.notifications.notifications.interfaces
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Bitmap
|
|
5
|
+
import android.os.Parcelable
|
|
6
|
+
import expo.modules.notifications.notifications.enums.NotificationPriority
|
|
7
|
+
import org.json.JSONObject
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* This interface is implemented by classes representing notification content.
|
|
11
|
+
* I.e. local notifications [NotificationContent] and remote notifications [RemoteNotificationContent].
|
|
12
|
+
*
|
|
13
|
+
* The reason the two classes exist is that one is persisted locally in SharedPreferences, and the other is not.
|
|
14
|
+
* The first is therefore a bit "fragile" and harder to refactor, while the second is easier to change.
|
|
15
|
+
* This interface exists to provide a common API for both classes.
|
|
16
|
+
* */
|
|
17
|
+
interface INotificationContent : Parcelable {
|
|
18
|
+
val title: String?
|
|
19
|
+
val text: String?
|
|
20
|
+
val subtitle: String?
|
|
21
|
+
val badgeCount: Number?
|
|
22
|
+
val shouldPlayDefaultSound: Boolean
|
|
23
|
+
|
|
24
|
+
// soundName is better off as a string (was an Uri) because in RemoteNotification we can obtain the sound name
|
|
25
|
+
// in local notification we store the uri and derive the sound name from it
|
|
26
|
+
val soundName: String?
|
|
27
|
+
val shouldUseDefaultVibrationPattern: Boolean
|
|
28
|
+
val vibrationPattern: LongArray?
|
|
29
|
+
val body: JSONObject?
|
|
30
|
+
val priority: NotificationPriority
|
|
31
|
+
val color: Number?
|
|
32
|
+
val isAutoDismiss: Boolean
|
|
33
|
+
val categoryId: String?
|
|
34
|
+
val isSticky: Boolean
|
|
35
|
+
|
|
36
|
+
fun containsImage(): Boolean
|
|
37
|
+
suspend fun getImage(context: Context): Bitmap?
|
|
38
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
package expo.modules.notifications.notifications.interfaces
|
|
2
|
+
|
|
3
|
+
import expo.modules.notifications.notifications.model.Notification
|
|
4
|
+
import expo.modules.notifications.notifications.model.NotificationBehavior
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* An object capable of building a [Notification] based
|
|
8
|
+
* on a [NotificationContent] spec.
|
|
9
|
+
*/
|
|
10
|
+
interface NotificationBuilder {
|
|
11
|
+
/**
|
|
12
|
+
* Pass in a [Notification] based on which the Android notification should be based.
|
|
13
|
+
*
|
|
14
|
+
* @param notification [Notification] on which the notification should be based.
|
|
15
|
+
* @return The same instance of [NotificationBuilder] updated with the notification.
|
|
16
|
+
*/
|
|
17
|
+
fun setNotification(notification: Notification?): NotificationBuilder?
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Pass in a [NotificationBehavior] if you want to override the behavior
|
|
21
|
+
* of the notification, i.e. whether it should show a heads-up alert, set badge, etc.
|
|
22
|
+
*
|
|
23
|
+
* @param behavior [NotificationBehavior] to which the presentation effect should conform.
|
|
24
|
+
* @return The same instance of [NotificationBuilder] updated with the remote message.
|
|
25
|
+
*/
|
|
26
|
+
fun setAllowedBehavior(behavior: NotificationBehavior?): NotificationBuilder?
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Builds the Android notification based on passed in data.
|
|
30
|
+
*
|
|
31
|
+
* @return Built notification.
|
|
32
|
+
*/
|
|
33
|
+
suspend fun build(): android.app.Notification
|
|
34
|
+
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
package expo.modules.notifications.notifications.interfaces;
|
|
2
2
|
|
|
3
|
-
import android.os.Build;
|
|
4
3
|
import android.os.Parcelable;
|
|
5
4
|
|
|
6
5
|
import androidx.annotation.Nullable;
|
|
7
|
-
import androidx.annotation.RequiresApi;
|
|
8
6
|
|
|
9
7
|
/**
|
|
10
8
|
* An interface specifying source of the notification, to be implemented
|
|
@@ -12,7 +10,6 @@ import androidx.annotation.RequiresApi;
|
|
|
12
10
|
*/
|
|
13
11
|
public interface NotificationTrigger extends Parcelable {
|
|
14
12
|
@Nullable
|
|
15
|
-
@RequiresApi(api = Build.VERSION_CODES.O)
|
|
16
13
|
default String getNotificationChannel() {
|
|
17
14
|
return null;
|
|
18
15
|
}
|
package/android/src/main/java/expo/modules/notifications/notifications/model/Notification.java
CHANGED
|
@@ -6,11 +6,11 @@ import android.os.Parcelable;
|
|
|
6
6
|
import java.util.Date;
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* A class representing a single notification
|
|
9
|
+
* A class representing a single notification. Origin date ({@link #mOriginDate}) is time when it was sent (remote) or when it was posted (local).
|
|
10
10
|
*/
|
|
11
11
|
public class Notification implements Parcelable {
|
|
12
12
|
private NotificationRequest mRequest;
|
|
13
|
-
private Date
|
|
13
|
+
private Date mOriginDate;
|
|
14
14
|
|
|
15
15
|
public Notification(NotificationRequest request) {
|
|
16
16
|
this(request, new Date());
|
|
@@ -18,16 +18,16 @@ public class Notification implements Parcelable {
|
|
|
18
18
|
|
|
19
19
|
public Notification(NotificationRequest request, Date date) {
|
|
20
20
|
mRequest = request;
|
|
21
|
-
|
|
21
|
+
mOriginDate = date;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
protected Notification(Parcel in) {
|
|
25
25
|
mRequest = in.readParcelable(getClass().getClassLoader());
|
|
26
|
-
|
|
26
|
+
mOriginDate = new Date(in.readLong());
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
public Date
|
|
30
|
-
return
|
|
29
|
+
public Date getOriginDate() {
|
|
30
|
+
return mOriginDate;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
public NotificationRequest getNotificationRequest() {
|
|
@@ -54,6 +54,6 @@ public class Notification implements Parcelable {
|
|
|
54
54
|
@Override
|
|
55
55
|
public void writeToParcel(Parcel dest, int flags) {
|
|
56
56
|
dest.writeParcelable(mRequest, 0);
|
|
57
|
-
dest.writeLong(
|
|
57
|
+
dest.writeLong(mOriginDate.getTime());
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
package expo.modules.notifications.notifications.model;
|
|
2
2
|
|
|
3
|
+
import static expo.modules.notifications.notifications.presentation.builders.ExpoNotificationBuilder.META_DATA_LARGE_ICON_KEY;
|
|
4
|
+
|
|
5
|
+
import android.content.Context;
|
|
6
|
+
import android.content.pm.ApplicationInfo;
|
|
7
|
+
import android.content.pm.PackageManager;
|
|
8
|
+
import android.graphics.Bitmap;
|
|
9
|
+
import android.graphics.BitmapFactory;
|
|
3
10
|
import android.net.Uri;
|
|
4
11
|
import android.os.Parcel;
|
|
5
12
|
import android.os.Parcelable;
|
|
13
|
+
import android.util.Log;
|
|
6
14
|
|
|
7
15
|
import org.json.JSONException;
|
|
8
16
|
import org.json.JSONObject;
|
|
@@ -11,15 +19,21 @@ import java.io.IOException;
|
|
|
11
19
|
import java.io.ObjectStreamException;
|
|
12
20
|
import java.io.Serializable;
|
|
13
21
|
|
|
22
|
+
import androidx.annotation.NonNull;
|
|
14
23
|
import androidx.annotation.Nullable;
|
|
15
24
|
|
|
16
25
|
import expo.modules.notifications.notifications.enums.NotificationPriority;
|
|
26
|
+
import expo.modules.notifications.notifications.interfaces.INotificationContent;
|
|
27
|
+
import kotlin.coroutines.Continuation;
|
|
17
28
|
|
|
18
29
|
/**
|
|
19
|
-
* A POJO representing a notification content: title, message, body, etc. Instances
|
|
30
|
+
* A POJO representing a local notification content: title, message, body, etc. Instances
|
|
20
31
|
* should be created using {@link NotificationContent.Builder}.
|
|
32
|
+
*
|
|
33
|
+
* Note that it implements {@link Serializable} interfaces to store the object in the SharedPreferences.
|
|
34
|
+
* Refactoring this class may require a migration strategy for the data stored in SharedPreferences.
|
|
21
35
|
*/
|
|
22
|
-
public class NotificationContent implements Parcelable, Serializable {
|
|
36
|
+
public class NotificationContent implements Parcelable, Serializable, INotificationContent {
|
|
23
37
|
private String mTitle;
|
|
24
38
|
private String mText;
|
|
25
39
|
private String mSubtitle;
|
|
@@ -70,17 +84,39 @@ public class NotificationContent implements Parcelable, Serializable {
|
|
|
70
84
|
return mBadgeCount;
|
|
71
85
|
}
|
|
72
86
|
|
|
73
|
-
|
|
87
|
+
@Override
|
|
88
|
+
public boolean getShouldPlayDefaultSound() {
|
|
74
89
|
return mShouldPlayDefaultSound;
|
|
75
90
|
}
|
|
76
91
|
|
|
92
|
+
@Override
|
|
93
|
+
public boolean getShouldUseDefaultVibrationPattern() {
|
|
94
|
+
return mShouldUseDefaultVibrationPattern;
|
|
95
|
+
}
|
|
96
|
+
|
|
77
97
|
@Nullable
|
|
78
|
-
public
|
|
79
|
-
return mSound;
|
|
98
|
+
public String getSoundName() {
|
|
99
|
+
return mSound != null ? mSound.getLastPathSegment() : null;
|
|
80
100
|
}
|
|
81
101
|
|
|
82
|
-
|
|
83
|
-
|
|
102
|
+
@Nullable
|
|
103
|
+
@Override
|
|
104
|
+
public Object getImage(@NonNull Context context, @NonNull Continuation<? super Bitmap> $completion) {
|
|
105
|
+
try {
|
|
106
|
+
ApplicationInfo ai = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
|
|
107
|
+
if (ai.metaData.containsKey(META_DATA_LARGE_ICON_KEY)) {
|
|
108
|
+
int resourceId = ai.metaData.getInt(META_DATA_LARGE_ICON_KEY);
|
|
109
|
+
return BitmapFactory.decodeResource(context.getResources(), resourceId);
|
|
110
|
+
}
|
|
111
|
+
} catch (PackageManager.NameNotFoundException | ClassCastException e) {
|
|
112
|
+
Log.e("expo-notifications", "Could not have fetched large notification icon.", e);
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@Override
|
|
118
|
+
public boolean containsImage() {
|
|
119
|
+
return true;
|
|
84
120
|
}
|
|
85
121
|
|
|
86
122
|
@Nullable
|
|
@@ -5,6 +5,7 @@ import android.os.Parcelable;
|
|
|
5
5
|
|
|
6
6
|
import java.io.Serializable;
|
|
7
7
|
|
|
8
|
+
import expo.modules.notifications.notifications.interfaces.INotificationContent;
|
|
8
9
|
import expo.modules.notifications.notifications.interfaces.NotificationTrigger;
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -13,16 +14,16 @@ import expo.modules.notifications.notifications.interfaces.NotificationTrigger;
|
|
|
13
14
|
*/
|
|
14
15
|
public class NotificationRequest implements Parcelable, Serializable {
|
|
15
16
|
private String mIdentifier;
|
|
16
|
-
private
|
|
17
|
+
private INotificationContent mContent;
|
|
17
18
|
private NotificationTrigger mTrigger;
|
|
18
19
|
|
|
19
|
-
public NotificationRequest(String identifier,
|
|
20
|
+
public NotificationRequest(String identifier, INotificationContent content, NotificationTrigger trigger) {
|
|
20
21
|
mIdentifier = identifier;
|
|
21
22
|
mContent = content;
|
|
22
23
|
mTrigger = trigger;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
public
|
|
26
|
+
public INotificationContent getContent() {
|
|
26
27
|
return mContent;
|
|
27
28
|
}
|
|
28
29
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
package expo.modules.notifications.notifications.model
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Bitmap
|
|
5
|
+
import android.os.Parcel
|
|
6
|
+
import android.os.Parcelable
|
|
7
|
+
import com.google.firebase.messaging.RemoteMessage
|
|
8
|
+
import expo.modules.notifications.notifications.enums.NotificationPriority
|
|
9
|
+
import expo.modules.notifications.notifications.interfaces.INotificationContent
|
|
10
|
+
import expo.modules.notifications.notifications.presentation.builders.downloadImage
|
|
11
|
+
import org.json.JSONObject
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A POJO representing a remote notification content: title, message, body, etc.
|
|
15
|
+
* The content originates in a RemoteMessage object.
|
|
16
|
+
*
|
|
17
|
+
* Instances of this class are not persisted in SharedPreferences (unlike {@link NotificationContent}. This class does
|
|
18
|
+
* not implement Serializable, but Parcelable ensures we can pass instances between different parts of the application.
|
|
19
|
+
*/
|
|
20
|
+
class RemoteNotificationContent(private val remoteMessage: RemoteMessage) : INotificationContent {
|
|
21
|
+
|
|
22
|
+
constructor(parcel: Parcel) : this(parcel.readParcelable<RemoteMessage>(RemoteMessage::class.java.classLoader)!!)
|
|
23
|
+
|
|
24
|
+
override suspend fun getImage(context: Context): Bitmap? {
|
|
25
|
+
val uri = remoteMessage.notification?.imageUrl
|
|
26
|
+
return uri?.let { downloadImage(it) }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
override fun containsImage(): Boolean {
|
|
30
|
+
return remoteMessage.notification?.imageUrl != null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
override val title: String?
|
|
34
|
+
get() = remoteMessage.notification?.title
|
|
35
|
+
|
|
36
|
+
override val text: String?
|
|
37
|
+
get() = remoteMessage.notification?.body
|
|
38
|
+
|
|
39
|
+
override val shouldPlayDefaultSound: Boolean
|
|
40
|
+
get() = remoteMessage.notification?.sound == null
|
|
41
|
+
|
|
42
|
+
override val soundName: String?
|
|
43
|
+
get() = remoteMessage.notification?.sound
|
|
44
|
+
|
|
45
|
+
override val shouldUseDefaultVibrationPattern: Boolean
|
|
46
|
+
get() = remoteMessage.notification?.defaultVibrateSettings == true
|
|
47
|
+
|
|
48
|
+
override val vibrationPattern: LongArray?
|
|
49
|
+
get() = remoteMessage.notification?.vibrateTimings
|
|
50
|
+
|
|
51
|
+
override val body: JSONObject?
|
|
52
|
+
get() = try {
|
|
53
|
+
remoteMessage.data["body"]?.let { JSONObject(it) }
|
|
54
|
+
} catch (e: Exception) {
|
|
55
|
+
null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
override val priority: NotificationPriority
|
|
59
|
+
get() = when (remoteMessage.priority) {
|
|
60
|
+
RemoteMessage.PRIORITY_HIGH -> NotificationPriority.HIGH
|
|
61
|
+
else -> NotificationPriority.DEFAULT
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
override val color: Number?
|
|
65
|
+
get() = remoteMessage.notification?.color?.let { android.graphics.Color.parseColor(it) }
|
|
66
|
+
|
|
67
|
+
// NOTE the following getter functions are here because the local notification content class has them
|
|
68
|
+
// and this class conforms to the same interface. They are not supported by FCM.
|
|
69
|
+
override val isAutoDismiss: Boolean
|
|
70
|
+
get() = remoteMessage.data["autoDismiss"]?.toBoolean() ?: true
|
|
71
|
+
|
|
72
|
+
override val categoryId: String?
|
|
73
|
+
get() = remoteMessage.data["categoryId"]
|
|
74
|
+
|
|
75
|
+
override val isSticky: Boolean
|
|
76
|
+
get() = remoteMessage.data["sticky"]?.toBoolean() ?: false
|
|
77
|
+
|
|
78
|
+
override val subtitle: String?
|
|
79
|
+
get() = remoteMessage.data["subtitle"]
|
|
80
|
+
|
|
81
|
+
override val badgeCount: Number?
|
|
82
|
+
get() = remoteMessage.data["badge"]?.toIntOrNull()
|
|
83
|
+
|
|
84
|
+
override fun describeContents(): Int = 0
|
|
85
|
+
|
|
86
|
+
override fun writeToParcel(dest: Parcel, flags: Int) {
|
|
87
|
+
dest.writeParcelable(remoteMessage, flags)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
companion object CREATOR : Parcelable.Creator<RemoteNotificationContent> {
|
|
91
|
+
override fun createFromParcel(parcel: Parcel): RemoteNotificationContent {
|
|
92
|
+
return RemoteNotificationContent(parcel)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
override fun newArray(size: Int): Array<RemoteNotificationContent?> {
|
|
96
|
+
return arrayOfNulls(size)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -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
|
+
}
|