expo-notifications 1.0.0-canary-20241008-90b13ad → 1.0.0-canary-20241018-bf4b2f7
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 +8 -0
- package/android/build.gradle +1 -0
- package/android/src/main/java/expo/modules/notifications/notifications/NotificationSerializer.java +5 -53
- package/android/src/main/java/expo/modules/notifications/notifications/channels/managers/AndroidXNotificationsChannelManager.java +4 -1
- package/android/src/main/java/expo/modules/notifications/notifications/debug/DebugLogging.kt +1 -1
- package/android/src/main/java/expo/modules/notifications/notifications/interfaces/NotificationTrigger.kt +19 -0
- package/android/src/main/java/expo/modules/notifications/notifications/model/triggers/FirebaseNotificationTrigger.kt +11 -3
- package/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/BaseNotificationBuilder.kt +1 -1
- package/android/src/main/java/expo/modules/notifications/notifications/scheduling/NotificationScheduler.kt +10 -10
- package/android/src/main/java/expo/modules/notifications/notifications/triggers/NotificationTriggers.kt +195 -0
- package/android/src/main/java/expo/modules/notifications/service/delegates/ExpoPresentationDelegate.kt +1 -1
- package/android/src/main/java/expo/modules/notifications/service/delegates/ExpoSchedulingDelegate.kt +5 -0
- package/build/NotificationPermissions.d.ts +3 -13
- package/build/NotificationPermissions.d.ts.map +1 -1
- package/build/NotificationPermissions.js +1 -15
- package/build/NotificationPermissions.js.map +1 -1
- package/build/Notifications.types.d.ts +1 -2
- package/build/Notifications.types.d.ts.map +1 -1
- package/build/Notifications.types.js +1 -0
- package/build/Notifications.types.js.map +1 -1
- package/ios/EXNotifications/Permissions/EXUserFacingNotificationsPermissionsRequester.m +1 -1
- package/package.json +7 -7
- package/src/NotificationPermissions.ts +2 -23
- package/src/Notifications.types.ts +4 -11
- package/android/src/main/java/expo/modules/notifications/notifications/interfaces/NotificationTrigger.java +0 -16
- package/android/src/main/java/expo/modules/notifications/notifications/triggers/ChannelAwareTrigger.java +0 -49
- package/android/src/main/java/expo/modules/notifications/notifications/triggers/DailyTrigger.java +0 -76
- package/android/src/main/java/expo/modules/notifications/notifications/triggers/DateTrigger.java +0 -64
- package/android/src/main/java/expo/modules/notifications/notifications/triggers/MonthlyTrigger.java +0 -85
- package/android/src/main/java/expo/modules/notifications/notifications/triggers/TimeIntervalTrigger.java +0 -87
- package/android/src/main/java/expo/modules/notifications/notifications/triggers/WeeklyTrigger.java +0 -85
- package/android/src/main/java/expo/modules/notifications/notifications/triggers/YearlyTrigger.java +0 -94
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
|
|
15
15
|
### 🐛 Bug fixes
|
|
16
16
|
|
|
17
|
+
- [ios] fix crash if expo-update reload happens while Notifications.requestPermissionsAsync() is showing native dialog ([#32096](https://github.com/expo/expo/pull/32096) by [@mfazekas](https://github.com/mfazekas))
|
|
18
|
+
- [android] `createNotificationChannel` could return incorrect channel information ([#32000](https://github.com/expo/expo/pull/32000) by [@vonovak](https://github.com/vonovak))
|
|
19
|
+
- [android] fix notifications with `ChannelAwareTrigger` not being presented ([#31999](https://github.com/expo/expo/pull/31999) by [@vonovak](https://github.com/vonovak))
|
|
20
|
+
- export `PermissionStatus` as value, not as type ([#31968](https://github.com/expo/expo/pull/31968) by [@vonovak](https://github.com/vonovak))
|
|
17
21
|
- throw improved error on invalid subscription in removeNotificationSubscription ([#31842](https://github.com/expo/expo/pull/31842) by [@vonovak](https://github.com/vonovak))
|
|
18
22
|
- [android] fix notifications actions not being presented ([#31795](https://github.com/expo/expo/pull/31795) by [@vonovak](https://github.com/vonovak))
|
|
19
23
|
- 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))
|
|
@@ -21,9 +25,12 @@
|
|
|
21
25
|
|
|
22
26
|
### 💡 Others
|
|
23
27
|
|
|
28
|
+
- [android] refactor trigger serialization ([#32032](https://github.com/expo/expo/pull/32032) by [@vonovak](https://github.com/vonovak))
|
|
29
|
+
- [android] simplify DateTrigger ([#32002](https://github.com/expo/expo/pull/32002) by [@vonovak](https://github.com/vonovak))
|
|
24
30
|
- [android] refactor ExpoNotificationBuilder ([#31838](https://github.com/expo/expo/pull/31838) by [@vonovak](https://github.com/vonovak))
|
|
25
31
|
- Warn about limited support in Expo Go ([#31573](https://github.com/expo/expo/pull/31573) by [@vonovak](https://github.com/vonovak))
|
|
26
32
|
- Keep using the legacy event emitter as the module is not fully migrated to Expo Modules API. ([#28946](https://github.com/expo/expo/pull/28946) by [@tsapeta](https://github.com/tsapeta))
|
|
33
|
+
- [Android] Convert trigger Java classes to Kotlin. ([#31856](https://github.com/expo/expo/pull/31856) by [@douglowder](https://github.com/douglowder))
|
|
27
34
|
|
|
28
35
|
## 0.28.17 - 2024-09-17
|
|
29
36
|
|
|
@@ -286,6 +293,7 @@ _This version does not introduce any user-facing changes._
|
|
|
286
293
|
|
|
287
294
|
### 🛠 Breaking changes
|
|
288
295
|
|
|
296
|
+
- remove `usePermissions` hook ([#31905](https://github.com/expo/expo/pull/31905) by [@vonovak](https://github.com/vonovak))
|
|
289
297
|
- [android] Set the "notification number" (sometimes used to increment badge count on some launchers) from the notification payload `badge` field. ([#17171](https://github.com/expo/expo/pull/17171) by [@danstepanov](https://github.com/danstepanov))
|
|
290
298
|
|
|
291
299
|
### 🐛 Bug fixes
|
package/android/build.gradle
CHANGED
package/android/src/main/java/expo/modules/notifications/notifications/NotificationSerializer.java
CHANGED
|
@@ -58,11 +58,12 @@ public class NotificationSerializer {
|
|
|
58
58
|
public static Bundle toBundle(NotificationRequest request) {
|
|
59
59
|
Bundle serializedRequest = new Bundle();
|
|
60
60
|
serializedRequest.putString("identifier", request.getIdentifier());
|
|
61
|
-
|
|
61
|
+
NotificationTrigger requestTrigger = request.getTrigger();
|
|
62
|
+
serializedRequest.putBundle("trigger", requestTrigger == null ? null : requestTrigger.toBundle());
|
|
62
63
|
Bundle content = toBundle(request.getContent());
|
|
63
64
|
Bundle existingContentData = content.getBundle("data");
|
|
64
65
|
if (existingContentData == null) {
|
|
65
|
-
if(
|
|
66
|
+
if(requestTrigger instanceof FirebaseNotificationTrigger trigger) {
|
|
66
67
|
RemoteMessage message = trigger.getRemoteMessage();
|
|
67
68
|
RemoteMessage.Notification notification = message.getNotification();
|
|
68
69
|
Map<String, String> data = message.getData();
|
|
@@ -77,8 +78,8 @@ public class NotificationSerializer {
|
|
|
77
78
|
content.putBundle("data", toBundle(data));
|
|
78
79
|
}
|
|
79
80
|
} else if(
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
requestTrigger instanceof SchedulableNotificationTrigger ||
|
|
82
|
+
requestTrigger == null
|
|
82
83
|
) {
|
|
83
84
|
JSONObject body = request.getContent().getBody();
|
|
84
85
|
if (body != null) {
|
|
@@ -187,55 +188,6 @@ public class NotificationSerializer {
|
|
|
187
188
|
return result;
|
|
188
189
|
}
|
|
189
190
|
|
|
190
|
-
private static Bundle toBundle(@Nullable NotificationTrigger trigger) {
|
|
191
|
-
if (trigger == null) {
|
|
192
|
-
return null;
|
|
193
|
-
}
|
|
194
|
-
Bundle bundle = new Bundle();
|
|
195
|
-
if (trigger instanceof FirebaseNotificationTrigger) {
|
|
196
|
-
bundle.putString("type", "push");
|
|
197
|
-
bundle.putBundle("remoteMessage", RemoteMessageSerializer.toBundle(((FirebaseNotificationTrigger) trigger).getRemoteMessage()));
|
|
198
|
-
} else if (trigger instanceof TimeIntervalTrigger) {
|
|
199
|
-
bundle.putString("type", "timeInterval");
|
|
200
|
-
bundle.putBoolean("repeats", ((TimeIntervalTrigger) trigger).isRepeating());
|
|
201
|
-
bundle.putLong("seconds", ((TimeIntervalTrigger) trigger).getTimeInterval());
|
|
202
|
-
} else if (trigger instanceof DateTrigger) {
|
|
203
|
-
bundle.putString("type", "date");
|
|
204
|
-
bundle.putBoolean("repeats", false);
|
|
205
|
-
bundle.putLong("value", ((DateTrigger) trigger).getTriggerDate().getTime());
|
|
206
|
-
} else if (trigger instanceof DailyTrigger) {
|
|
207
|
-
bundle.putString("type", "daily");
|
|
208
|
-
bundle.putInt("hour", ((DailyTrigger) trigger).getHour());
|
|
209
|
-
bundle.putInt("minute", ((DailyTrigger) trigger).getMinute());
|
|
210
|
-
} else if (trigger instanceof WeeklyTrigger) {
|
|
211
|
-
bundle.putString("type", "weekly");
|
|
212
|
-
bundle.putInt("weekday", ((WeeklyTrigger) trigger).getWeekday());
|
|
213
|
-
bundle.putInt("hour", ((WeeklyTrigger) trigger).getHour());
|
|
214
|
-
bundle.putInt("minute", ((WeeklyTrigger) trigger).getMinute());
|
|
215
|
-
} else if (trigger instanceof MonthlyTrigger) {
|
|
216
|
-
bundle.putString("type", "monthly");
|
|
217
|
-
bundle.putInt("day", ((MonthlyTrigger) trigger).getDay());
|
|
218
|
-
bundle.putInt("hour", ((MonthlyTrigger) trigger).getHour());
|
|
219
|
-
bundle.putInt("minute", ((MonthlyTrigger) trigger).getMinute());
|
|
220
|
-
} else if (trigger instanceof YearlyTrigger) {
|
|
221
|
-
bundle.putString("type", "yearly");
|
|
222
|
-
bundle.putInt("day", ((YearlyTrigger) trigger).getDay());
|
|
223
|
-
bundle.putInt("month", ((YearlyTrigger) trigger).getMonth());
|
|
224
|
-
bundle.putInt("hour", ((YearlyTrigger) trigger).getHour());
|
|
225
|
-
bundle.putInt("minute", ((YearlyTrigger) trigger).getMinute());
|
|
226
|
-
} else {
|
|
227
|
-
bundle.putString("type", "unknown");
|
|
228
|
-
}
|
|
229
|
-
bundle.putString("channelId", getChannelId(trigger));
|
|
230
|
-
|
|
231
|
-
return bundle;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
@Nullable
|
|
235
|
-
private static String getChannelId(NotificationTrigger trigger) {
|
|
236
|
-
return trigger.getNotificationChannel();
|
|
237
|
-
}
|
|
238
|
-
|
|
239
191
|
@NotNull
|
|
240
192
|
public static Bundle toResponseBundleFromExtras(Bundle extras) {
|
|
241
193
|
Bundle serializedContent = new Bundle();
|
|
@@ -80,7 +80,10 @@ public class AndroidXNotificationsChannelManager implements NotificationsChannel
|
|
|
80
80
|
NotificationChannel channel = new NotificationChannel(channelId, name, importance);
|
|
81
81
|
configureChannelWithOptions(channel, channelOptions);
|
|
82
82
|
mNotificationManager.createNotificationChannel(channel);
|
|
83
|
-
return channel
|
|
83
|
+
// We return the channel given by mNotificationManager, not the one we created.
|
|
84
|
+
// see "Note that the created channel may differ from this value." ("this value" = the one we provided)
|
|
85
|
+
// https://developer.android.com/reference/android/app/NotificationManager#createNotificationChannel(android.app.NotificationChannel)
|
|
86
|
+
return mNotificationManager.getNotificationChannel(channelId);
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
// Processing options
|
package/android/src/main/java/expo/modules/notifications/notifications/debug/DebugLogging.kt
CHANGED
|
@@ -73,7 +73,7 @@ object DebugLogging {
|
|
|
73
73
|
notification.notificationRequest.content.subText: ${notification.notificationRequest.content.subText}
|
|
74
74
|
notification.notificationRequest.content.text: ${notification.notificationRequest.content.text}
|
|
75
75
|
notification.notificationRequest.content.sound: ${notification.notificationRequest.content.soundName}
|
|
76
|
-
notification.notificationRequest.content.channelID: ${notification.notificationRequest.trigger.
|
|
76
|
+
notification.notificationRequest.content.channelID: ${notification.notificationRequest.trigger.getNotificationChannel()}
|
|
77
77
|
notification.notificationRequest.content.body: ${notification.notificationRequest.content.body}
|
|
78
78
|
notification.notificationRequest.content.color: ${notification.notificationRequest.content.color}
|
|
79
79
|
notification.notificationRequest.content.vibrationPattern: ${notification.notificationRequest.content.vibrationPattern?.contentToString()}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
package expo.modules.notifications.notifications.interfaces
|
|
2
|
+
|
|
3
|
+
import android.os.Bundle
|
|
4
|
+
import android.os.Parcelable
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* An interface specifying source of the notification, to be implemented
|
|
8
|
+
* by concrete classes.
|
|
9
|
+
*/
|
|
10
|
+
interface NotificationTrigger : Parcelable {
|
|
11
|
+
// these are functions so that we're absolutely sure @Parcelize doesn't try to parcelize them
|
|
12
|
+
// (as opposed to if they were properties)
|
|
13
|
+
|
|
14
|
+
fun getNotificationChannel(): String? {
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
fun toBundle(): Bundle
|
|
19
|
+
}
|
|
@@ -1,30 +1,38 @@
|
|
|
1
1
|
package expo.modules.notifications.notifications.model.triggers
|
|
2
2
|
|
|
3
3
|
import android.os.Build
|
|
4
|
+
import android.os.Bundle
|
|
4
5
|
import android.os.Parcel
|
|
5
6
|
import android.os.Parcelable
|
|
6
7
|
import androidx.annotation.RequiresApi
|
|
8
|
+
import androidx.core.os.bundleOf
|
|
7
9
|
import com.google.firebase.messaging.RemoteMessage
|
|
10
|
+
import expo.modules.notifications.notifications.RemoteMessageSerializer
|
|
8
11
|
import expo.modules.notifications.notifications.interfaces.NotificationTrigger
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
14
|
* A trigger representing an incoming remote Firebase notification.
|
|
12
15
|
*/
|
|
13
|
-
class FirebaseNotificationTrigger(
|
|
16
|
+
class FirebaseNotificationTrigger(val remoteMessage: RemoteMessage) : NotificationTrigger {
|
|
14
17
|
|
|
15
18
|
private constructor(parcel: Parcel) : this(
|
|
16
19
|
parcel.readParcelable(FirebaseNotificationTrigger::class.java.classLoader)
|
|
17
20
|
?: throw IllegalArgumentException("RemoteMessage from readParcelable must not be null")
|
|
18
21
|
)
|
|
19
22
|
|
|
20
|
-
fun getRemoteMessage(): RemoteMessage = remoteMessage
|
|
21
|
-
|
|
22
23
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
|
23
24
|
override fun getNotificationChannel(): String? {
|
|
24
25
|
val channelId = remoteMessage.notification?.channelId ?: remoteMessage.data["channelId"]
|
|
25
26
|
return channelId ?: super.getNotificationChannel()
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
override fun toBundle(): Bundle {
|
|
30
|
+
return bundleOf(
|
|
31
|
+
"type" to "push",
|
|
32
|
+
"remoteMessage" to RemoteMessageSerializer.toBundle(remoteMessage)
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
28
36
|
override fun describeContents(): Int {
|
|
29
37
|
return 0
|
|
30
38
|
}
|
|
@@ -58,7 +58,7 @@ abstract class BaseNotificationBuilder protected constructor(protected val conte
|
|
|
58
58
|
return fallbackNotificationChannel!!.id
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
val requestedChannelId = trigger.
|
|
61
|
+
val requestedChannelId = trigger.getNotificationChannel()
|
|
62
62
|
?: return fallbackNotificationChannel!!.id
|
|
63
63
|
|
|
64
64
|
val channelForRequestedId =
|
|
@@ -157,14 +157,14 @@ open class NotificationScheduler : Module() {
|
|
|
157
157
|
val seconds = params["seconds"] as? Number
|
|
158
158
|
?: throw InvalidArgumentException("Invalid value provided as interval of trigger.")
|
|
159
159
|
|
|
160
|
-
TimeIntervalTrigger(seconds.toLong(), params.getBoolean("repeats")
|
|
160
|
+
TimeIntervalTrigger(channelId, seconds.toLong(), params.getBoolean("repeats"))
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
"date" -> {
|
|
164
164
|
val timestamp = params["timestamp"] as? Number
|
|
165
165
|
?: throw InvalidArgumentException("Invalid value provided as date of trigger.")
|
|
166
166
|
|
|
167
|
-
DateTrigger(timestamp.toLong()
|
|
167
|
+
DateTrigger(channelId, timestamp.toLong())
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
"daily" -> {
|
|
@@ -176,9 +176,9 @@ open class NotificationScheduler : Module() {
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
DailyTrigger(
|
|
179
|
+
channelId,
|
|
179
180
|
hour.toInt(),
|
|
180
|
-
minute.toInt()
|
|
181
|
-
channelId
|
|
181
|
+
minute.toInt()
|
|
182
182
|
)
|
|
183
183
|
}
|
|
184
184
|
|
|
@@ -191,10 +191,10 @@ open class NotificationScheduler : Module() {
|
|
|
191
191
|
throw InvalidArgumentException("Invalid value(s) provided for weekly trigger.")
|
|
192
192
|
}
|
|
193
193
|
WeeklyTrigger(
|
|
194
|
+
channelId,
|
|
194
195
|
weekday.toInt(),
|
|
195
196
|
hour.toInt(),
|
|
196
|
-
minute.toInt()
|
|
197
|
-
channelId
|
|
197
|
+
minute.toInt()
|
|
198
198
|
)
|
|
199
199
|
}
|
|
200
200
|
|
|
@@ -208,10 +208,10 @@ open class NotificationScheduler : Module() {
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
MonthlyTrigger(
|
|
211
|
+
channelId,
|
|
211
212
|
day.toInt(),
|
|
212
213
|
hour.toInt(),
|
|
213
|
-
minute.toInt()
|
|
214
|
-
channelId
|
|
214
|
+
minute.toInt()
|
|
215
215
|
)
|
|
216
216
|
}
|
|
217
217
|
|
|
@@ -226,11 +226,11 @@ open class NotificationScheduler : Module() {
|
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
YearlyTrigger(
|
|
229
|
+
channelId,
|
|
229
230
|
day.toInt(),
|
|
230
231
|
month.toInt(),
|
|
231
232
|
hour.toInt(),
|
|
232
|
-
minute.toInt()
|
|
233
|
-
channelId
|
|
233
|
+
minute.toInt()
|
|
234
234
|
)
|
|
235
235
|
}
|
|
236
236
|
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
package expo.modules.notifications.notifications.triggers
|
|
2
|
+
|
|
3
|
+
import android.os.Bundle
|
|
4
|
+
import androidx.core.os.bundleOf
|
|
5
|
+
import expo.modules.notifications.notifications.interfaces.NotificationTrigger
|
|
6
|
+
import expo.modules.notifications.notifications.interfaces.SchedulableNotificationTrigger
|
|
7
|
+
import kotlinx.parcelize.IgnoredOnParcel
|
|
8
|
+
import kotlinx.parcelize.Parcelize
|
|
9
|
+
import java.io.Serializable
|
|
10
|
+
import java.util.Calendar
|
|
11
|
+
import java.util.Date
|
|
12
|
+
|
|
13
|
+
@Parcelize
|
|
14
|
+
open class ChannelAwareTrigger(open val channelId: String?) :
|
|
15
|
+
NotificationTrigger, Serializable {
|
|
16
|
+
|
|
17
|
+
override fun describeContents(): Int = 0
|
|
18
|
+
|
|
19
|
+
override fun getNotificationChannel() = channelId
|
|
20
|
+
|
|
21
|
+
override fun toBundle() = bundleWithChannelId()
|
|
22
|
+
|
|
23
|
+
protected fun bundleWithChannelId(vararg pairs: Pair<String, Any?>): Bundle {
|
|
24
|
+
return bundleOf("channelId" to channelId, *pairs)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A schedulable trigger representing a notification to be scheduled once per day.
|
|
30
|
+
*/
|
|
31
|
+
@Parcelize
|
|
32
|
+
class DailyTrigger(override val channelId: String?, val hour: Int, val minute: Int) : ChannelAwareTrigger(channelId), SchedulableNotificationTrigger {
|
|
33
|
+
override fun toBundle() = bundleWithChannelId(
|
|
34
|
+
"type" to "daily",
|
|
35
|
+
"hour" to hour,
|
|
36
|
+
"minute" to minute
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
override fun nextTriggerDate(): Date? {
|
|
40
|
+
val nextTriggerDate = Calendar.getInstance()
|
|
41
|
+
nextTriggerDate[Calendar.HOUR_OF_DAY] = hour
|
|
42
|
+
nextTriggerDate[Calendar.MINUTE] = minute
|
|
43
|
+
nextTriggerDate[Calendar.SECOND] = 0
|
|
44
|
+
nextTriggerDate[Calendar.MILLISECOND] = 0
|
|
45
|
+
val rightNow = Calendar.getInstance()
|
|
46
|
+
if (nextTriggerDate.before(rightNow)) {
|
|
47
|
+
nextTriggerDate.add(Calendar.DATE, 1)
|
|
48
|
+
}
|
|
49
|
+
return nextTriggerDate.time
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A schedulable trigger representing notification to be scheduled only once at a given moment of time.
|
|
55
|
+
*/
|
|
56
|
+
@Parcelize
|
|
57
|
+
class DateTrigger(override val channelId: String?, val timestamp: Long) : ChannelAwareTrigger(channelId), SchedulableNotificationTrigger {
|
|
58
|
+
|
|
59
|
+
override fun toBundle() = bundleWithChannelId(
|
|
60
|
+
"type" to "date",
|
|
61
|
+
"repeats" to false,
|
|
62
|
+
"value" to timestamp
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
override fun nextTriggerDate(): Date? {
|
|
66
|
+
val now = Date()
|
|
67
|
+
val triggerDate = Date(timestamp)
|
|
68
|
+
|
|
69
|
+
if (triggerDate.before(now)) {
|
|
70
|
+
return null
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return triggerDate
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A schedulable trigger representing a notification to be scheduled once per month.
|
|
79
|
+
*/
|
|
80
|
+
@Parcelize
|
|
81
|
+
class MonthlyTrigger(override val channelId: String?, val day: Int, val hour: Int, val minute: Int) : ChannelAwareTrigger(channelId), SchedulableNotificationTrigger {
|
|
82
|
+
override fun toBundle() = bundleWithChannelId(
|
|
83
|
+
"type" to "monthly",
|
|
84
|
+
"day" to day,
|
|
85
|
+
"hour" to hour,
|
|
86
|
+
"minute" to minute
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
override fun nextTriggerDate(): Date? {
|
|
90
|
+
val nextTriggerDate = Calendar.getInstance()
|
|
91
|
+
nextTriggerDate[Calendar.DATE] = day
|
|
92
|
+
nextTriggerDate[Calendar.HOUR_OF_DAY] = hour
|
|
93
|
+
nextTriggerDate[Calendar.MINUTE] = minute
|
|
94
|
+
nextTriggerDate[Calendar.SECOND] = 0
|
|
95
|
+
nextTriggerDate[Calendar.MILLISECOND] = 0
|
|
96
|
+
val rightNow = Calendar.getInstance()
|
|
97
|
+
if (nextTriggerDate.before(rightNow)) {
|
|
98
|
+
nextTriggerDate.add(Calendar.MONTH, 1)
|
|
99
|
+
}
|
|
100
|
+
return nextTriggerDate.time
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* A schedulable trigger representing notification to be scheduled after X milliseconds,
|
|
106
|
+
* optionally repeating.
|
|
107
|
+
*
|
|
108
|
+
*
|
|
109
|
+
* *Note: The implementation ensures that the trigger times do not drift away too much from the
|
|
110
|
+
* * initial time, so eg. a trigger started at 11111000 time repeated every 1000 ms should always
|
|
111
|
+
* * trigger around …000 timestamp.*
|
|
112
|
+
*/
|
|
113
|
+
@Parcelize
|
|
114
|
+
class TimeIntervalTrigger(override val channelId: String?, val timeInterval: Long, val isRepeating: Boolean) : ChannelAwareTrigger(channelId), SchedulableNotificationTrigger {
|
|
115
|
+
@IgnoredOnParcel
|
|
116
|
+
private var triggerDate = Date(Date().time + timeInterval * 1000)
|
|
117
|
+
|
|
118
|
+
override fun toBundle() = bundleWithChannelId(
|
|
119
|
+
"type" to "timeInterval",
|
|
120
|
+
"repeats" to isRepeating,
|
|
121
|
+
"seconds" to timeInterval
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
override fun nextTriggerDate(): Date? {
|
|
125
|
+
val now = Date()
|
|
126
|
+
|
|
127
|
+
if (isRepeating) {
|
|
128
|
+
while (triggerDate.before(now)) {
|
|
129
|
+
triggerDate.time += timeInterval * 1000
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (triggerDate.before(now)) {
|
|
134
|
+
return null
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return triggerDate
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* A schedulable trigger representing a notification to be scheduled once per week.
|
|
143
|
+
*/
|
|
144
|
+
@Parcelize
|
|
145
|
+
class WeeklyTrigger(override val channelId: String?, val weekday: Int, val hour: Int, val minute: Int) : ChannelAwareTrigger(channelId), SchedulableNotificationTrigger {
|
|
146
|
+
override fun toBundle() = bundleWithChannelId(
|
|
147
|
+
"type" to "weekly",
|
|
148
|
+
"weekday" to weekday,
|
|
149
|
+
"hour" to hour,
|
|
150
|
+
"minute" to minute
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
override fun nextTriggerDate(): Date? {
|
|
154
|
+
val nextTriggerDate = Calendar.getInstance()
|
|
155
|
+
nextTriggerDate[Calendar.DAY_OF_WEEK] = weekday
|
|
156
|
+
nextTriggerDate[Calendar.HOUR_OF_DAY] = hour
|
|
157
|
+
nextTriggerDate[Calendar.MINUTE] = minute
|
|
158
|
+
nextTriggerDate[Calendar.SECOND] = 0
|
|
159
|
+
nextTriggerDate[Calendar.MILLISECOND] = 0
|
|
160
|
+
val rightNow = Calendar.getInstance()
|
|
161
|
+
if (nextTriggerDate.before(rightNow)) {
|
|
162
|
+
nextTriggerDate.add(Calendar.DAY_OF_WEEK_IN_MONTH, 1)
|
|
163
|
+
}
|
|
164
|
+
return nextTriggerDate.time
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* A schedulable trigger representing a notification to be scheduled once per year.
|
|
170
|
+
*/
|
|
171
|
+
@Parcelize
|
|
172
|
+
class YearlyTrigger(override val channelId: String?, val day: Int, val month: Int, val hour: Int, val minute: Int) : ChannelAwareTrigger(channelId), SchedulableNotificationTrigger {
|
|
173
|
+
override fun toBundle() = bundleWithChannelId(
|
|
174
|
+
"type" to "yearly",
|
|
175
|
+
"day" to day,
|
|
176
|
+
"month" to month,
|
|
177
|
+
"hour" to hour,
|
|
178
|
+
"minute" to minute
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
override fun nextTriggerDate(): Date? {
|
|
182
|
+
val nextTriggerDate = Calendar.getInstance()
|
|
183
|
+
nextTriggerDate[Calendar.DATE] = day
|
|
184
|
+
nextTriggerDate[Calendar.MONTH] = month
|
|
185
|
+
nextTriggerDate[Calendar.HOUR_OF_DAY] = hour
|
|
186
|
+
nextTriggerDate[Calendar.MINUTE] = minute
|
|
187
|
+
nextTriggerDate[Calendar.SECOND] = 0
|
|
188
|
+
nextTriggerDate[Calendar.MILLISECOND] = 0
|
|
189
|
+
val rightNow = Calendar.getInstance()
|
|
190
|
+
if (nextTriggerDate.before(rightNow)) {
|
|
191
|
+
nextTriggerDate.add(Calendar.YEAR, 1)
|
|
192
|
+
}
|
|
193
|
+
return nextTriggerDate.time
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -115,7 +115,7 @@ open class ExpoPresentationDelegate(
|
|
|
115
115
|
|
|
116
116
|
private fun getNotificationSoundUri(notification: Notification): Uri? {
|
|
117
117
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
118
|
-
notification.notificationRequest.trigger.
|
|
118
|
+
notification.notificationRequest.trigger.getNotificationChannel()?.let {
|
|
119
119
|
notificationManager.getNotificationChannel(it)?.sound
|
|
120
120
|
}
|
|
121
121
|
} else {
|
package/android/src/main/java/expo/modules/notifications/service/delegates/ExpoSchedulingDelegate.kt
CHANGED
|
@@ -9,6 +9,7 @@ import androidx.core.app.AlarmManagerCompat
|
|
|
9
9
|
import expo.modules.notifications.notifications.interfaces.SchedulableNotificationTrigger
|
|
10
10
|
import expo.modules.notifications.notifications.model.Notification
|
|
11
11
|
import expo.modules.notifications.notifications.model.NotificationRequest
|
|
12
|
+
import expo.modules.notifications.notifications.triggers.ChannelAwareTrigger
|
|
12
13
|
import expo.modules.notifications.service.NotificationsService
|
|
13
14
|
import expo.modules.notifications.service.interfaces.SchedulingDelegate
|
|
14
15
|
import java.io.IOException
|
|
@@ -50,6 +51,10 @@ class ExpoSchedulingDelegate(protected val context: Context) : SchedulingDelegat
|
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
if (request.trigger !is SchedulableNotificationTrigger) {
|
|
54
|
+
if (request.trigger is ChannelAwareTrigger) {
|
|
55
|
+
NotificationsService.receive(context, Notification(request))
|
|
56
|
+
return
|
|
57
|
+
}
|
|
53
58
|
throw IllegalArgumentException("Notification request \"${request.identifier}\" does not have a schedulable trigger (it's ${request.trigger}). Refusing to schedule.")
|
|
54
59
|
}
|
|
55
60
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NotificationPermissionsRequest
|
|
1
|
+
import { NotificationPermissionsRequest } from './NotificationPermissions.types';
|
|
2
2
|
/**
|
|
3
3
|
* Calling this function checks current permissions settings related to notifications.
|
|
4
4
|
* It lets you verify whether the app is currently allowed to display alerts, play sounds, etc.
|
|
@@ -18,7 +18,7 @@ import { NotificationPermissionsRequest, NotificationPermissionsStatus } from '.
|
|
|
18
18
|
* ```
|
|
19
19
|
* @header permissions
|
|
20
20
|
*/
|
|
21
|
-
export declare function getPermissionsAsync(): Promise<NotificationPermissionsStatus>;
|
|
21
|
+
export declare function getPermissionsAsync(): Promise<import("./NotificationPermissions.types").NotificationPermissionsStatus>;
|
|
22
22
|
/**
|
|
23
23
|
* Prompts the user for notification permissions according to request. **Request defaults to asking the user to allow displaying alerts,
|
|
24
24
|
* setting badge count and playing sounds**.
|
|
@@ -41,15 +41,5 @@ export declare function getPermissionsAsync(): Promise<NotificationPermissionsSt
|
|
|
41
41
|
* ```
|
|
42
42
|
* @header permissions
|
|
43
43
|
*/
|
|
44
|
-
export declare function requestPermissionsAsync(permissions?: NotificationPermissionsRequest): Promise<NotificationPermissionsStatus>;
|
|
45
|
-
/**
|
|
46
|
-
* Check or request permissions to send and receive push notifications.
|
|
47
|
-
* This uses both `requestPermissionsAsync` and `getPermissionsAsync` to interact with the permissions.
|
|
48
|
-
* @example
|
|
49
|
-
* ```ts
|
|
50
|
-
* const [permissionResponse, requestPermission] = Notifications.usePermissions();
|
|
51
|
-
* ```
|
|
52
|
-
* @header permission
|
|
53
|
-
*/
|
|
54
|
-
export declare const usePermissions: (options?: import("expo-modules-core").PermissionHookOptions<NotificationPermissionsRequest> | undefined) => [NotificationPermissionsStatus | null, () => Promise<NotificationPermissionsStatus>, () => Promise<NotificationPermissionsStatus>];
|
|
44
|
+
export declare function requestPermissionsAsync(permissions?: NotificationPermissionsRequest): Promise<import("./NotificationPermissions.types").NotificationPermissionsStatus>;
|
|
55
45
|
//# sourceMappingURL=NotificationPermissions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationPermissions.d.ts","sourceRoot":"","sources":["../src/NotificationPermissions.ts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"NotificationPermissions.d.ts","sourceRoot":"","sources":["../src/NotificationPermissions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,8BAA8B,EAAE,MAAM,iCAAiC,CAAC;AAGjF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,mBAAmB,qFAMxC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,uBAAuB,CAAC,WAAW,CAAC,EAAE,8BAA8B,oFAczF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Platform, UnavailabilityError } from 'expo-modules-core';
|
|
2
2
|
import NotificationPermissionsModule from './NotificationPermissionsModule';
|
|
3
3
|
/**
|
|
4
4
|
* Calling this function checks current permissions settings related to notifications.
|
|
@@ -61,18 +61,4 @@ export async function requestPermissionsAsync(permissions) {
|
|
|
61
61
|
const requestedPlatformPermissions = requestedPermissions[Platform.OS];
|
|
62
62
|
return await NotificationPermissionsModule.requestPermissionsAsync(requestedPlatformPermissions);
|
|
63
63
|
}
|
|
64
|
-
// @needsAudit
|
|
65
|
-
/**
|
|
66
|
-
* Check or request permissions to send and receive push notifications.
|
|
67
|
-
* This uses both `requestPermissionsAsync` and `getPermissionsAsync` to interact with the permissions.
|
|
68
|
-
* @example
|
|
69
|
-
* ```ts
|
|
70
|
-
* const [permissionResponse, requestPermission] = Notifications.usePermissions();
|
|
71
|
-
* ```
|
|
72
|
-
* @header permission
|
|
73
|
-
*/
|
|
74
|
-
export const usePermissions = createPermissionHook({
|
|
75
|
-
requestMethod: requestPermissionsAsync,
|
|
76
|
-
getMethod: getPermissionsAsync,
|
|
77
|
-
});
|
|
78
64
|
//# sourceMappingURL=NotificationPermissions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationPermissions.js","sourceRoot":"","sources":["../src/NotificationPermissions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"NotificationPermissions.js","sourceRoot":"","sources":["../src/NotificationPermissions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGlE,OAAO,6BAA6B,MAAM,iCAAiC,CAAC;AAE5E;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,IAAI,CAAC,6BAA6B,CAAC,mBAAmB,EAAE;QACtD,MAAM,IAAI,mBAAmB,CAAC,eAAe,EAAE,qBAAqB,CAAC,CAAC;KACvE;IAED,OAAO,MAAM,6BAA6B,CAAC,mBAAmB,EAAE,CAAC;AACnE,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,WAA4C;IACxF,IAAI,CAAC,6BAA6B,CAAC,uBAAuB,EAAE;QAC1D,MAAM,IAAI,mBAAmB,CAAC,eAAe,EAAE,yBAAyB,CAAC,CAAC;KAC3E;IAED,MAAM,oBAAoB,GAAG,WAAW,IAAI;QAC1C,GAAG,EAAE;YACH,UAAU,EAAE,IAAI;YAChB,UAAU,EAAE,IAAI;YAChB,UAAU,EAAE,IAAI;SACjB;KACF,CAAC;IACF,MAAM,4BAA4B,GAAG,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACvE,OAAO,MAAM,6BAA6B,CAAC,uBAAuB,CAAC,4BAA4B,CAAC,CAAC;AACnG,CAAC","sourcesContent":["import { Platform, UnavailabilityError } from 'expo-modules-core';\n\nimport { NotificationPermissionsRequest } from './NotificationPermissions.types';\nimport NotificationPermissionsModule from './NotificationPermissionsModule';\n\n/**\n * Calling this function checks current permissions settings related to notifications.\n * It lets you verify whether the app is currently allowed to display alerts, play sounds, etc.\n * There is no user-facing effect of calling this.\n * @return It returns a `Promise` resolving to an object represents permission settings ([`NotificationPermissionsStatus`](#notificationpermissionsstatus)).\n * On iOS, make sure you [properly interpret the permissions response](#interpret-the-ios-permissions-response).\n * @example Check if the app is allowed to send any type of notifications (interrupting and non-interrupting–provisional on iOS).\n * ```ts\n * import * as Notifications from 'expo-notifications';\n *\n * export async function allowsNotificationsAsync() {\n * const settings = await Notifications.getPermissionsAsync();\n * return (\n * settings.granted || settings.ios?.status === Notifications.IosAuthorizationStatus.PROVISIONAL\n * );\n * }\n * ```\n * @header permissions\n */\nexport async function getPermissionsAsync() {\n if (!NotificationPermissionsModule.getPermissionsAsync) {\n throw new UnavailabilityError('Notifications', 'getPermissionsAsync');\n }\n\n return await NotificationPermissionsModule.getPermissionsAsync();\n}\n\n/**\n * Prompts the user for notification permissions according to request. **Request defaults to asking the user to allow displaying alerts,\n * setting badge count and playing sounds**.\n * @param permissions An object representing configuration for the request scope.\n * @return It returns a Promise resolving to an object represents permission settings ([`NotificationPermissionsStatus`](#notificationpermissionsstatus)).\n * On iOS, make sure you [properly interpret the permissions response](#interpret-the-ios-permissions-response).\n * @example Prompts the user to allow the app to show alerts, play sounds, set badge count and let Siri read out messages through AirPods.\n * ```ts\n * import * as Notifications from 'expo-notifications';\n *\n * export function requestPermissionsAsync() {\n * return await Notifications.requestPermissionsAsync({\n * ios: {\n * allowAlert: true,\n * allowBadge: true,\n * allowSound: true,\n * },\n * });\n * }\n * ```\n * @header permissions\n */\nexport async function requestPermissionsAsync(permissions?: NotificationPermissionsRequest) {\n if (!NotificationPermissionsModule.requestPermissionsAsync) {\n throw new UnavailabilityError('Notifications', 'requestPermissionsAsync');\n }\n\n const requestedPermissions = permissions ?? {\n ios: {\n allowAlert: true,\n allowBadge: true,\n allowSound: true,\n },\n };\n const requestedPlatformPermissions = requestedPermissions[Platform.OS];\n return await NotificationPermissionsModule.requestPermissionsAsync(requestedPlatformPermissions);\n}\n"]}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { PermissionExpiration, PermissionResponse, PermissionStatus, EventSubscription } from 'expo-modules-core';
|
|
2
1
|
/**
|
|
3
2
|
* An object represents a notification delivered by a push notification system.
|
|
4
3
|
*
|
|
@@ -702,5 +701,5 @@ export type NotificationCategoryOptions = {
|
|
|
702
701
|
*/
|
|
703
702
|
allowAnnouncement?: boolean;
|
|
704
703
|
};
|
|
705
|
-
export
|
|
704
|
+
export { PermissionExpiration, PermissionResponse, EventSubscription, PermissionStatus, } from 'expo-modules-core';
|
|
706
705
|
//# sourceMappingURL=Notifications.types.d.ts.map
|