expo-notifications 0.24.1 → 0.25.0

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 (85) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/android/build.gradle +49 -30
  3. package/android/src/main/java/expo/modules/notifications/Exceptions.kt +22 -0
  4. package/android/src/main/java/expo/modules/notifications/NotificationsPackage.java +3 -33
  5. package/android/src/main/java/expo/modules/notifications/Utils.kt +19 -0
  6. package/android/src/main/java/expo/modules/notifications/badge/BadgeHelper.kt +24 -0
  7. package/android/src/main/java/expo/modules/notifications/badge/BadgeModule.kt +14 -29
  8. package/android/src/main/java/expo/modules/notifications/notifications/background/BackgroundRemoteNotificationTaskConsumer.java +2 -6
  9. package/android/src/main/java/expo/modules/notifications/notifications/background/ExpoBackgroundNotificationTasksModule.kt +33 -0
  10. package/android/src/main/java/expo/modules/notifications/notifications/categories/ExpoNotificationCategoriesModule.kt +149 -0
  11. package/android/src/main/java/expo/modules/notifications/notifications/channels/NotificationChannelGroupManagerModule.kt +70 -0
  12. package/android/src/main/java/expo/modules/notifications/notifications/channels/NotificationChannelManagerModule.kt +83 -0
  13. package/android/src/main/java/expo/modules/notifications/notifications/emitting/NotificationsEmitter.kt +78 -0
  14. package/android/src/main/java/expo/modules/notifications/notifications/handling/NotificationsHandler.kt +127 -0
  15. package/android/src/main/java/expo/modules/notifications/notifications/handling/SingleNotificationHandlerTask.java +1 -1
  16. package/android/src/main/java/expo/modules/notifications/notifications/presentation/ExpoNotificationPresentationModule.kt +113 -0
  17. package/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ExpoNotificationBuilder.java +1 -5
  18. package/android/src/main/java/expo/modules/notifications/notifications/scheduling/NotificationScheduler.kt +235 -0
  19. package/android/src/main/java/expo/modules/notifications/permissions/NotificationPermissionsModule.kt +36 -56
  20. package/android/src/main/java/expo/modules/notifications/serverregistration/ServerRegistrationModule.kt +32 -0
  21. package/android/src/main/java/expo/modules/notifications/service/NotificationsService.kt +1 -0
  22. package/android/src/main/java/expo/modules/notifications/service/delegates/ExpoPresentationDelegate.kt +0 -6
  23. package/android/src/main/java/expo/modules/notifications/tokens/PushTokenModule.kt +91 -0
  24. package/build/BackgroundNotificationTasksModule.native.d.ts.map +1 -1
  25. package/build/BackgroundNotificationTasksModule.native.js +2 -2
  26. package/build/BackgroundNotificationTasksModule.native.js.map +1 -1
  27. package/build/BadgeModule.native.d.ts.map +1 -1
  28. package/build/BadgeModule.native.js +4 -3
  29. package/build/BadgeModule.native.js.map +1 -1
  30. package/build/NotificationCategoriesModule.native.d.ts.map +1 -1
  31. package/build/NotificationCategoriesModule.native.js +2 -2
  32. package/build/NotificationCategoriesModule.native.js.map +1 -1
  33. package/build/NotificationChannelGroupManager.native.d.ts.map +1 -1
  34. package/build/NotificationChannelGroupManager.native.js +2 -2
  35. package/build/NotificationChannelGroupManager.native.js.map +1 -1
  36. package/build/NotificationChannelManager.native.d.ts.map +1 -1
  37. package/build/NotificationChannelManager.native.js +2 -2
  38. package/build/NotificationChannelManager.native.js.map +1 -1
  39. package/build/NotificationPermissionsModule.native.d.ts.map +1 -1
  40. package/build/NotificationPermissionsModule.native.js +2 -2
  41. package/build/NotificationPermissionsModule.native.js.map +1 -1
  42. package/build/NotificationPresenterModule.native.d.ts.map +1 -1
  43. package/build/NotificationPresenterModule.native.js +2 -2
  44. package/build/NotificationPresenterModule.native.js.map +1 -1
  45. package/build/NotificationScheduler.native.d.ts.map +1 -1
  46. package/build/NotificationScheduler.native.js +2 -2
  47. package/build/NotificationScheduler.native.js.map +1 -1
  48. package/build/NotificationsEmitterModule.native.d.ts.map +1 -1
  49. package/build/NotificationsEmitterModule.native.js +2 -2
  50. package/build/NotificationsEmitterModule.native.js.map +1 -1
  51. package/build/NotificationsHandlerModule.native.d.ts.map +1 -1
  52. package/build/NotificationsHandlerModule.native.js +2 -2
  53. package/build/NotificationsHandlerModule.native.js.map +1 -1
  54. package/build/PushTokenManager.native.d.ts.map +1 -1
  55. package/build/PushTokenManager.native.js +2 -2
  56. package/build/PushTokenManager.native.js.map +1 -1
  57. package/build/ServerRegistrationModule.native.d.ts.map +1 -1
  58. package/build/ServerRegistrationModule.native.js +2 -2
  59. package/build/ServerRegistrationModule.native.js.map +1 -1
  60. package/expo-module.config.json +20 -0
  61. package/ios/EXNotifications/Notifications/Background/EXBackgroundRemoteNotificationConsumer.m +2 -4
  62. package/package.json +4 -4
  63. package/src/BackgroundNotificationTasksModule.native.ts +4 -2
  64. package/src/BadgeModule.native.ts +5 -3
  65. package/src/NotificationCategoriesModule.native.ts +4 -2
  66. package/src/NotificationChannelGroupManager.native.ts +4 -2
  67. package/src/NotificationChannelManager.native.ts +2 -2
  68. package/src/NotificationPermissionsModule.native.ts +4 -2
  69. package/src/NotificationPresenterModule.native.ts +2 -2
  70. package/src/NotificationScheduler.native.ts +2 -2
  71. package/src/NotificationsEmitterModule.native.ts +2 -2
  72. package/src/NotificationsHandlerModule.native.ts +2 -2
  73. package/src/PushTokenManager.native.ts +2 -2
  74. package/src/ServerRegistrationModule.native.ts +4 -2
  75. package/android/src/main/java/expo/modules/notifications/notifications/background/ExpoBackgroundNotificationTasksModule.java +0 -50
  76. package/android/src/main/java/expo/modules/notifications/notifications/categories/ExpoNotificationCategoriesModule.java +0 -124
  77. package/android/src/main/java/expo/modules/notifications/notifications/channels/NotificationChannelGroupManagerModule.java +0 -98
  78. package/android/src/main/java/expo/modules/notifications/notifications/channels/NotificationChannelManagerModule.java +0 -109
  79. package/android/src/main/java/expo/modules/notifications/notifications/emitting/NotificationsEmitter.java +0 -98
  80. package/android/src/main/java/expo/modules/notifications/notifications/handling/NotificationsHandler.java +0 -136
  81. package/android/src/main/java/expo/modules/notifications/notifications/presentation/ExpoNotificationPresentationModule.java +0 -116
  82. package/android/src/main/java/expo/modules/notifications/notifications/scheduling/NotificationScheduler.java +0 -222
  83. package/android/src/main/java/expo/modules/notifications/serverregistration/ServerRegistrationModule.java +0 -41
  84. package/android/src/main/java/expo/modules/notifications/tokens/PushTokenModule.java +0 -116
  85. package/unimodule.json +0 -4
package/CHANGELOG.md CHANGED
@@ -10,6 +10,24 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 0.25.0 — 2023-10-17
14
+
15
+ ### 🛠 Breaking changes
16
+
17
+ - Dropped support for Android SDK 21 and 22. ([#24201](https://github.com/expo/expo/pull/24201) by [@behenate](https://github.com/behenate))
18
+
19
+ ### 🐛 Bug fixes
20
+
21
+ - Send background notifications through when the app is in the foreground ([#24684](https://github.com/expo/expo/pull/24684) by [@kadikraman](https://github.com/kadikraman))
22
+
23
+ ### 💡 Others
24
+
25
+ - Migrated codebase to use Expo Modules API. ([#24499](https://github.com/expo/expo/pull/24499) by [@lukmccall](https://github.com/lukmccall))
26
+
27
+ ## 0.24.2 — 2023-09-18
28
+
29
+ _This version does not introduce any user-facing changes._
30
+
13
31
  ## 0.24.1 — 2023-09-15
14
32
 
15
33
  ### 💡 Others
@@ -3,15 +3,20 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '0.24.1'
7
-
8
- buildscript {
9
- def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
10
- if (expoModulesCorePlugin.exists()) {
11
- apply from: expoModulesCorePlugin
12
- applyKotlinExpoModulesCorePlugin()
6
+ version = '0.25.0'
7
+
8
+ def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
9
+ if (expoModulesCorePlugin.exists()) {
10
+ apply from: expoModulesCorePlugin
11
+ applyKotlinExpoModulesCorePlugin()
12
+ // Remove this check, but keep the contents after SDK49 support is dropped
13
+ if (safeExtGet("expoProvidesDefaultConfig", false)) {
14
+ useExpoPublishing()
15
+ useCoreDependencies()
13
16
  }
17
+ }
14
18
 
19
+ buildscript {
15
20
  // Simple helper that allows the root project to override versions declared by this library.
16
21
  ext.safeExtGet = { prop, fallback ->
17
22
  rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
@@ -35,23 +40,44 @@ buildscript {
35
40
  }
36
41
  }
37
42
 
38
- afterEvaluate {
39
- publishing {
40
- publications {
41
- release(MavenPublication) {
42
- from components.release
43
+ // Remove this if and it's contents, when support for SDK49 is dropped
44
+ if (!safeExtGet("expoProvidesDefaultConfig", false)) {
45
+ afterEvaluate {
46
+ publishing {
47
+ publications {
48
+ release(MavenPublication) {
49
+ from components.release
50
+ }
43
51
  }
44
- }
45
- repositories {
46
- maven {
47
- url = mavenLocal().url
52
+ repositories {
53
+ maven {
54
+ url = mavenLocal().url
55
+ }
48
56
  }
49
57
  }
50
58
  }
51
59
  }
52
60
 
53
61
  android {
54
- compileSdkVersion safeExtGet("compileSdkVersion", 33)
62
+ // Remove this if and it's contents, when support for SDK49 is dropped
63
+ if (!safeExtGet("expoProvidesDefaultConfig", false)) {
64
+ compileSdkVersion safeExtGet("compileSdkVersion", 33)
65
+
66
+ defaultConfig {
67
+ minSdkVersion safeExtGet("minSdkVersion", 23)
68
+ targetSdkVersion safeExtGet("targetSdkVersion", 33)
69
+ }
70
+
71
+ publishing {
72
+ singleVariant("release") {
73
+ withSourcesJar()
74
+ }
75
+ }
76
+
77
+ lintOptions {
78
+ abortOnError false
79
+ }
80
+ }
55
81
 
56
82
  def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
57
83
  if (agpVersion.tokenize('.')[0].toInteger() < 8) {
@@ -67,34 +93,27 @@ android {
67
93
 
68
94
  namespace "expo.modules.notifications"
69
95
  defaultConfig {
70
- minSdkVersion safeExtGet("minSdkVersion", 21)
71
- targetSdkVersion safeExtGet("targetSdkVersion", 33)
72
96
  versionCode 21
73
- versionName '0.24.1'
97
+ versionName '0.25.0'
74
98
  }
75
99
 
76
- lintOptions {
77
- abortOnError false
78
- }
79
- publishing {
80
- singleVariant("release") {
81
- withSourcesJar()
82
- }
83
- }
84
100
  buildFeatures {
85
101
  buildConfig true
86
102
  }
87
103
  }
88
104
 
89
105
  dependencies {
90
- implementation project(':expo-modules-core')
106
+ // Remove this if and it's contents, when support for SDK49 is dropped
107
+ if (!safeExtGet("expoProvidesDefaultConfig", false)) {
108
+ implementation project(':expo-modules-core')
109
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
110
+ }
91
111
 
92
112
  api 'androidx.core:core:1.5.0'
93
113
  api 'androidx.lifecycle:lifecycle-runtime:2.2.0'
94
114
  api 'androidx.lifecycle:lifecycle-process:2.2.0'
95
115
  api 'androidx.lifecycle:lifecycle-common-java8:2.2.0'
96
116
 
97
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
98
117
 
99
118
  api 'com.google.firebase:firebase-messaging:22.0.0'
100
119
 
@@ -0,0 +1,22 @@
1
+ package expo.modules.notifications
2
+
3
+ import expo.modules.kotlin.exception.CodedException
4
+ import kotlin.reflect.KClass
5
+
6
+ class ModuleNotFoundException(moduleClass: KClass<*>) :
7
+ CodedException(message = "$moduleClass module not found")
8
+
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
+ }
@@ -2,50 +2,20 @@ package expo.modules.notifications;
2
2
 
3
3
  import android.content.Context;
4
4
 
5
+ import java.util.Arrays;
6
+ import java.util.List;
7
+
5
8
  import expo.modules.core.BasePackage;
6
9
  import expo.modules.core.ExportedModule;
7
10
  import expo.modules.core.interfaces.InternalModule;
8
11
  import expo.modules.core.interfaces.SingletonModule;
9
-
10
- import java.util.Arrays;
11
- import java.util.List;
12
-
13
- import expo.modules.notifications.badge.BadgeModule;
14
- import expo.modules.notifications.serverregistration.ServerRegistrationModule;
15
12
  import expo.modules.notifications.notifications.NotificationManager;
16
- import expo.modules.notifications.notifications.background.ExpoBackgroundNotificationTasksModule;
17
13
  import expo.modules.notifications.notifications.categories.ExpoNotificationCategoriesModule;
18
14
  import expo.modules.notifications.notifications.categories.serializers.ExpoNotificationsCategoriesSerializer;
19
15
  import expo.modules.notifications.notifications.channels.AndroidXNotificationsChannelsProvider;
20
- import expo.modules.notifications.notifications.channels.NotificationChannelGroupManagerModule;
21
- import expo.modules.notifications.notifications.channels.NotificationChannelManagerModule;
22
- import expo.modules.notifications.notifications.emitting.NotificationsEmitter;
23
- import expo.modules.notifications.notifications.handling.NotificationsHandler;
24
- import expo.modules.notifications.notifications.presentation.ExpoNotificationPresentationModule;
25
- import expo.modules.notifications.notifications.scheduling.NotificationScheduler;
26
- import expo.modules.notifications.permissions.NotificationPermissionsModule;
27
16
  import expo.modules.notifications.tokens.PushTokenManager;
28
- import expo.modules.notifications.tokens.PushTokenModule;
29
17
 
30
18
  public class NotificationsPackage extends BasePackage {
31
- @Override
32
- public List<ExportedModule> createExportedModules(Context context) {
33
- return Arrays.asList(
34
- new BadgeModule(context),
35
- new PushTokenModule(context),
36
- new NotificationsEmitter(context),
37
- new NotificationsHandler(context),
38
- new NotificationScheduler(context),
39
- new ServerRegistrationModule(context),
40
- new NotificationPermissionsModule(context),
41
- new NotificationChannelManagerModule(context),
42
- new ExpoNotificationPresentationModule(context),
43
- new NotificationChannelGroupManagerModule(context),
44
- new ExpoNotificationCategoriesModule(context),
45
- new ExpoBackgroundNotificationTasksModule(context)
46
- );
47
- }
48
-
49
19
  @Override
50
20
  public List<SingletonModule> createSingletonModules(Context context) {
51
21
  return Arrays.asList(
@@ -0,0 +1,19 @@
1
+ package expo.modules.notifications
2
+
3
+ import android.os.Bundle
4
+ import android.os.Handler
5
+ import android.os.ResultReceiver
6
+
7
+ typealias ResultReceiverBody = (resultCode: Int, resultData: Bundle?) -> Unit
8
+
9
+ internal fun createDefaultResultReceiver(
10
+ handler: Handler?,
11
+ body: ResultReceiverBody,
12
+ ): ResultReceiver {
13
+ return object : ResultReceiver(handler) {
14
+ override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {
15
+ super.onReceiveResult(resultCode, resultData)
16
+ body(resultCode, resultData)
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,24 @@
1
+ package expo.modules.notifications.badge
2
+
3
+ import android.content.Context
4
+ import android.util.Log
5
+ import me.leolin.shortcutbadger.ShortcutBadgeException
6
+ import me.leolin.shortcutbadger.ShortcutBadger
7
+
8
+ object BadgeHelper {
9
+ var badgeCount = 0
10
+ get() = synchronized(this) { field }
11
+ private set(value) = synchronized(this) { field = value }
12
+
13
+ fun setBadgeCount(context: Context, badgeCount: Int): Boolean {
14
+ return try {
15
+ ShortcutBadger.applyCountOrThrow(context.applicationContext, badgeCount)
16
+ BadgeHelper.badgeCount = badgeCount
17
+ true
18
+ } catch (e: ShortcutBadgeException) {
19
+ Log.d("expo-notifications", "Could not have set badge count: ${e.message}", e)
20
+ e.printStackTrace()
21
+ false
22
+ }
23
+ }
24
+ }
@@ -1,37 +1,22 @@
1
1
  package expo.modules.notifications.badge
2
2
 
3
- import android.content.Context
4
- import android.util.Log
5
- import expo.modules.core.ExportedModule
6
- import expo.modules.core.Promise
7
- import expo.modules.core.interfaces.ExpoMethod
8
- import me.leolin.shortcutbadger.ShortcutBadgeException
9
- import me.leolin.shortcutbadger.ShortcutBadger
3
+ import expo.modules.kotlin.exception.Exceptions
4
+ import expo.modules.kotlin.modules.Module
5
+ import expo.modules.kotlin.modules.ModuleDefinition
10
6
 
11
- class BadgeModule(context: Context) : ExportedModule(context) {
12
- override fun getName(): String = "ExpoBadgeModule"
7
+ class BadgeModule : Module() {
8
+ override fun definition() = ModuleDefinition {
9
+ Name("ExpoBadgeModule")
13
10
 
14
- @ExpoMethod
15
- fun getBadgeCountAsync(promise: Promise) {
16
- promise.resolve(badgeCount)
17
- }
18
-
19
- @ExpoMethod
20
- fun setBadgeCountAsync(badgeCount: Int, promise: Promise) {
21
- try {
22
- ShortcutBadger.applyCountOrThrow(context.applicationContext, badgeCount)
23
- BadgeModule.badgeCount = badgeCount
24
- promise.resolve(true)
25
- } catch (e: ShortcutBadgeException) {
26
- Log.d("expo-notifications", "Could not have set badge count: ${e.message}", e)
27
- e.printStackTrace()
28
- promise.resolve(false)
11
+ AsyncFunction("getBadgeCountAsync") {
12
+ BadgeHelper.badgeCount
29
13
  }
30
- }
31
14
 
32
- companion object {
33
- var badgeCount = 0
34
- get() = synchronized(this) { field }
35
- set(value) = synchronized(this) { field = value }
15
+ AsyncFunction("setBadgeCountAsync") { badgeCount: Int ->
16
+ BadgeHelper.setBadgeCount(
17
+ appContext.reactContext ?: throw Exceptions.ReactContextLost(),
18
+ badgeCount
19
+ )
20
+ }
36
21
  }
37
22
  }
@@ -7,9 +7,6 @@ import android.os.Bundle;
7
7
  import android.os.PersistableBundle;
8
8
  import android.util.Log;
9
9
 
10
- import androidx.lifecycle.Lifecycle;
11
- import androidx.lifecycle.ProcessLifecycleOwner;
12
-
13
10
  import org.json.JSONException;
14
11
  import org.json.JSONObject;
15
12
 
@@ -26,7 +23,7 @@ import expo.modules.interfaces.taskManager.TaskInterface;
26
23
  import expo.modules.interfaces.taskManager.TaskManagerUtilsInterface;
27
24
 
28
25
  /**
29
- * Represents a task to be run when the app is backgrounded and receives a remote push
26
+ * Represents a task to be run when the app is receives a remote push
30
27
  * notification. Map of current tasks is maintained in {@link FirebaseMessagingDelegate}.
31
28
  */
32
29
  public class BackgroundRemoteNotificationTaskConsumer extends TaskConsumer implements TaskConsumerInterface {
@@ -59,9 +56,8 @@ public class BackgroundRemoteNotificationTaskConsumer extends TaskConsumer imple
59
56
 
60
57
  public void scheduleJob(Bundle bundle) {
61
58
  Context context = getContext();
62
- boolean isInForeground = ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED);
63
59
 
64
- if (context != null && mTask != null && !isInForeground) {
60
+ if (context != null && mTask != null) {
65
61
  PersistableBundle data = new PersistableBundle();
66
62
  // Bundles are not persistable, so let's convert to a JSON string
67
63
  data.putString(NOTIFICATION_KEY, bundleToJson(bundle).toString());
@@ -0,0 +1,33 @@
1
+ package expo.modules.notifications.notifications.background
2
+
3
+ import expo.modules.interfaces.taskManager.TaskManagerInterface
4
+ import expo.modules.kotlin.modules.Module
5
+ import expo.modules.kotlin.modules.ModuleDefinition
6
+ import expo.modules.notifications.ModuleNotFoundException
7
+
8
+ class ExpoBackgroundNotificationTasksModule : Module() {
9
+ private lateinit var taskManager: TaskManagerInterface
10
+
11
+ override fun definition() = ModuleDefinition {
12
+ Name("ExpoBackgroundNotificationTasksModule")
13
+ OnCreate {
14
+ taskManager = appContext.legacyModule()
15
+ ?: throw ModuleNotFoundException(TaskManagerInterface::class)
16
+ }
17
+
18
+ AsyncFunction("registerTaskAsync") { taskName: String ->
19
+ taskManager.registerTask(
20
+ taskName,
21
+ BackgroundRemoteNotificationTaskConsumer::class.java,
22
+ emptyMap()
23
+ )
24
+ }
25
+
26
+ AsyncFunction("unregisterTaskAsync") { taskName: String ->
27
+ taskManager.unregisterTask(
28
+ taskName,
29
+ BackgroundRemoteNotificationTaskConsumer::class.java
30
+ )
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,149 @@
1
+ package expo.modules.notifications.notifications.categories
2
+
3
+ import android.content.Context
4
+ import android.os.Bundle
5
+ import expo.modules.core.errors.InvalidArgumentException
6
+ import expo.modules.kotlin.Promise
7
+ import expo.modules.kotlin.exception.Exceptions
8
+ import expo.modules.kotlin.modules.Module
9
+ import expo.modules.kotlin.modules.ModuleDefinition
10
+ import expo.modules.kotlin.records.Field
11
+ import expo.modules.kotlin.records.Record
12
+ import expo.modules.kotlin.records.Required
13
+ import expo.modules.notifications.ModuleNotFoundException
14
+ import expo.modules.notifications.ResultReceiverBody
15
+ import expo.modules.notifications.createDefaultResultReceiver
16
+ import expo.modules.notifications.notifications.categories.serializers.NotificationsCategoriesSerializer
17
+ import expo.modules.notifications.notifications.model.NotificationAction
18
+ import expo.modules.notifications.notifications.model.NotificationCategory
19
+ import expo.modules.notifications.notifications.model.TextInputNotificationAction
20
+ import expo.modules.notifications.service.NotificationsService
21
+ import expo.modules.notifications.service.NotificationsService.Companion.deleteCategory
22
+ import expo.modules.notifications.service.NotificationsService.Companion.getCategories
23
+ import expo.modules.notifications.service.NotificationsService.Companion.setCategory
24
+
25
+ class NotificationActionRecord : Record {
26
+ @Field
27
+ @Required
28
+ val identifier: String = ""
29
+
30
+ @Field
31
+ @Required
32
+ val buttonTitle: String = ""
33
+
34
+ @Field
35
+ val textInput: TextInput? = null
36
+
37
+ @Field
38
+ val options = Options()
39
+
40
+ class TextInput : Record {
41
+ @Field
42
+ @Required
43
+ val placeholder: String = ""
44
+ }
45
+
46
+ class Options : Record {
47
+ @Field
48
+ val opensAppToForeground = true
49
+ }
50
+ }
51
+
52
+ open class ExpoNotificationCategoriesModule : Module() {
53
+
54
+ protected val serializer by lazy {
55
+ appContext.legacyModule<NotificationsCategoriesSerializer>()
56
+ ?: throw ModuleNotFoundException(NotificationsCategoriesSerializer::class)
57
+ }
58
+
59
+ private val context: Context
60
+ get() = appContext.reactContext ?: throw Exceptions.ReactContextLost()
61
+
62
+ override fun definition() = ModuleDefinition {
63
+ Name("ExpoNotificationCategoriesModule")
64
+
65
+ AsyncFunction("getNotificationCategoriesAsync") { promise: Promise ->
66
+ getCategories(
67
+ context,
68
+ createResultReceiver { resultCode: Int, resultData: Bundle? ->
69
+ val categories = resultData?.getParcelableArrayList<NotificationCategory>(NotificationsService.NOTIFICATION_CATEGORIES_KEY)
70
+ if (resultCode == NotificationsService.SUCCESS_CODE && categories != null) {
71
+ promise.resolve(serializeCategories(categories))
72
+ } else {
73
+ promise.reject("ERR_CATEGORIES_FETCH_FAILED", "A list of notification categories could not be fetched.", null)
74
+ }
75
+ }
76
+ )
77
+ }
78
+
79
+ AsyncFunction("setNotificationCategoryAsync", this@ExpoNotificationCategoriesModule::setNotificationCategoryAsync)
80
+
81
+ AsyncFunction("deleteNotificationCategoryAsync", this@ExpoNotificationCategoriesModule::deleteNotificationCategoryAsync)
82
+ }
83
+
84
+ private fun createResultReceiver(body: ResultReceiverBody) =
85
+ createDefaultResultReceiver(null, body)
86
+
87
+ open fun setNotificationCategoryAsync(
88
+ identifier: String,
89
+ actionArguments: List<NotificationActionRecord>,
90
+ categoryOptions: Map<String, Any?>?,
91
+ promise: Promise
92
+ ) {
93
+ val actions = mutableListOf<NotificationAction>()
94
+ for (actionMap in actionArguments) {
95
+ val textInputOptions = actionMap.textInput
96
+ if (textInputOptions != null) {
97
+ actions.add(
98
+ TextInputNotificationAction(
99
+ actionMap.identifier,
100
+ actionMap.buttonTitle,
101
+ actionMap.options.opensAppToForeground,
102
+ textInputOptions.placeholder
103
+ )
104
+ )
105
+ } else {
106
+ actions.add(
107
+ NotificationAction(
108
+ actionMap.identifier,
109
+ actionMap.buttonTitle,
110
+ actionMap.options.opensAppToForeground,
111
+ )
112
+ )
113
+ }
114
+ }
115
+ if (actions.isEmpty()) {
116
+ throw InvalidArgumentException("Invalid arguments provided for notification category. Must provide at least one action.")
117
+ }
118
+ setCategory(
119
+ context,
120
+ NotificationCategory(identifier, actions),
121
+ createResultReceiver { resultCode: Int, resultData: Bundle? ->
122
+ val category = resultData?.getParcelable<NotificationCategory>(NotificationsService.NOTIFICATION_CATEGORY_KEY)
123
+ if (resultCode == NotificationsService.SUCCESS_CODE && category != null) {
124
+ promise.resolve(serializer.toBundle(category))
125
+ } else {
126
+ promise.reject("ERR_CATEGORY_SET_FAILED", "The provided category could not be set.", null)
127
+ }
128
+ }
129
+ )
130
+ }
131
+
132
+ open fun deleteNotificationCategoryAsync(identifier: String, promise: Promise) {
133
+ deleteCategory(
134
+ context,
135
+ identifier,
136
+ createResultReceiver { resultCode: Int, resultData: Bundle? ->
137
+ if (resultCode == NotificationsService.SUCCESS_CODE) {
138
+ promise.resolve(resultData?.getBoolean(NotificationsService.SUCCEEDED_KEY))
139
+ } else {
140
+ promise.reject("ERR_CATEGORY_DELETE_FAILED", "The category could not be deleted.", null)
141
+ }
142
+ }
143
+ )
144
+ }
145
+
146
+ protected open fun serializeCategories(categories: Collection<NotificationCategory>): List<Bundle?> {
147
+ return categories.map(serializer::toBundle)
148
+ }
149
+ }
@@ -0,0 +1,70 @@
1
+ package expo.modules.notifications.notifications.channels
2
+
3
+ import android.os.Build
4
+ import expo.modules.core.arguments.ReadableArguments
5
+ import expo.modules.kotlin.modules.Module
6
+ import expo.modules.kotlin.modules.ModuleDefinition
7
+ import expo.modules.notifications.ModuleNotFoundException
8
+ import expo.modules.notifications.notifications.channels.managers.NotificationsChannelGroupManager
9
+ import expo.modules.notifications.notifications.channels.serializers.NotificationsChannelGroupSerializer
10
+
11
+ /**
12
+ * An exported module responsible for exposing methods for managing notification channel groups.
13
+ */
14
+ class NotificationChannelGroupManagerModule : Module() {
15
+ private lateinit var groupManager: NotificationsChannelGroupManager
16
+ private lateinit var groupSerializer: NotificationsChannelGroupSerializer
17
+
18
+ override fun definition() = ModuleDefinition {
19
+ Name("ExpoNotificationChannelGroupManager")
20
+
21
+ OnCreate {
22
+ val provider = appContext.legacyModule<NotificationsChannelsProvider>()
23
+ ?: throw ModuleNotFoundException(NotificationsChannelsProvider::class)
24
+ groupManager = provider.groupManager
25
+ groupSerializer = provider.groupSerializer
26
+ }
27
+
28
+ AsyncFunction("getNotificationChannelGroupAsync") { groupId: String ->
29
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
30
+ val group = groupManager.getNotificationChannelGroup(groupId)
31
+ groupSerializer.toBundle(group)
32
+ } else {
33
+ null
34
+ }
35
+ }
36
+
37
+ AsyncFunction("getNotificationChannelGroupsAsync") {
38
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
39
+ groupManager
40
+ .notificationChannelGroups
41
+ .map(groupSerializer::toBundle)
42
+ } else {
43
+ null
44
+ }
45
+ }
46
+
47
+ AsyncFunction("setNotificationChannelGroupAsync") { groupId: String, groupOptions: ReadableArguments ->
48
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
49
+ val group = groupManager.createNotificationChannelGroup(
50
+ groupId,
51
+ getNameFromOptions(groupOptions),
52
+ groupOptions
53
+ )
54
+ groupSerializer.toBundle(group)
55
+ } else {
56
+ null
57
+ }
58
+ }
59
+
60
+ AsyncFunction("deleteNotificationChannelGroupAsync") { groupId: String ->
61
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
62
+ groupManager.deleteNotificationChannelGroup(groupId)
63
+ }
64
+ }
65
+ }
66
+
67
+ private fun getNameFromOptions(groupOptions: ReadableArguments): String {
68
+ return groupOptions.getString(NotificationsChannelGroupSerializer.NAME_KEY)
69
+ }
70
+ }