Update dependency com.google.firebase:firebase-bom to v34.15.0 (#7089)

* Update dependency com.google.firebase:firebase-bom to v34.15.0

* Replace deprecated FCM `token` usages with `installationId`

Renamed a few components, changed the behaviour of `FirebaseTokenRotator` so it doesn't save the new token, `FirebaseMessagingService` will do it in its `onRegistered` method

* Enable firebase messaging installation_id

* Make sure we delete the FID when unregistering the app from Firebase

* Add `runFirebaseTask` util to convert `Task<T>` into a `Result<T>` returned from a suspending function

Also, rename the use cases and make them use the `invoke` pattern

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
Co-authored-by: Benoit Marty <benoit@matrix.org>
This commit is contained in:
renovate[bot]
2026-06-25 09:48:48 +02:00
committed by GitHub
parent 81b0b357e5
commit a3736786cd
22 changed files with 227 additions and 208 deletions
+1 -1
View File
@@ -84,7 +84,7 @@ kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlin
kover_gradle_plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" } kover_gradle_plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" }
ksp_gradle_plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } ksp_gradle_plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
# https://firebase.google.com/docs/android/setup#available-libraries # https://firebase.google.com/docs/android/setup#available-libraries
google_firebase_bom = "com.google.firebase:firebase-bom:34.14.1" google_firebase_bom = "com.google.firebase:firebase-bom:34.15.0"
firebase_appdistribution_gradle = { module = "com.google.firebase:firebase-appdistribution-gradle", version.ref = "firebaseAppDistribution" } firebase_appdistribution_gradle = { module = "com.google.firebase:firebase-appdistribution-gradle", version.ref = "firebaseAppDistribution" }
autonomousapps_dependencyanalysis_plugin = { module = "com.autonomousapps:dependency-analysis-gradle-plugin", version.ref = "dependencyAnalysis" } autonomousapps_dependencyanalysis_plugin = { module = "com.autonomousapps:dependency-analysis-gradle-plugin", version.ref = "dependencyAnalysis" }
ksp_plugin = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } ksp_plugin = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
@@ -7,6 +7,10 @@
--> -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application> <application>
<meta-data
android:name="firebase_messaging_installation_id_enabled"
android:value="true" />
<!-- Firebase components --> <!-- Firebase components -->
<meta-data <meta-data
android:name="firebase_analytics_collection_deactivated" android:name="firebase_analytics_collection_deactivated"
@@ -20,26 +20,26 @@ import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.libraries.sessionstorage.api.toUserList import io.element.android.libraries.sessionstorage.api.toUserList
import timber.log.Timber import timber.log.Timber
private val loggerTag = LoggerTag("FirebaseNewTokenHandler", LoggerTag.PushLoggerTag) private val loggerTag = LoggerTag("FirebaseNewInstallationIdHandler", LoggerTag.PushLoggerTag)
/** /**
* Handle new token receive from Firebase. Will update all the sessions which are using Firebase as a push provider. * Handle new installationId received from Firebase. Will update all the sessions which are using Firebase as a push provider.
*/ */
interface FirebaseNewTokenHandler { interface FirebaseNewInstallationIdHandler {
suspend fun handle(firebaseToken: String) suspend fun handle(installationId: String)
} }
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class DefaultFirebaseNewTokenHandler( class DefaultFirebaseNewInstallationIdHandler(
private val pusherSubscriber: PusherSubscriber, private val pusherSubscriber: PusherSubscriber,
private val sessionStore: SessionStore, private val sessionStore: SessionStore,
private val userPushStoreFactory: UserPushStoreFactory, private val userPushStoreFactory: UserPushStoreFactory,
private val matrixClientProvider: MatrixClientProvider, private val matrixClientProvider: MatrixClientProvider,
private val firebaseStore: FirebaseStore, private val firebaseStore: FirebaseStore,
private val firebaseGatewayProvider: FirebaseGatewayProvider, private val firebaseGatewayProvider: FirebaseGatewayProvider,
) : FirebaseNewTokenHandler { ) : FirebaseNewInstallationIdHandler {
override suspend fun handle(firebaseToken: String) { override suspend fun handle(installationId: String) {
firebaseStore.storeFcmToken(firebaseToken) firebaseStore.storeInstallationId(installationId)
// Register the pusher for all the sessions // Register the pusher for all the sessions
sessionStore.getAllSessions().toUserList() sessionStore.getAllSessions().toUserList()
.map { SessionId(it) } .map { SessionId(it) }
@@ -55,7 +55,7 @@ class DefaultFirebaseNewTokenHandler(
pusherSubscriber pusherSubscriber
.registerPusher( .registerPusher(
matrixClient = client, matrixClient = client,
pushKey = firebaseToken, pushKey = installationId,
gateway = firebaseGatewayProvider.getFirebaseGateway(), gateway = firebaseGatewayProvider.getFirebaseGateway(),
) )
.onFailure { .onFailure {
@@ -26,7 +26,7 @@ class FirebasePushProvider(
private val firebaseStore: FirebaseStore, private val firebaseStore: FirebaseStore,
private val pusherSubscriber: PusherSubscriber, private val pusherSubscriber: PusherSubscriber,
private val isPlayServiceAvailable: IsPlayServiceAvailable, private val isPlayServiceAvailable: IsPlayServiceAvailable,
private val firebaseTokenRotator: FirebaseTokenRotator, private val rotateFirebaseSession: RotateFirebaseSession,
private val firebaseGatewayProvider: FirebaseGatewayProvider, private val firebaseGatewayProvider: FirebaseGatewayProvider,
) : PushProvider { ) : PushProvider {
override val index = FirebaseConfig.INDEX override val index = FirebaseConfig.INDEX
@@ -40,7 +40,7 @@ class FirebasePushProvider(
} }
override suspend fun registerWith(matrixClient: MatrixClient, distributor: Distributor): Result<Unit> { override suspend fun registerWith(matrixClient: MatrixClient, distributor: Distributor): Result<Unit> {
val pushKey = firebaseStore.getFcmToken() ?: return Result.failure<Unit>( val pushKey = firebaseStore.getInstallationId() ?: return Result.failure<Unit>(
IllegalStateException( IllegalStateException(
"Unable to register pusher, Firebase token is not known." "Unable to register pusher, Firebase token is not known."
) )
@@ -59,7 +59,7 @@ class FirebasePushProvider(
override suspend fun getCurrentDistributor(sessionId: SessionId) = firebaseDistributor override suspend fun getCurrentDistributor(sessionId: SessionId) = firebaseDistributor
override suspend fun unregister(matrixClient: MatrixClient): Result<Unit> { override suspend fun unregister(matrixClient: MatrixClient): Result<Unit> {
val pushKey = firebaseStore.getFcmToken() val pushKey = firebaseStore.getInstallationId()
return if (pushKey == null) { return if (pushKey == null) {
Timber.tag(loggerTag.value).w("Unable to unregister pusher, Firebase token is not known.") Timber.tag(loggerTag.value).w("Unable to unregister pusher, Firebase token is not known.")
Result.success(Unit) Result.success(Unit)
@@ -74,7 +74,7 @@ class FirebasePushProvider(
override suspend fun onSessionDeleted(sessionId: SessionId) = Unit override suspend fun onSessionDeleted(sessionId: SessionId) = Unit
override suspend fun getPushConfig(sessionId: SessionId): Config? { override suspend fun getPushConfig(sessionId: SessionId): Config? {
return firebaseStore.getFcmToken()?.let { fcmToken -> return firebaseStore.getInstallationId()?.let { fcmToken ->
Config( Config(
url = firebaseGatewayProvider.getFirebaseGateway(), url = firebaseGatewayProvider.getFirebaseGateway(),
pushKey = fcmToken pushKey = fcmToken
@@ -85,7 +85,7 @@ class FirebasePushProvider(
override fun canRotateToken(): Boolean = true override fun canRotateToken(): Boolean = true
override suspend fun rotateToken(): Result<Unit> { override suspend fun rotateToken(): Result<Unit> {
return firebaseTokenRotator.rotate() return rotateFirebaseSession()
} }
companion object { companion object {
@@ -18,28 +18,28 @@ import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.onStart
/** /**
* This class store the Firebase token in SharedPrefs. * This class stores the Firebase installationId in SharedPrefs.
*/ */
interface FirebaseStore { interface FirebaseStore {
fun getFcmToken(): String? fun getInstallationId(): String?
fun fcmTokenFlow(): Flow<String?> fun fcmInstallationIdFlow(): Flow<String?>
fun storeFcmToken(token: String?) fun storeInstallationId(installationId: String?)
} }
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class SharedPreferencesFirebaseStore( class SharedPreferencesFirebaseStore(
private val sharedPreferences: SharedPreferences, private val sharedPreferences: SharedPreferences,
) : FirebaseStore { ) : FirebaseStore {
override fun getFcmToken(): String? { override fun getInstallationId(): String? {
return sharedPreferences.getString(PREFS_KEY_FCM_TOKEN, null) return sharedPreferences.getString(PREFS_KEY_FCM_INSTALLATION_ID, null)
} }
override fun fcmTokenFlow(): Flow<String?> { override fun fcmInstallationIdFlow(): Flow<String?> {
val flow = MutableStateFlow(getFcmToken()) val flow = MutableStateFlow(getInstallationId())
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, k -> val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, k ->
if (k == PREFS_KEY_FCM_TOKEN) { if (k == PREFS_KEY_FCM_INSTALLATION_ID) {
try { try {
flow.value = getFcmToken() flow.value = getInstallationId()
} catch (_: Exception) { } catch (_: Exception) {
flow.value = null flow.value = null
} }
@@ -50,13 +50,13 @@ class SharedPreferencesFirebaseStore(
.onCompletion { sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener) } .onCompletion { sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener) }
} }
override fun storeFcmToken(token: String?) { override fun storeInstallationId(installationId: String?) {
sharedPreferences.edit { sharedPreferences.edit {
putString(PREFS_KEY_FCM_TOKEN, token) putString(PREFS_KEY_FCM_INSTALLATION_ID, installationId)
} }
} }
companion object { companion object {
private const val PREFS_KEY_FCM_TOKEN = "FCM_TOKEN" private const val PREFS_KEY_FCM_INSTALLATION_ID = "FCM_TOKEN"
} }
} }
@@ -1,49 +0,0 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.pushproviders.firebase
import com.google.firebase.messaging.FirebaseMessaging
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import timber.log.Timber
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
interface FirebaseTokenDeleter {
/**
* Deletes the current Firebase token.
*/
suspend fun delete()
}
@ContributesBinding(AppScope::class)
class DefaultFirebaseTokenDeleter(
private val isPlayServiceAvailable: IsPlayServiceAvailable,
) : FirebaseTokenDeleter {
override suspend fun delete() {
// 'app should always check the device for a compatible Google Play services APK before accessing Google Play services features'
isPlayServiceAvailable.checkAvailableOrThrow()
suspendCoroutine { continuation ->
try {
FirebaseMessaging.getInstance().deleteToken()
.addOnSuccessListener {
continuation.resume(Unit)
}
.addOnFailureListener { e ->
Timber.e(e, "## deleteFirebaseToken() : failed")
continuation.resumeWithException(e)
}
} catch (e: Throwable) {
Timber.e(e, "## deleteFirebaseToken() : failed")
continuation.resumeWithException(e)
}
}
}
}
@@ -1,50 +0,0 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.pushproviders.firebase
import com.google.firebase.messaging.FirebaseMessaging
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import timber.log.Timber
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
interface FirebaseTokenGetter {
/**
* Read the current Firebase token from FirebaseMessaging.
* If the token does not exist, it will be generated.
*/
suspend fun get(): String
}
@ContributesBinding(AppScope::class)
class DefaultFirebaseTokenGetter(
private val isPlayServiceAvailable: IsPlayServiceAvailable,
) : FirebaseTokenGetter {
override suspend fun get(): String {
// 'app should always check the device for a compatible Google Play services APK before accessing Google Play services features'
isPlayServiceAvailable.checkAvailableOrThrow()
return suspendCoroutine { continuation ->
try {
FirebaseMessaging.getInstance().token
.addOnSuccessListener { token ->
continuation.resume(token)
}
.addOnFailureListener { e ->
Timber.e(e, "## retrievedFirebaseToken() : failed")
continuation.resumeWithException(e)
}
} catch (e: Throwable) {
Timber.e(e, "## retrievedFirebaseToken() : failed")
continuation.resumeWithException(e)
}
}
}
}
@@ -1,33 +0,0 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.pushproviders.firebase
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.core.extensions.runCatchingExceptions
interface FirebaseTokenRotator {
suspend fun rotate(): Result<Unit>
}
/**
* This class delete the Firebase token and generate a new one.
*/
@ContributesBinding(AppScope::class)
class DefaultFirebaseTokenRotator(
private val firebaseTokenDeleter: FirebaseTokenDeleter,
private val firebaseTokenGetter: FirebaseTokenGetter,
) : FirebaseTokenRotator {
override suspend fun rotate(): Result<Unit> {
return runCatchingExceptions {
firebaseTokenDeleter.delete()
firebaseTokenGetter.get()
}
}
}
@@ -10,7 +10,6 @@ package io.element.android.libraries.pushproviders.firebase
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.core.extensions.runCatchingExceptions
interface FirebaseTroubleshooter { interface FirebaseTroubleshooter {
suspend fun troubleshoot(): Result<Unit> suspend fun troubleshoot(): Result<Unit>
@@ -21,13 +20,9 @@ interface FirebaseTroubleshooter {
*/ */
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class DefaultFirebaseTroubleshooter( class DefaultFirebaseTroubleshooter(
private val newTokenHandler: FirebaseNewTokenHandler, private val rotateFirebaseSession: RotateFirebaseSession,
private val firebaseTokenGetter: FirebaseTokenGetter,
) : FirebaseTroubleshooter { ) : FirebaseTroubleshooter {
override suspend fun troubleshoot(): Result<Unit> { override suspend fun troubleshoot(): Result<Unit> {
return runCatchingExceptions { return rotateFirebaseSession()
val token = firebaseTokenGetter.get()
newTokenHandler.handle(token)
}
} }
} }
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.pushproviders.firebase
import com.google.firebase.messaging.FirebaseMessaging
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.pushproviders.firebase.util.runFirebaseTask
import timber.log.Timber
fun interface RegisterFirebaseSession {
/**
* Register the device to Firebase Messaging.
*/
suspend operator fun invoke(): Result<Unit>
}
@ContributesBinding(AppScope::class)
class DefaultRegisterFirebaseSession(
private val isPlayServiceAvailable: IsPlayServiceAvailable,
) : RegisterFirebaseSession {
override suspend operator fun invoke(): Result<Unit> {
// 'app should always check the device for a compatible Google Play services APK before accessing Google Play services features'
isPlayServiceAvailable.checkAvailableOrThrow()
return runFirebaseTask { FirebaseMessaging.getInstance().register() }
.onFailure { Timber.e(it, "## registerFirebaseMessaging() : failed") }
// Change return type from Void! to Unit
.map {}
}
}
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.pushproviders.firebase
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.core.extensions.runCatchingExceptions
fun interface RotateFirebaseSession {
suspend operator fun invoke(): Result<Unit>
}
/**
* This class deletes the Firebase installation id and generates a new one.
*/
@ContributesBinding(AppScope::class)
class DefaultRotateFirebaseSession(
private val registerFirebaseSession: RegisterFirebaseSession,
private val unregisterFirebaseSession: UnregisterFirebaseSession,
) : RotateFirebaseSession {
override suspend operator fun invoke(): Result<Unit> {
return runCatchingExceptions {
// Stop the current session, which will also delete the existing installation id from Firebase
unregisterFirebaseSession().getOrThrow()
// Register again to get a new installation id
registerFirebaseSession().getOrThrow()
}
}
}
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.pushproviders.firebase
import com.google.firebase.installations.FirebaseInstallations
import com.google.firebase.messaging.FirebaseMessaging
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.pushproviders.firebase.util.runFirebaseTask
import timber.log.Timber
fun interface UnregisterFirebaseSession {
/**
* Deletes the current Firebase token.
*/
suspend operator fun invoke(): Result<Unit>
}
@ContributesBinding(AppScope::class)
class DefaultUnregisterFirebaseSession(
private val isPlayServiceAvailable: IsPlayServiceAvailable,
) : UnregisterFirebaseSession {
override suspend operator fun invoke(): Result<Unit> {
// 'app should always check the device for a compatible Google Play services APK before accessing Google Play services features'
isPlayServiceAvailable.checkAvailableOrThrow()
return runFirebaseTask {
// Unregister the device from Firebase Messaging
FirebaseMessaging.getInstance().unregister()
// Also delete the existing installation id from Firebase
.continueWithTask { FirebaseInstallations.getInstance().delete() }
}
.onFailure {
Timber.e(it, "## unregisterFirebaseMessaging() : failed")
}
// Change return type from Void! to Unit
.map {}
}
}
@@ -24,7 +24,7 @@ import timber.log.Timber
private val loggerTag = LoggerTag("VectorFirebaseMessagingService", LoggerTag.PushLoggerTag) private val loggerTag = LoggerTag("VectorFirebaseMessagingService", LoggerTag.PushLoggerTag)
class VectorFirebaseMessagingService : FirebaseMessagingService() { class VectorFirebaseMessagingService : FirebaseMessagingService() {
@Inject lateinit var firebaseNewTokenHandler: FirebaseNewTokenHandler @Inject lateinit var firebaseNewInstallationIdHandler: FirebaseNewInstallationIdHandler
@Inject lateinit var pushParser: FirebasePushParser @Inject lateinit var pushParser: FirebasePushParser
@Inject lateinit var pushHandler: PushHandler @Inject lateinit var pushHandler: PushHandler
@Inject lateinit var fetchPushForegroundServiceManager: FetchPushForegroundServiceManager @Inject lateinit var fetchPushForegroundServiceManager: FetchPushForegroundServiceManager
@@ -36,10 +36,12 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
bindings<VectorFirebaseMessagingServiceBindings>().inject(this) bindings<VectorFirebaseMessagingServiceBindings>().inject(this)
} }
override fun onNewToken(token: String) { override fun onRegistered(installationId: String) {
Timber.tag(loggerTag.value).w("New Firebase token") super.onRegistered(installationId)
Timber.tag(loggerTag.value).w("New Firebase installation id")
coroutineScope.launch { coroutineScope.launch {
firebaseNewTokenHandler.handle(token) firebaseNewInstallationIdHandler.handle(installationId)
} }
} }
@@ -49,7 +49,7 @@ class FirebaseTokenTest(
override suspend fun run(coroutineScope: CoroutineScope) { override suspend fun run(coroutineScope: CoroutineScope) {
currentJob?.cancel() currentJob?.cancel()
delegate.start() delegate.start()
currentJob = firebaseStore.fcmTokenFlow() currentJob = firebaseStore.fcmInstallationIdFlow()
.onEach { token -> .onEach { token ->
if (token != null) { if (token != null) {
delegate.updateState( delegate.updateState(
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.pushproviders.firebase.util
import com.google.android.gms.tasks.Task
import io.element.android.libraries.core.extensions.runCatchingExceptions
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
suspend fun <T> runFirebaseTaskWithResult(closure: () -> Task<T>): Result<T> {
return runCatchingExceptions {
suspendCancellableCoroutine { continuation ->
try {
closure().addOnSuccessListener {
continuation.resume(it)
}.addOnFailureListener { e ->
continuation.resumeWithException(e)
}
} catch (e: Throwable) {
continuation.resumeWithException(e)
}
}
}
}
// Special case for `Void!` return type, which is not a valid Kotlin type. We convert it to `Unit` instead.
suspend fun runFirebaseTask(closure: () -> Task<Void?>): Result<Unit> {
return runFirebaseTaskWithResult<Unit> { closure().continueWith {} }
}
@@ -30,16 +30,16 @@ import io.element.android.tests.testutils.lambda.value
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
class DefaultFirebaseNewTokenHandlerTest { class DefaultFirebaseNewInstallationIdHandlerTest {
@Test @Test
fun `when a new token is received it is stored in the firebase store`() = runTest { fun `when a new token is received it is stored in the firebase store`() = runTest {
val firebaseStore = InMemoryFirebaseStore() val firebaseStore = InMemoryFirebaseStore()
assertThat(firebaseStore.getFcmToken()).isNull() assertThat(firebaseStore.getInstallationId()).isNull()
val firebaseNewTokenHandler = createDefaultFirebaseNewTokenHandler( val firebaseNewTokenHandler = createDefaultFirebaseNewTokenHandler(
firebaseStore = firebaseStore, firebaseStore = firebaseStore,
) )
firebaseNewTokenHandler.handle("aToken") firebaseNewTokenHandler.handle("aToken")
assertThat(firebaseStore.getFcmToken()).isEqualTo("aToken") assertThat(firebaseStore.getInstallationId()).isEqualTo("aToken")
} }
@Test @Test
@@ -142,8 +142,8 @@ class DefaultFirebaseNewTokenHandlerTest {
matrixClientProvider: MatrixClientProvider = FakeMatrixClientProvider(), matrixClientProvider: MatrixClientProvider = FakeMatrixClientProvider(),
firebaseStore: FirebaseStore = InMemoryFirebaseStore(), firebaseStore: FirebaseStore = InMemoryFirebaseStore(),
firebaseGatewayProvider: FirebaseGatewayProvider = FakeFirebaseGatewayProvider(), firebaseGatewayProvider: FirebaseGatewayProvider = FakeFirebaseGatewayProvider(),
): FirebaseNewTokenHandler { ): FirebaseNewInstallationIdHandler {
return DefaultFirebaseNewTokenHandler( return DefaultFirebaseNewInstallationIdHandler(
pusherSubscriber = pusherSubscriber, pusherSubscriber = pusherSubscriber,
sessionStore = sessionStore, sessionStore = sessionStore,
userPushStoreFactory = userPushStoreFactory, userPushStoreFactory = userPushStoreFactory,
@@ -10,10 +10,10 @@ package io.element.android.libraries.pushproviders.firebase
import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaError
class FakeFirebaseNewTokenHandler( class FakeFirebaseNewInstallationIdHandler(
private val handleResult: (String) -> Unit = { lambdaError() } private val handleResult: (String) -> Unit = { lambdaError() }
) : FirebaseNewTokenHandler { ) : FirebaseNewInstallationIdHandler {
override suspend fun handle(firebaseToken: String) { override suspend fun handle(installationId: String) {
handleResult(firebaseToken) handleResult(installationId)
} }
} }
@@ -10,10 +10,10 @@ package io.element.android.libraries.pushproviders.firebase
import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaError
class FakeFirebaseTokenRotator( class FakeRotateFirebaseSession(
private val rotateWithResult: () -> Result<Unit> = { lambdaError() } private val rotateWithResult: () -> Result<Unit> = { lambdaError() }
) : FirebaseTokenRotator { ) : RotateFirebaseSession {
override suspend fun rotate(): Result<Unit> { override suspend fun invoke(): Result<Unit> {
return rotateWithResult() return rotateWithResult()
} }
} }
@@ -61,7 +61,7 @@ class FirebasePushProviderTest {
val registerPusherResultLambda = lambdaRecorder<MatrixClient, String, String, Result<Unit>> { _, _, _ -> Result.success(Unit) } val registerPusherResultLambda = lambdaRecorder<MatrixClient, String, String, Result<Unit>> { _, _, _ -> Result.success(Unit) }
val firebasePushProvider = createFirebasePushProvider( val firebasePushProvider = createFirebasePushProvider(
firebaseStore = InMemoryFirebaseStore( firebaseStore = InMemoryFirebaseStore(
token = "aToken" installationId = "aToken"
), ),
pusherSubscriber = FakePusherSubscriber( pusherSubscriber = FakePusherSubscriber(
registerPusherResult = registerPusherResultLambda registerPusherResult = registerPusherResultLambda
@@ -78,7 +78,7 @@ class FirebasePushProviderTest {
fun `register ko no token`() = runTest { fun `register ko no token`() = runTest {
val firebasePushProvider = createFirebasePushProvider( val firebasePushProvider = createFirebasePushProvider(
firebaseStore = InMemoryFirebaseStore( firebaseStore = InMemoryFirebaseStore(
token = null installationId = null
), ),
pusherSubscriber = FakePusherSubscriber( pusherSubscriber = FakePusherSubscriber(
registerPusherResult = { _, _, _ -> Result.success(Unit) } registerPusherResult = { _, _, _ -> Result.success(Unit) }
@@ -92,7 +92,7 @@ class FirebasePushProviderTest {
fun `register ko error`() = runTest { fun `register ko error`() = runTest {
val firebasePushProvider = createFirebasePushProvider( val firebasePushProvider = createFirebasePushProvider(
firebaseStore = InMemoryFirebaseStore( firebaseStore = InMemoryFirebaseStore(
token = "aToken" installationId = "aToken"
), ),
pusherSubscriber = FakePusherSubscriber( pusherSubscriber = FakePusherSubscriber(
registerPusherResult = { _, _, _ -> Result.failure(AN_EXCEPTION) } registerPusherResult = { _, _, _ -> Result.failure(AN_EXCEPTION) }
@@ -108,7 +108,7 @@ class FirebasePushProviderTest {
val unregisterPusherResultLambda = lambdaRecorder<MatrixClient, String, String, Result<Unit>> { _, _, _ -> Result.success(Unit) } val unregisterPusherResultLambda = lambdaRecorder<MatrixClient, String, String, Result<Unit>> { _, _, _ -> Result.success(Unit) }
val firebasePushProvider = createFirebasePushProvider( val firebasePushProvider = createFirebasePushProvider(
firebaseStore = InMemoryFirebaseStore( firebaseStore = InMemoryFirebaseStore(
token = "aToken" installationId = "aToken"
), ),
pusherSubscriber = FakePusherSubscriber( pusherSubscriber = FakePusherSubscriber(
unregisterPusherResult = unregisterPusherResultLambda unregisterPusherResult = unregisterPusherResultLambda
@@ -125,7 +125,7 @@ class FirebasePushProviderTest {
fun `unregister no token - in this case, the error is ignored`() = runTest { fun `unregister no token - in this case, the error is ignored`() = runTest {
val firebasePushProvider = createFirebasePushProvider( val firebasePushProvider = createFirebasePushProvider(
firebaseStore = InMemoryFirebaseStore( firebaseStore = InMemoryFirebaseStore(
token = null installationId = null
), ),
) )
val result = firebasePushProvider.unregister(FakeMatrixClient()) val result = firebasePushProvider.unregister(FakeMatrixClient())
@@ -136,7 +136,7 @@ class FirebasePushProviderTest {
fun `unregister ko error`() = runTest { fun `unregister ko error`() = runTest {
val firebasePushProvider = createFirebasePushProvider( val firebasePushProvider = createFirebasePushProvider(
firebaseStore = InMemoryFirebaseStore( firebaseStore = InMemoryFirebaseStore(
token = "aToken" installationId = "aToken"
), ),
pusherSubscriber = FakePusherSubscriber( pusherSubscriber = FakePusherSubscriber(
unregisterPusherResult = { _, _, _ -> Result.failure(AN_EXCEPTION) } unregisterPusherResult = { _, _, _ -> Result.failure(AN_EXCEPTION) }
@@ -150,7 +150,7 @@ class FirebasePushProviderTest {
fun `getCurrentUserPushConfig no push ket`() = runTest { fun `getCurrentUserPushConfig no push ket`() = runTest {
val firebasePushProvider = createFirebasePushProvider( val firebasePushProvider = createFirebasePushProvider(
firebaseStore = InMemoryFirebaseStore( firebaseStore = InMemoryFirebaseStore(
token = null installationId = null
) )
) )
val result = firebasePushProvider.getPushConfig(A_SESSION_ID) val result = firebasePushProvider.getPushConfig(A_SESSION_ID)
@@ -161,7 +161,7 @@ class FirebasePushProviderTest {
fun `getCurrentUserPushConfig ok`() = runTest { fun `getCurrentUserPushConfig ok`() = runTest {
val firebasePushProvider = createFirebasePushProvider( val firebasePushProvider = createFirebasePushProvider(
firebaseStore = InMemoryFirebaseStore( firebaseStore = InMemoryFirebaseStore(
token = "aToken" installationId = "aToken"
), ),
) )
val result = firebasePushProvider.getPushConfig(A_SESSION_ID) val result = firebasePushProvider.getPushConfig(A_SESSION_ID)
@@ -169,10 +169,10 @@ class FirebasePushProviderTest {
} }
@Test @Test
fun `rotateToken invokes the FirebaseTokenRotator`() = runTest { fun `rotateToken invokes the FirebaseMessagingSessionRotator`() = runTest {
val lambda = lambdaRecorder<Result<Unit>> { Result.success(Unit) } val lambda = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
val firebasePushProvider = createFirebasePushProvider( val firebasePushProvider = createFirebasePushProvider(
firebaseTokenRotator = FakeFirebaseTokenRotator(lambda), rotateFirebaseSession = FakeRotateFirebaseSession(lambda),
) )
firebasePushProvider.rotateToken() firebasePushProvider.rotateToken()
lambda.assertions().isCalledOnce() lambda.assertions().isCalledOnce()
@@ -194,14 +194,14 @@ class FirebasePushProviderTest {
firebaseStore: FirebaseStore = InMemoryFirebaseStore(), firebaseStore: FirebaseStore = InMemoryFirebaseStore(),
pusherSubscriber: PusherSubscriber = FakePusherSubscriber(), pusherSubscriber: PusherSubscriber = FakePusherSubscriber(),
isPlayServiceAvailable: IsPlayServiceAvailable = FakeIsPlayServiceAvailable(false), isPlayServiceAvailable: IsPlayServiceAvailable = FakeIsPlayServiceAvailable(false),
firebaseTokenRotator: FirebaseTokenRotator = FakeFirebaseTokenRotator(), rotateFirebaseSession: RotateFirebaseSession = FakeRotateFirebaseSession(),
firebaseGatewayProvider: FirebaseGatewayProvider = FakeFirebaseGatewayProvider() firebaseGatewayProvider: FirebaseGatewayProvider = FakeFirebaseGatewayProvider()
): FirebasePushProvider { ): FirebasePushProvider {
return FirebasePushProvider( return FirebasePushProvider(
firebaseStore = firebaseStore, firebaseStore = firebaseStore,
pusherSubscriber = pusherSubscriber, pusherSubscriber = pusherSubscriber,
isPlayServiceAvailable = isPlayServiceAvailable, isPlayServiceAvailable = isPlayServiceAvailable,
firebaseTokenRotator = firebaseTokenRotator, rotateFirebaseSession = rotateFirebaseSession,
firebaseGatewayProvider = firebaseGatewayProvider, firebaseGatewayProvider = firebaseGatewayProvider,
) )
} }
@@ -12,13 +12,13 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
class InMemoryFirebaseStore( class InMemoryFirebaseStore(
private var token: String? = null private var installationId: String? = null
) : FirebaseStore { ) : FirebaseStore {
override fun getFcmToken(): String? = token override fun getInstallationId(): String? = installationId
override fun fcmTokenFlow(): Flow<String?> = flowOf(token) override fun fcmInstallationIdFlow(): Flow<String?> = flowOf(installationId)
override fun storeFcmToken(token: String?) { override fun storeInstallationId(installationId: String?) {
this.token = token this.installationId = installationId
} }
} }
@@ -171,22 +171,22 @@ class VectorFirebaseMessagingServiceTest : RobolectricTest() {
fun `test new token is forwarded to the handler`() = runTest { fun `test new token is forwarded to the handler`() = runTest {
val lambda = lambdaRecorder<String, Unit> { } val lambda = lambdaRecorder<String, Unit> { }
val vectorFirebaseMessagingService = createVectorFirebaseMessagingService( val vectorFirebaseMessagingService = createVectorFirebaseMessagingService(
firebaseNewTokenHandler = FakeFirebaseNewTokenHandler(handleResult = lambda) firebaseNewInstallationIdHandler = FakeFirebaseNewInstallationIdHandler(handleResult = lambda)
) )
vectorFirebaseMessagingService.onNewToken("aToken") vectorFirebaseMessagingService.onRegistered("installationId")
advanceUntilIdle() advanceUntilIdle()
lambda.assertions() lambda.assertions()
.isCalledOnce() .isCalledOnce()
.with(value("aToken")) .with(value("installationId"))
} }
private fun TestScope.createVectorFirebaseMessagingService( private fun TestScope.createVectorFirebaseMessagingService(
firebaseNewTokenHandler: FirebaseNewTokenHandler = FakeFirebaseNewTokenHandler(), firebaseNewInstallationIdHandler: FirebaseNewInstallationIdHandler = FakeFirebaseNewInstallationIdHandler(),
pushHandler: PushHandler = FakePushHandler(), pushHandler: PushHandler = FakePushHandler(),
pushHandlingWakeLock: FakeFetchPushForegroundServiceManager = FakeFetchPushForegroundServiceManager(), pushHandlingWakeLock: FakeFetchPushForegroundServiceManager = FakeFetchPushForegroundServiceManager(),
): VectorFirebaseMessagingService { ): VectorFirebaseMessagingService {
return VectorFirebaseMessagingService().apply { return VectorFirebaseMessagingService().apply {
this.firebaseNewTokenHandler = firebaseNewTokenHandler this.firebaseNewInstallationIdHandler = firebaseNewInstallationIdHandler
this.pushParser = FirebasePushParser() this.pushParser = FirebasePushParser()
this.pushHandler = pushHandler this.pushHandler = pushHandler
this.coroutineScope = this@createVectorFirebaseMessagingService this.coroutineScope = this@createVectorFirebaseMessagingService
@@ -45,7 +45,7 @@ class FirebaseTokenTestTest {
firebaseStore = firebaseStore, firebaseStore = firebaseStore,
firebaseTroubleshooter = FakeFirebaseTroubleshooter( firebaseTroubleshooter = FakeFirebaseTroubleshooter(
troubleShootResult = { troubleShootResult = {
firebaseStore.storeFcmToken(FAKE_TOKEN) firebaseStore.storeInstallationId(FAKE_TOKEN)
Result.success(Unit) Result.success(Unit)
} }
), ),
@@ -70,7 +70,7 @@ class FirebaseTokenTestTest {
firebaseStore = firebaseStore, firebaseStore = firebaseStore,
firebaseTroubleshooter = FakeFirebaseTroubleshooter( firebaseTroubleshooter = FakeFirebaseTroubleshooter(
troubleShootResult = { troubleShootResult = {
firebaseStore.storeFcmToken(FAKE_TOKEN) firebaseStore.storeInstallationId(FAKE_TOKEN)
Result.success(Unit) Result.success(Unit)
} }
), ),