diff --git a/app/src/main/java/com/kickstarter/services/firebase/MessageService.java b/app/src/main/java/com/kickstarter/services/firebase/MessageService.java deleted file mode 100644 index f6dab46bcf..0000000000 --- a/app/src/main/java/com/kickstarter/services/firebase/MessageService.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.kickstarter.services.firebase; - -import android.text.TextUtils; - -import androidx.annotation.NonNull; - -import com.google.firebase.messaging.FirebaseMessagingService; -import com.google.firebase.messaging.RemoteMessage; -import com.google.gson.Gson; -import com.kickstarter.KSApplication; -import com.kickstarter.R; -import com.kickstarter.libs.PushNotifications; -import com.kickstarter.libs.braze.RemotePushClientType; -import com.kickstarter.models.pushdata.Activity; -import com.kickstarter.models.pushdata.GCM; -import com.kickstarter.services.apiresponses.PushNotificationEnvelope; - -import java.util.Map; - -import javax.inject.Inject; - -import timber.log.Timber; - -public class MessageService extends FirebaseMessagingService { - @Inject protected Gson gson; - @Inject protected PushNotifications pushNotifications; - @Inject protected RemotePushClientType remotePushClientType; - - @Override - public void onNewToken(@NonNull final String s) { - super.onNewToken(s); - } - - @Override - public void onCreate() { - super.onCreate(); - ((KSApplication) getApplicationContext()).component().inject(this); - } - - /** - * Called when a message is received from Firebase. - * - If the message comes from braze it will be handle on: - * - @see RemotePushClientType#handleRemoteMessages(android.content.Context, com.google.firebase.messaging.RemoteMessage) - * - If the message is from Kickstarter it will be process here - * - * @param remoteMessage Object containing message information. - */ - @Override - public void onMessageReceived(final RemoteMessage remoteMessage) { - super.onMessageReceived(remoteMessage); - final Boolean isBrazeMessage = this.remotePushClientType.handleRemoteMessages(this, remoteMessage); - - if (!isBrazeMessage) { - final String senderId = getString(R.string.gcm_defaultSenderId); - final String from = remoteMessage.getFrom(); - if (!TextUtils.equals(from, senderId)) { - Timber.e("Received a message from %s, expecting %s", from, senderId); - return; - } - - final Map data = remoteMessage.getData(); - - final PushNotificationEnvelope envelope = PushNotificationEnvelope.builder() - .activity(this.gson.fromJson(data.get("activity"), Activity.class)) - .erroredPledge(this.gson.fromJson(data.get("errored_pledge"), PushNotificationEnvelope.ErroredPledge.class)) - .gcm(this.gson.fromJson(data.get("gcm"), GCM.class)) - .message(this.gson.fromJson(data.get("message"), PushNotificationEnvelope.Message.class)) - .project(this.gson.fromJson(data.get("project"), PushNotificationEnvelope.Project.class)) - .survey(this.gson.fromJson(data.get("survey"), PushNotificationEnvelope.Survey.class)) - .build(); - - if (envelope == null) { - Timber.e("Cannot parse message, malformed or unexpected data: %s", data.toString()); - return; - } - - Timber.d("Received message: %s", envelope.toString()); - this.pushNotifications.add(envelope); - } - } -} diff --git a/app/src/main/java/com/kickstarter/services/firebase/MessageService.kt b/app/src/main/java/com/kickstarter/services/firebase/MessageService.kt new file mode 100644 index 0000000000..10b97d7d52 --- /dev/null +++ b/app/src/main/java/com/kickstarter/services/firebase/MessageService.kt @@ -0,0 +1,122 @@ +package com.kickstarter.services.firebase + +import android.text.TextUtils +import com.google.firebase.crashlytics.FirebaseCrashlytics +import com.google.firebase.messaging.FirebaseMessagingService +import com.google.firebase.messaging.RemoteMessage +import com.google.gson.Gson +import com.kickstarter.KSApplication +import com.kickstarter.R +import com.kickstarter.libs.PushNotifications +import com.kickstarter.libs.braze.RemotePushClientType +import com.kickstarter.models.pushdata.Activity +import com.kickstarter.models.pushdata.GCM +import com.kickstarter.services.ApiClientTypeV2 +import com.kickstarter.services.ApiException +import com.kickstarter.services.apiresponses.PushNotificationEnvelope +import com.kickstarter.services.apiresponses.PushNotificationEnvelope.Companion.builder +import com.kickstarter.services.apiresponses.PushNotificationEnvelope.ErroredPledge +import com.kickstarter.services.apiresponses.PushNotificationEnvelope.Survey +import timber.log.Timber +import javax.inject.Inject + +interface RefreshPushToken { + companion object { + val OK_MESSAGE = "Push Token refreshed onNewToken: " + val KO_MESSAGE = "Failed to register Push Token: " + } + fun invoke(apiClient: ApiClientTypeV2, newToken: String, gson: Gson, successCallback: (String) -> Unit, errorCallback: (String) -> Unit) { + try { + val response = apiClient.registerPushToken(newToken).blockingSingle() + val message = "$OK_MESSAGE $response" + successCallback.invoke(message) + } catch (exception: ApiException) { + val errorMessage = "$KO_MESSAGE ${ + exception.errorEnvelope().errorMessage() + }" + if (exception.errorEnvelope().httpCode() != 401) // else OAuth token not authorized + errorCallback.invoke(errorMessage) + } + } +} + +class MessageService : FirebaseMessagingService() { + + @Inject + lateinit var pushNotifications: PushNotifications + + @Inject + lateinit var remotePushClientType: RemotePushClientType + + @Inject + lateinit var apiClient: ApiClientTypeV2 + + @Inject + lateinit var gson: Gson + + override fun onNewToken(s: String) { + super.onNewToken(s) + val refreshHandler = object : RefreshPushToken {} + refreshHandler.invoke( + apiClient = apiClient, + newToken = s, + gson = gson, + successCallback = { message -> FirebaseCrashlytics.getInstance().log(message) }, + errorCallback = { errorMessage -> FirebaseCrashlytics.getInstance().recordException(Exception(errorMessage)) } + ) + } + + override fun onCreate() { + super.onCreate() + (applicationContext as KSApplication).component().inject(this) + } + + /** + * Called when a message is received from Firebase. + * - If the message comes from braze it will be handle on: + * - @see RemotePushClientType#handleRemoteMessages(android.content.Context, com.google.firebase.messaging.RemoteMessage) + * - If the message is from Kickstarter it will be process here + * + * @param remoteMessage Object containing message information. + */ + override fun onMessageReceived(remoteMessage: RemoteMessage) { + super.onMessageReceived(remoteMessage) + val isBrazeMessage = + remotePushClientType.handleRemoteMessages(this, remoteMessage) + + if (!isBrazeMessage) { + val senderId = getString(R.string.gcm_defaultSenderId) + val from = remoteMessage.from + if (!TextUtils.equals(from, senderId)) { + Timber.e("Received a message from %s, expecting %s", from, senderId) + return + } + + val data = remoteMessage.data + + val envelope = builder() + .activity( + gson.fromJson( + data["activity"], Activity::class.java + ) + ) + .erroredPledge(gson.fromJson(data["errored_pledge"], ErroredPledge::class.java)) + .gcm(gson.fromJson(data["gcm"], GCM::class.java)) + .message( + gson.fromJson( + data["message"], PushNotificationEnvelope.Message::class.java + ) + ) + .project( + gson.fromJson( + data["project"], PushNotificationEnvelope.Project::class.java + ) + ) + .survey(gson.fromJson(data["survey"], Survey::class.java)) + .build() + + Timber.d("Received message: %s", envelope.toString()) + pushNotifications.add(envelope) + } + } +} diff --git a/app/src/test/java/com/kickstarter/services/RegisterPushTokenTest.kt b/app/src/test/java/com/kickstarter/services/RegisterPushTokenTest.kt new file mode 100644 index 0000000000..aa8294afe8 --- /dev/null +++ b/app/src/test/java/com/kickstarter/services/RegisterPushTokenTest.kt @@ -0,0 +1,76 @@ +package com.kickstarter.services + +import com.google.gson.Gson +import com.google.gson.JsonObject +import com.kickstarter.KSRobolectricTestCase +import com.kickstarter.mock.factories.ApiExceptionFactory +import com.kickstarter.mock.services.MockApiClientV2 +import com.kickstarter.services.firebase.RefreshPushToken +import com.kickstarter.services.firebase.RefreshPushToken.Companion.KO_MESSAGE +import com.kickstarter.services.firebase.RefreshPushToken.Companion.OK_MESSAGE +import io.reactivex.Observable +import org.junit.Test + +class RegisterPushTokenTest : KSRobolectricTestCase() { + + @Test + fun `test with successful response for registerPushToken api call`() { + val jObj = JsonObject() + + val apiClient = object : MockApiClientV2() { + override fun registerPushToken(token: String): Observable { + jObj.addProperty("token", token) + return Observable.just(jObj) + } + } + + val refreshPushToken = object : RefreshPushToken {} + + var successMessage = "" + var error = "" + refreshPushToken.invoke( + apiClient = apiClient, + newToken = "a new Token!!", + gson = Gson(), + successCallback = { message -> + successMessage = message + }, + errorCallback = { errorMessage -> + error = errorMessage + } + ) + + assertTrue(error.isEmpty()) + assert(successMessage == "$OK_MESSAGE $jObj") + } + + @Test + fun `test with errored response for registerPushToken api call`() { + + val apiClient = object : MockApiClientV2() { + override fun registerPushToken(token: String): Observable { + return Observable.error(ApiExceptionFactory.badRequestException()) + } + } + + val refreshPushToken = object : RefreshPushToken {} + + var successMessage = "" + var error = "" + refreshPushToken.invoke( + apiClient = apiClient, + newToken = "a new Token!!", + gson = Gson(), + successCallback = { message -> + successMessage = message + }, + errorCallback = { errorMessage -> + error = errorMessage + } + ) + + assertTrue(successMessage.isEmpty()) + val messageToAssert = "$KO_MESSAGE ${ApiExceptionFactory.badRequestException().errorEnvelope().errorMessage()}" + assertEquals(error, messageToAssert) + } +}