1 package host.exp.exponent.fcm
2 
3 import android.content.Context
4 import com.google.firebase.messaging.RemoteMessage
5 import expo.modules.notifications.notifications.model.NotificationContent
6 import expo.modules.notifications.notifications.model.NotificationRequest
7 import expo.modules.notifications.notifications.model.triggers.FirebaseNotificationTrigger
8 import expo.modules.notifications.service.delegates.FirebaseMessagingDelegate
9 import host.exp.exponent.ABIVersion
10 import host.exp.exponent.Constants
11 import host.exp.exponent.analytics.EXL
12 import host.exp.exponent.notifications.NotificationConstants
13 import host.exp.exponent.notifications.PushNotificationHelper
14 import host.exp.exponent.notifications.model.ScopedNotificationRequest
15 import host.exp.exponent.storage.ExponentDB
16 import org.json.JSONException
17 
18 class ExpoFirebaseMessagingDelegate(context: Context) : FirebaseMessagingDelegate(context) {
19   override fun onNewToken(token: String) {
20     if (!Constants.FCM_ENABLED) {
21       return
22     }
23     super.onNewToken(token)
24     FcmRegistrationIntentService.registerForeground(context.applicationContext, token)
25   }
26 
27   override fun onMessageReceived(remoteMessage: RemoteMessage) {
28     if (!Constants.FCM_ENABLED) {
29       return
30     }
31 
32     val scopeKey = remoteMessage.data[NotificationConstants.NOTIFICATION_EXPERIENCE_SCOPE_KEY_KEY]
33     if (scopeKey == null) {
34       EXL.e("expo-notifications", "No scope key found in notification")
35       return
36     }
37 
38     val exponentDBObject = try {
39       ExponentDB.experienceScopeKeyToExperienceSync(scopeKey)
40     } catch (e: JSONException) {
41       e.printStackTrace()
42       EXL.e("expo-notifications", "Error getting experience for scope key $scopeKey")
43       return
44     }
45 
46     if (exponentDBObject == null) {
47       EXL.e("expo-notifications", "No experience found for scope key $scopeKey")
48       return
49     }
50 
51     val sdkVersionString = exponentDBObject.manifest.getSDKVersion()
52     if (sdkVersionString == null) {
53       dispatchToNextNotificationModule(remoteMessage)
54       return
55     }
56 
57     val sdkVersion = ABIVersion.toNumber(sdkVersionString) / 10000
58 
59     // Remove the entire legacy notifications API after we drop SDK 40
60     if (sdkVersion <= 40 && !exponentDBObject.manifest.shouldUseNextNotificationsApi()) {
61       dispatchToLegacyNotificationModule(remoteMessage)
62     } else {
63       dispatchToNextNotificationModule(remoteMessage)
64     }
65   }
66 
67   private fun dispatchToNextNotificationModule(remoteMessage: RemoteMessage) {
68     super.onMessageReceived(remoteMessage)
69   }
70 
71   private fun dispatchToLegacyNotificationModule(remoteMessage: RemoteMessage) {
72     PushNotificationHelper.instance.onMessageReceived(
73       context,
74       remoteMessage.data[NotificationConstants.NOTIFICATION_EXPERIENCE_SCOPE_KEY_KEY]!!,
75       remoteMessage.data["channelId"],
76       remoteMessage.data["message"],
77       remoteMessage.data["body"],
78       remoteMessage.data["title"],
79       remoteMessage.data["categoryId"]
80     )
81   }
82 
83   override fun createNotificationRequest(
84     identifier: String,
85     content: NotificationContent,
86     notificationTrigger: FirebaseNotificationTrigger
87   ): NotificationRequest {
88     if (Constants.isStandaloneApp()) {
89       return super.createNotificationRequest(identifier, content, notificationTrigger)
90     }
91 
92     val data = notificationTrigger.remoteMessage.data
93     return ScopedNotificationRequest(
94       identifier,
95       content,
96       notificationTrigger,
97       data[NotificationConstants.NOTIFICATION_EXPERIENCE_SCOPE_KEY_KEY]
98     )
99   }
100 }
101