Skip to content

Commit

Permalink
[expo-notifications] Migrate FirebaseMessagingService to Kotlin (expo…
Browse files Browse the repository at this point in the history
…#10556)

# Why

Prerequisite for single threaded notifications handling.

# How

Used Android Studio migration tool, then looked at the code trying to think of more kotlinish structures. Fixed nullabilities of arguments and return values in the `expoview` subclass.

# Test Plan

Code compiles, push notifications are received.
  • Loading branch information
sjchmiela authored Oct 6, 2020
1 parent b6e6c1e commit eac5fed
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 116 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import java.util.Map;

import androidx.annotation.NonNull;
import expo.modules.notifications.FirebaseListenerService;
import expo.modules.notifications.notifications.model.NotificationContent;
import expo.modules.notifications.notifications.model.NotificationRequest;
Expand All @@ -24,7 +25,7 @@
public class ExpoFcmMessagingService extends FirebaseListenerService {

@Override
public void onNewToken(String token) {
public void onNewToken(@NonNull String token) {
if (!Constants.FCM_ENABLED) {
return;
}
Expand All @@ -34,7 +35,7 @@ public void onNewToken(String token) {
}

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
if (!Constants.FCM_ENABLED) {
return;
}
Expand Down Expand Up @@ -81,8 +82,9 @@ private void dispatchToLegacyNotificationModule(RemoteMessage remoteMessage) {
PushNotificationHelper.getInstance().onMessageReceived(this, remoteMessage.getData().get("experienceId"), remoteMessage.getData().get("channelId"), remoteMessage.getData().get("message"), remoteMessage.getData().get("body"), remoteMessage.getData().get("title"), remoteMessage.getData().get("categoryId"));
}

@NonNull
@Override
protected NotificationRequest createNotificationRequest(String identifier, NotificationContent content, FirebaseNotificationTrigger notificationTrigger) {
protected NotificationRequest createNotificationRequest(@NonNull String identifier, @NonNull NotificationContent content, FirebaseNotificationTrigger notificationTrigger) {
ExperienceId experienceId;
Map<String, String> data = notificationTrigger.getRemoteMessage().getData();
if (!data.containsKey("experienceId")) {
Expand Down
4 changes: 4 additions & 0 deletions packages/expo-notifications/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}

kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
}

defaultConfig {
minSdkVersion safeExtGet("minSdkVersion", 21)
targetSdkVersion safeExtGet("targetSdkVersion", 29)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package expo.modules.notifications

import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import expo.modules.notifications.notifications.JSONNotificationContentBuilder
import expo.modules.notifications.notifications.interfaces.NotificationsScoper
import expo.modules.notifications.notifications.model.Notification
import expo.modules.notifications.notifications.model.NotificationContent
import expo.modules.notifications.notifications.model.NotificationRequest
import expo.modules.notifications.notifications.model.triggers.FirebaseNotificationTrigger
import expo.modules.notifications.notifications.service.NotificationsHelper
import expo.modules.notifications.tokens.interfaces.FirebaseTokenListener
import org.json.JSONObject
import java.lang.ref.WeakReference
import java.util.*

/**
* Subclass of FirebaseMessagingService responsible for dispatching new tokens and remote messages.
*/
open class FirebaseListenerService : FirebaseMessagingService() {
companion object {
// Unfortunately we cannot save state between instances of a service other way
// than by static properties. Fortunately, using weak references we can
// be somehow sure instances of PushTokenListeners won't be leaked by this component.
/**
* We store this value to be able to inform new listeners of last known token.
*/
private var sLastToken: String? = null

/**
* A weak map of listeners -> reference. Used to check quickly whether given listener
* is already registered and to iterate over when notifying of new token.
*/
private val sTokenListenersReferences = WeakHashMap<FirebaseTokenListener, WeakReference<FirebaseTokenListener?>?>()

/**
* Used only by [FirebaseTokenListener] instances. If you look for a place to register
* your listener, use [FirebaseTokenListener] singleton module.
*
* Purposefully the argument is expected to be a [FirebaseTokenListener] and just a listener.
*
* This class doesn't hold strong references to listeners, so you need to own your listeners.
*
* @param listener A listener instance to be informed of new push device tokens.
*/
@JvmStatic
fun addTokenListener(listener: FirebaseTokenListener) {
// Checks whether this listener has already been registered
if (!sTokenListenersReferences.containsKey(listener)) {
sTokenListenersReferences[listener] = WeakReference(listener)
// Since it's a new listener and we know of a last valid token, let's let them know.
if (sLastToken != null) {
listener.onNewToken(sLastToken)
}
}
}
}

/**
* Called on new token, dispatches it to [FirebaseListenerService.sTokenListenersReferences].
*
* @param token New device push token.
*/
override fun onNewToken(token: String) {
super.onNewToken(token)
for (listenerReference in sTokenListenersReferences.values) {
listenerReference?.get()?.onNewToken(token)
}
sLastToken = token
}

override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
createNotificationsHelper().notificationReceived(createNotification(remoteMessage))
}

protected fun createNotificationsHelper(): NotificationsHelper {
return NotificationsHelper(this, NotificationsScoper.create(this).createReconstructor())
}

protected fun createNotification(remoteMessage: RemoteMessage): Notification {
val identifier = remoteMessage.messageId ?: UUID.randomUUID().toString()
val payload = JSONObject(remoteMessage.data as Map<*, *>)
val content = JSONNotificationContentBuilder(this).setPayload(payload).build()
val request = createNotificationRequest(identifier, content, FirebaseNotificationTrigger(remoteMessage))
return Notification(request, Date(remoteMessage.sentTime))
}

protected open fun createNotificationRequest(
identifier: String,
content: NotificationContent,
notificationTrigger: FirebaseNotificationTrigger
): NotificationRequest {
return NotificationRequest(identifier, content, notificationTrigger)
}

override fun onDeletedMessages() {
super.onDeletedMessages()
createNotificationsHelper().dropped(null)
}
}

0 comments on commit eac5fed

Please sign in to comment.