Split push module into several modules: Firebase, UnifiedPush, store

This commit is contained in:
Benoit Marty
2023-04-07 20:42:59 +02:00
committed by Benoit Marty
parent 1134f50090
commit 219b97eea7
53 changed files with 768 additions and 276 deletions

View File

@@ -45,6 +45,7 @@ dependencies {
implementation(projects.libraries.architecture)
implementation(projects.libraries.matrix.api)
implementation(projects.libraries.push.api)
implementation(projects.libraries.pushproviders.api)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.matrixui)
implementation(projects.libraries.uiStrings)

View File

@@ -46,7 +46,9 @@ class LoggedInPresenter @Inject constructor(
override fun present(): LoggedInState {
LaunchedEffect(Unit) {
// Ensure pusher is registered
pushService.registerFirebasePusher(matrixClient)
// TODO Register with Firebase for now
val pushProvider = pushService.getAvailablePushProviders().firstOrNull() ?: return@LaunchedEffect
pushService.registerWith(matrixClient, pushProvider, pushProvider.getDistributorNames().first())
}
val permissionsState = postNotificationPermissionsPresenter.present()

View File

@@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.permissions.api.PermissionsPresenter
import io.element.android.libraries.permissions.noop.NoopPermissionsPresenter
import io.element.android.libraries.push.api.PushService
import io.element.android.libraries.push.providers.api.PushProvider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -55,7 +56,11 @@ class LoggedInPresenterTest {
override fun notificationStyleChanged() {
}
override suspend fun registerFirebasePusher(matrixClient: MatrixClient) {
override fun getAvailablePushProviders(): List<PushProvider> {
return emptyList()
}
override suspend fun registerWith(matrixClient: MatrixClient, pushProvider: PushProvider, distributorName: String) {
}
override suspend fun testPush() {

View File

@@ -26,4 +26,5 @@ dependencies {
implementation(libs.androidx.corektx)
implementation(libs.coroutines.core)
implementation(projects.libraries.matrix.api)
implementation(projects.libraries.pushproviders.api)
}

View File

@@ -17,12 +17,21 @@
package io.element.android.libraries.push.api
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.push.providers.api.PushProvider
interface PushService {
// TODO Move away
fun notificationStyleChanged()
// Ensure pusher is registered
suspend fun registerFirebasePusher(matrixClient: MatrixClient)
fun getAvailablePushProviders(): List<PushProvider>
/**
* Will unregister any previous pusher and register a new one with the provided [PushProvider].
*
* The method has effect only if the [PushProvider] is different than the current one.
*/
suspend fun registerWith(matrixClient: MatrixClient, pushProvider: PushProvider, distributorName: String)
// TODO Move away
suspend fun testPush()
}

View File

@@ -43,6 +43,8 @@ dependencies {
implementation(projects.libraries.androidutils)
implementation(projects.libraries.network)
implementation(projects.libraries.matrix.api)
api(projects.libraries.pushproviders.api)
api(projects.libraries.pushstore.api)
api(projects.libraries.push.api)
implementation(projects.services.analytics.api)
@@ -53,12 +55,10 @@ dependencies {
exclude(group = "com.android.support", module = "support-annotations")
}
// TODO Remove
implementation(platform(libs.google.firebase.bom))
implementation("com.google.firebase:firebase-messaging-ktx")
// UnifiedPush
api(libs.unifiedpush)
testImplementation(libs.test.junit)
testImplementation(libs.test.mockk)
testImplementation(libs.test.truth)

View File

@@ -14,63 +14,15 @@
~ limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application>
<!-- Firebase components -->
<meta-data
android:name="firebase_analytics_collection_deactivated"
android:value="true" />
<service
android:name=".firebase.VectorFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<!-- UnifiedPush -->
<receiver
android:name=".unifiedpush.VectorUnifiedPushMessagingReceiver"
android:enabled="true"
android:exported="true"
tools:ignore="ExportedReceiver">
<intent-filter>
<action android:name="org.unifiedpush.android.connector.MESSAGE" />
<action android:name="org.unifiedpush.android.connector.UNREGISTERED" />
<action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT" />
<action android:name="org.unifiedpush.android.connector.REGISTRATION_FAILED" />
<action android:name="org.unifiedpush.android.connector.REGISTRATION_REFUSED" />
</intent-filter>
</receiver>
<receiver
android:name=".unifiedpush.KeepInternalDistributor"
android:enabled="true"
android:exported="false">
<intent-filter>
<!--
This action is checked to track installed and uninstalled distributors.
We declare it to keep the background sync as an internal
unifiedpush distributor.
-->
<action android:name="org.unifiedpush.android.distributor.REGISTER" />
</intent-filter>
</receiver>
<receiver
android:name=".notifications.TestNotificationReceiver"
android:exported="false" />
<receiver
android:name=".notifications.NotificationBroadcastReceiver"
android:enabled="true"
android:exported="false" />
</application>
</manifest>

View File

@@ -20,27 +20,29 @@ import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.push.api.PushService
import io.element.android.libraries.push.impl.config.PushConfig
import io.element.android.libraries.push.impl.log.pushLoggerTag
import io.element.android.libraries.push.impl.notifications.NotificationDrawerManager
import timber.log.Timber
import io.element.android.libraries.push.providers.api.PushProvider
import javax.inject.Inject
@ContributesBinding(AppScope::class)
class DefaultPushService @Inject constructor(
private val notificationDrawerManager: NotificationDrawerManager,
private val pushersManager: PushersManager,
private val fcmHelper: FcmHelper,
private val pushProviders: Set<@JvmSuppressWildcards PushProvider>,
) : PushService {
override fun notificationStyleChanged() {
notificationDrawerManager.notificationStyleChanged()
}
override suspend fun registerFirebasePusher(matrixClient: MatrixClient) {
val pushKey = fcmHelper.getFcmToken() ?: return Unit.also {
Timber.tag(pushLoggerTag.value).w("Unable to register pusher, Firebase token is not known.")
}
pushersManager.registerPusher(matrixClient, pushKey, PushConfig.pusher_http_url)
override fun getAvailablePushProviders(): List<PushProvider> {
// TODO Sort by priority?
return pushProviders.toList()
}
override suspend fun registerWith(matrixClient: MatrixClient, pushProvider: PushProvider, distributorName: String) {
// TODO Get current push provider, compare with provided one, then unregister and register if different, and store change
pushProvider.registerWith(matrixClient, distributorName)
}
override suspend fun testPush() {

View File

@@ -1,49 +0,0 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.push.impl
interface FcmHelper {
fun isFirebaseAvailable(): Boolean
/**
* Retrieves the FCM registration token.
*
* @return the FCM token or null if not received from FCM.
*/
fun getFcmToken(): String?
/**
* Store FCM token to the SharedPrefs.
*
* @param token the token to store.
*/
fun storeFcmToken(token: String?)
/**
* onNewToken may not be called on application upgrade, so ensure my shared pref is set.
*
* @param pushersManager the instance to register the pusher on.
* @param registerPusher whether the pusher should be registered.
*/
fun ensureFcmTokenIsRetrieved(pushersManager: PushersManager, registerPusher: Boolean)
/*
fun onEnterForeground(activeSessionHolder: ActiveSessionHolder)
fun onEnterBackground(activeSessionHolder: ActiveSessionHolder)
*/
}

View File

@@ -16,7 +16,9 @@
package io.element.android.libraries.push.impl
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.core.EventId
@@ -26,10 +28,9 @@ import io.element.android.libraries.push.impl.clientsecret.PushClientSecret
import io.element.android.libraries.push.impl.config.PushConfig
import io.element.android.libraries.push.impl.log.pushLoggerTag
import io.element.android.libraries.push.impl.pushgateway.PushGatewayNotifyRequest
import io.element.android.libraries.push.impl.userpushstore.UserPushStoreFactory
import io.element.android.libraries.push.impl.userpushstore.isFirebase
import io.element.android.libraries.push.providers.api.PusherSubscriber
import io.element.android.libraries.pushstore.api.UserPushStoreFactory
import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.libraries.sessionstorage.api.toUserList
import io.element.android.services.toolbox.api.appname.AppNameProvider
import timber.log.Timber
import javax.inject.Inject
@@ -38,8 +39,9 @@ internal const val DEFAULT_PUSHER_FILE_TAG = "mobile"
private val loggerTag = LoggerTag("PushersManager", pushLoggerTag)
@ContributesBinding(AppScope::class)
class PushersManager @Inject constructor(
private val unifiedPushHelper: UnifiedPushHelper,
// private val unifiedPushHelper: UnifiedPushHelper,
// private val localeProvider: LocaleProvider,
private val appNameProvider: AppNameProvider,
// private val getDeviceInfoUseCase: GetDeviceInfoUseCase,
@@ -48,14 +50,14 @@ class PushersManager @Inject constructor(
private val sessionStore: SessionStore,
private val matrixAuthenticationService: MatrixAuthenticationService,
private val userPushStoreFactory: UserPushStoreFactory,
private val fcmHelper: FcmHelper,
) {
): PusherSubscriber {
// TODO Move this to the PushProvider API
suspend fun testPush() {
pushGatewayNotifyRequest.execute(
PushGatewayNotifyRequest.Params(
url = unifiedPushHelper.getPushGateway() ?: return,
url = "TODO", // unifiedPushHelper.getPushGateway() ?: return,
appId = PushConfig.pusher_app_id,
pushKey = unifiedPushHelper.getEndpointOrToken().orEmpty(),
pushKey = "TODO", // unifiedPushHelper.getEndpointOrToken().orEmpty(),
eventId = TEST_EVENT_ID
)
)
@@ -73,26 +75,10 @@ class PushersManager @Inject constructor(
TODO()
}
suspend fun onNewFirebaseToken(firebaseToken: String) {
fcmHelper.storeFcmToken(firebaseToken)
// Register the pusher for all the sessions
sessionStore.getAllSessions().toUserList().forEach { userId ->
val userDataStore = userPushStoreFactory.create(userId)
if (userDataStore.isFirebase()) {
matrixAuthenticationService.restoreSession(SessionId(userId)).getOrNull()?.use { client ->
registerPusher(client, firebaseToken, PushConfig.pusher_http_url)
}
} else {
Timber.tag(loggerTag.value).d("This session is not using Firebase pusher")
}
}
}
/**
* Register a pusher to the server if not done yet.
*/
suspend fun registerPusher(matrixClient: MatrixClient, pushKey: String, gateway: String) {
override suspend fun registerPusher(matrixClient: MatrixClient, pushKey: String, gateway: String) {
val userDataStore = userPushStoreFactory.create(matrixClient.sessionId.value)
if (userDataStore.getCurrentRegisteredPushKey() == pushKey) {
Timber.tag(loggerTag.value).d("Unnecessary to register again the same pusher")

View File

@@ -17,25 +17,8 @@
package io.element.android.libraries.push.impl.config
object PushConfig {
/**
* It is the push gateway for FCM embedded distributor.
* Note: pusher_http_url should have path '/_matrix/push/v1/notify' -->
*/
const val pusher_http_url: String = "https://matrix.org/_matrix/push/v1/notify"
/**
* It is the push gateway for UnifiedPush.
* Note: default_push_gateway_http_url should have path '/_matrix/push/v1/notify'
*/
const val default_push_gateway_http_url: String = "https://matrix.gateway.unifiedpush.org/_matrix/push/v1/notify"
/**
* Note: pusher_app_id cannot exceed 64 chars.
*/
const val pusher_app_id: String = "im.vector.app.android"
/**
* Set to true to allow external push distributor such as Ntfy.
*/
const val allowExternalUnifiedPushDistributors: Boolean = false
}

View File

@@ -21,9 +21,11 @@ import android.content.Intent
import android.os.Handler
import android.os.Looper
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.androidutils.network.WifiDetector
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.push.api.store.PushDataStore
@@ -34,6 +36,8 @@ import io.element.android.libraries.push.impl.notifications.NotifiableEventResol
import io.element.android.libraries.push.impl.notifications.NotificationActionIds
import io.element.android.libraries.push.impl.notifications.NotificationDrawerManager
import io.element.android.libraries.push.impl.store.DefaultPushDataStore
import io.element.android.libraries.push.providers.api.PushData
import io.element.android.libraries.push.providers.api.PushHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
@@ -43,7 +47,8 @@ import javax.inject.Inject
private val loggerTag = LoggerTag("PushHandler", pushLoggerTag)
class PushHandler @Inject constructor(
@ContributesBinding(AppScope::class)
class DefaultPushHandler @Inject constructor(
private val notificationDrawerManager: NotificationDrawerManager,
private val notifiableEventResolver: NotifiableEventResolver,
private val pushDataStore: PushDataStore,
@@ -53,7 +58,7 @@ class PushHandler @Inject constructor(
@ApplicationContext private val context: Context,
private val buildMeta: BuildMeta,
private val matrixAuthenticationService: MatrixAuthenticationService,
) {
): PushHandler {
private val coroutineScope = CoroutineScope(SupervisorJob())
private val wifiDetector: WifiDetector = WifiDetector(context)
@@ -68,7 +73,7 @@ class PushHandler @Inject constructor(
*
* @param pushData the data received in the push.
*/
suspend fun handle(pushData: PushData) {
override suspend fun handle(pushData: PushData) {
Timber.tag(loggerTag.value).d("## handling pushData")
if (buildMeta.lowPrivacyLoggingEnabled) {

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id("io.element.android-library")
}
android {
namespace = "io.element.android.libraries.push.providers.api"
}
dependencies {
implementation(projects.libraries.core)
implementation(projects.libraries.matrix.api)
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.push
package io.element.android.libraries.push.providers.api
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
@@ -28,8 +28,8 @@ import io.element.android.libraries.matrix.api.core.RoomId
* @property clientSecret data used when the pusher was configured, to be able to determine the session.
*/
data class PushData(
val eventId: EventId,
val roomId: RoomId,
val unread: Int?,
val clientSecret: String?,
val eventId: EventId,
val roomId: RoomId,
val unread: Int?,
val clientSecret: String?,
)

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.push.providers.api
interface PushHandler {
suspend fun handle(pushData: PushData)
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.push.providers.api
import io.element.android.libraries.matrix.api.MatrixClient
/**
* This is the main API for this module
*/
interface PushProvider {
fun getDistributorNames(): List<String>
suspend fun registerWith(matrixClient: MatrixClient, distributorName: String)
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.push.providers.api
import io.element.android.libraries.matrix.api.MatrixClient
interface PusherSubscriber {
suspend fun registerPusher(matrixClient: MatrixClient, pushKey: String, gateway: String)
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id("io.element.android-library")
alias(libs.plugins.anvil)
// kotlin("plugin.serialization") version "1.8.10"
}
android {
namespace = "io.element.android.libraries.push.providers.firebase"
}
anvil {
generateDaggerFactories.set(true)
}
dependencies {
implementation(libs.dagger)
implementation(libs.androidx.corektx)
implementation(projects.libraries.architecture)
implementation(projects.libraries.core)
implementation(projects.libraries.di)
implementation(projects.libraries.matrix.api)
implementation(projects.libraries.pushstore.api)
implementation(projects.libraries.pushproviders.api)
implementation(platform(libs.google.firebase.bom))
implementation("com.google.firebase:firebase-messaging-ktx")
testImplementation(libs.test.junit)
testImplementation(libs.test.truth)
testImplementation(projects.libraries.matrix.test)
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2023 New Vector Ltd
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<!-- Firebase components -->
<meta-data
android:name="firebase_analytics_collection_deactivated"
android:value="true" />
<service
android:name="io.element.android.libraries.push.providers.firebase.VectorFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>

View File

@@ -14,24 +14,22 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.firebase
package io.element.android.libraries.push.providers.firebase
import io.element.android.libraries.push.impl.FcmHelper
import io.element.android.libraries.push.impl.PushersManager
import io.element.android.libraries.push.impl.UnifiedPushHelper
import javax.inject.Inject
// TODO
class EnsureFcmTokenIsRetrievedUseCase @Inject constructor(
private val unifiedPushHelper: UnifiedPushHelper,
private val fcmHelper: FcmHelper,
// private val unifiedPushHelper: UnifiedPushHelper,
// private val fcmHelper: FcmHelper,
// private val activeSessionHolder: ActiveSessionHolder,
) {
fun execute(pushersManager: PushersManager, registerPusher: Boolean) {
if (unifiedPushHelper.isEmbeddedDistributor()) {
fcmHelper.ensureFcmTokenIsRetrieved(pushersManager, shouldAddHttpPusher(registerPusher))
}
}
// fun execute(pushersManager: PushersManager, registerPusher: Boolean) {
// if (unifiedPushHelper.isEmbeddedDistributor()) {
// fcmHelper.ensureFcmTokenIsRetrieved(pushersManager, shouldAddHttpPusher(registerPusher))
// }
// }
private fun shouldAddHttpPusher(registerPusher: Boolean) = if (registerPusher) {
/*

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.push.providers.firebase
object FirebaseConfig {
/**
* It is the push gateway for firebase.
* Note: pusher_http_url should have path '/_matrix/push/v1/notify' -->
*/
const val pusher_http_url: String = "https://matrix.org/_matrix/push/v1/notify"
const val internalName = "NOTIFICATION_METHOD_FIREBASE"
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +14,9 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.firebase
package io.element.android.libraries.push.providers.firebase
import io.element.android.libraries.push.impl.push.PushData
import io.element.android.libraries.push.providers.api.PushData
import javax.inject.Inject
class FirebasePushParser @Inject constructor() {

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.push.providers.firebase
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.push.providers.api.PushProvider
import io.element.android.libraries.push.providers.api.PusherSubscriber
import timber.log.Timber
import javax.inject.Inject
private val loggerTag = LoggerTag("FirebasePushProvider")
class FirebasePushProvider @Inject constructor(
private val googleFcmHelper: GoogleFcmHelper,
private val pusherSubscriber: PusherSubscriber,
) : PushProvider {
override fun getDistributorNames(): List<String> {
// Must return an non-empty list for now
return listOf("unused")
}
override suspend fun registerWith(matrixClient: MatrixClient, distributorName: String) {
val pushKey = googleFcmHelper.getFcmToken() ?: return Unit.also {
Timber.tag(loggerTag.value).w("Unable to register pusher, Firebase token is not known.")
}
pusherSubscriber.registerPusher(matrixClient, pushKey, FirebaseConfig.pusher_http_url)
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.push.providers.firebase
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.push.providers.api.PusherSubscriber
import io.element.android.libraries.pushstore.api.UserPushStoreFactory
import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.libraries.sessionstorage.api.toUserList
import timber.log.Timber
import javax.inject.Inject
private val loggerTag = LoggerTag("FirebaseSetPusher")
// TODO Rename
class FirebaseSetPusher @Inject constructor(
private val pusherSubscriber: PusherSubscriber,
private val sessionStore: SessionStore,
private val userPushStoreFactory: UserPushStoreFactory,
private val matrixAuthenticationService: MatrixAuthenticationService,
) {
suspend fun onNewFirebaseToken(firebaseToken: String) {
// Register the pusher for all the sessions
sessionStore.getAllSessions().toUserList().forEach { userId ->
val userDataStore = userPushStoreFactory.create(userId)
if (userDataStore.getNotificationMethod() == FirebaseConfig.internalName) {
matrixAuthenticationService.restoreSession(SessionId(userId)).getOrNull()?.use { client ->
pusherSubscriber.registerPusher(client, firebaseToken, FirebaseConfig.pusher_http_url)
}
} else {
Timber.tag(loggerTag.value).d("This session is not using Firebase pusher")
}
}
}
}

View File

@@ -14,44 +14,37 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl
package io.element.android.libraries.push.providers.firebase
import android.content.Context
import android.content.SharedPreferences
import android.widget.Toast
import androidx.core.content.edit
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.google.firebase.messaging.FirebaseMessaging
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.DefaultPreferences
import kotlinx.coroutines.runBlocking
import timber.log.Timber
import javax.inject.Inject
/**
* This class store the FCM token in SharedPrefs and ensure this token is retrieved.
* It has an alter ego in the fdroid variant.
*/
@ContributesBinding(AppScope::class)
// TODO Rename to store?
class GoogleFcmHelper @Inject constructor(
@ApplicationContext private val context: Context,
@DefaultPreferences private val sharedPrefs: SharedPreferences,
) : FcmHelper {
override fun isFirebaseAvailable(): Boolean = true
override fun getFcmToken(): String? {
) {
fun getFcmToken(): String? {
return sharedPrefs.getString(PREFS_KEY_FCM_TOKEN, null)
}
override fun storeFcmToken(token: String?) {
fun storeFcmToken(token: String?) {
sharedPrefs.edit {
putString(PREFS_KEY_FCM_TOKEN, token)
}
}
/*
override fun ensureFcmTokenIsRetrieved(pushersManager: PushersManager, registerPusher: Boolean) {
// 'app should always check the device for a compatible Google Play services APK before accessing Google Play services features'
if (checkPlayServices(context)) {
@@ -76,6 +69,7 @@ class GoogleFcmHelper @Inject constructor(
Timber.e("No valid Google Play Services found. Cannot use FCM.")
}
}
*/
/**
* Check the device to make sure it has the Google Play Services APK. If

View File

@@ -14,11 +14,11 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.firebase
package io.element.android.libraries.push.providers.firebase
import io.element.android.libraries.matrix.api.core.asEventId
import io.element.android.libraries.matrix.api.core.asRoomId
import io.element.android.libraries.push.impl.push.PushData
import io.element.android.libraries.push.providers.api.PushData
/**
* In this case, the format is:

View File

@@ -14,27 +14,26 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.firebase
package io.element.android.libraries.push.providers.firebase
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.push.impl.PushersManager
import io.element.android.libraries.push.impl.log.pushLoggerTag
import io.element.android.libraries.push.impl.push.PushHandler
import io.element.android.libraries.push.providers.api.PushHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
private val loggerTag = LoggerTag("Firebase", pushLoggerTag)
private val loggerTag = LoggerTag("Firebase")
class VectorFirebaseMessagingService : FirebaseMessagingService() {
@Inject lateinit var pushersManager: PushersManager
@Inject lateinit var firebaseSetPusher: FirebaseSetPusher
@Inject lateinit var pushParser: FirebasePushParser
@Inject lateinit var pushHandler: PushHandler
@Inject lateinit var googleFcmHelper: GoogleFcmHelper
private val coroutineScope = CoroutineScope(SupervisorJob())
@@ -45,8 +44,9 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
override fun onNewToken(token: String) {
Timber.tag(loggerTag.value).d("New Firebase token")
googleFcmHelper.storeFcmToken(token)
coroutineScope.launch {
pushersManager.onNewFirebaseToken(token)
firebaseSetPusher.onNewFirebaseToken(token)
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.firebase
package io.element.android.libraries.push.providers.firebase
import com.squareup.anvil.annotations.ContributesTo
import io.element.android.libraries.di.AppScope

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.push.providers.firebase.di
import com.squareup.anvil.annotations.ContributesTo
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.push.providers.api.PushProvider
import io.element.android.libraries.push.providers.firebase.FirebasePushProvider
@Module
@ContributesTo(AppScope::class)
interface FirebaseModule {
@Binds
@IntoSet
fun bind(pushProvider: FirebasePushProvider): PushProvider
}

View File

@@ -14,12 +14,12 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.firebase
package io.element.android.libraries.push.providers.firebase
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.push.impl.push.PushData
import io.element.android.libraries.push.providers.api.PushData
import org.junit.Test
class FirebasePushParserTest {

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id("io.element.android-library")
alias(libs.plugins.anvil)
kotlin("plugin.serialization") version "1.8.10"
}
android {
namespace = "io.element.android.libraries.push.providers.unifiedpush"
}
anvil {
generateDaggerFactories.set(true)
}
dependencies {
implementation(libs.dagger)
implementation(projects.libraries.core)
implementation(projects.libraries.matrix.api)
implementation(projects.libraries.pushproviders.api)
implementation(projects.libraries.architecture)
implementation(projects.libraries.core)
implementation(projects.services.toolbox.api)
implementation(libs.serialization.json)
// UnifiedPush library
api(libs.unifiedpush)
testImplementation(libs.test.junit)
testImplementation(libs.test.truth)
testImplementation(projects.libraries.matrix.test)
}

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2023 New Vector Ltd
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application>
<receiver
android:name=".VectorUnifiedPushMessagingReceiver"
android:enabled="true"
android:exported="true"
tools:ignore="ExportedReceiver">
<intent-filter>
<action android:name="org.unifiedpush.android.connector.MESSAGE" />
<action android:name="org.unifiedpush.android.connector.UNREGISTERED" />
<action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT" />
<action android:name="org.unifiedpush.android.connector.REGISTRATION_FAILED" />
<action android:name="org.unifiedpush.android.connector.REGISTRATION_REFUSED" />
</intent-filter>
</receiver>
<receiver
android:name=".KeepInternalDistributor"
android:enabled="true"
android:exported="false">
<intent-filter>
<!--
This action is checked to track installed and uninstalled distributors.
We declare it to keep the background sync as an internal
unifiedpush distributor.
-->
<action android:name="org.unifiedpush.android.distributor.REGISTER" />
</intent-filter>
</receiver>
</application>
</manifest>

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.unifiedpush
package io.element.android.libraries.push.providers.unifiedpush
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.unifiedpush
package io.element.android.libraries.push.providers.unifiedpush
import android.content.BroadcastReceiver
import android.content.Context

View File

@@ -14,11 +14,11 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.unifiedpush
package io.element.android.libraries.push.providers.unifiedpush
import io.element.android.libraries.matrix.api.core.asEventId
import io.element.android.libraries.matrix.api.core.asRoomId
import io.element.android.libraries.push.impl.push.PushData
import io.element.android.libraries.push.providers.api.PushData
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -14,11 +14,10 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.unifiedpush
package io.element.android.libraries.push.providers.unifiedpush
import android.content.Context
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.push.impl.config.PushConfig
import org.unifiedpush.android.connector.UnifiedPush
import javax.inject.Inject
@@ -37,11 +36,6 @@ class RegisterUnifiedPushUseCase @Inject constructor(
return RegisterUnifiedPushResult.Success
}
if (!PushConfig.allowExternalUnifiedPushDistributors) {
saveAndRegisterApp(context.packageName)
return RegisterUnifiedPushResult.Success
}
if (UnifiedPush.getDistributor(context).isNotEmpty()) {
registerApp()
return RegisterUnifiedPushResult.Success

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush
object UnifiedPushConfig {
/**
* It is the push gateway for UnifiedPush.
* Note: default_push_gateway_http_url should have path '/_matrix/push/v1/notify'
*/
const val default_push_gateway_http_url: String = "https://matrix.gateway.unifiedpush.org/_matrix/push/v1/notify"
const val internalName = "NOTIFICATION_METHOD_UNIFIEDPUSH"
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,12 +14,10 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl
package io.element.android.libraries.push.providers.unifiedpush
import android.content.Context
import io.element.android.libraries.androidutils.system.getApplicationLabel
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.push.impl.config.PushConfig
import io.element.android.services.toolbox.api.strings.StringProvider
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@@ -31,8 +29,6 @@ import javax.inject.Inject
class UnifiedPushHelper @Inject constructor(
@ApplicationContext private val context: Context,
private val unifiedPushStore: UnifiedPushStore,
// private val matrix: Matrix,
private val fcmHelper: FcmHelper,
private val stringProvider: StringProvider,
) {
@@ -95,11 +91,14 @@ class UnifiedPushHelper @Inject constructor(
// if we use the embedded distributor,
// register app_id type upfcm on sygnal
// the pushkey if FCM key
/*
if (UnifiedPush.getDistributor(context) == context.packageName) {
unifiedPushStore.storePushGateway(PushConfig.pusher_http_url)
onDoneRunnable?.run()
return
}
*/
/* TODO EAx UnifiedPush
// else, unifiedpush, and pushkey is an endpoint
val gateway = PushConfig.default_push_gateway_http_url
@@ -132,19 +131,25 @@ class UnifiedPushHelper @Inject constructor(
}
fun getCurrentDistributorName(): String {
TODO()
/*
return when {
isEmbeddedDistributor() -> stringProvider.getString(R.string.push_distributor_firebase_android)
isBackgroundSync() -> stringProvider.getString(R.string.push_distributor_background_sync_android)
else -> context.getApplicationLabel(UnifiedPush.getDistributor(context))
}
*/
}
fun isEmbeddedDistributor(): Boolean {
return isInternalDistributor() && fcmHelper.isFirebaseAvailable()
TODO()
//return isInternalDistributor() && fcmHelper.isFirebaseAvailable()
}
fun isBackgroundSync(): Boolean {
return isInternalDistributor() && !fcmHelper.isFirebaseAvailable()
TODO()
//return isInternalDistributor() && !fcmHelper.isFirebaseAvailable()
}
private fun isInternalDistributor(): Boolean {
@@ -168,12 +173,13 @@ class UnifiedPushHelper @Inject constructor(
}
fun getEndpointOrToken(): String? {
return if (isEmbeddedDistributor()) fcmHelper.getFcmToken()
// TODO
return if (isEmbeddedDistributor()) "" // fcmHelper.getFcmToken()
else unifiedPushStore.getEndpoint()
}
fun getPushGateway(): String? {
return if (isEmbeddedDistributor()) PushConfig.pusher_http_url
return if (isEmbeddedDistributor()) "" // PushConfig.pusher_http_url
else unifiedPushStore.getPushGateway()
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,10 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.unifiedpush
package io.element.android.libraries.push.providers.unifiedpush
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.push.impl.push.PushData
import io.element.android.libraries.push.providers.api.PushData
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import javax.inject.Inject

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.push.providers.api.PushProvider
import javax.inject.Inject
class UnifiedPushProvider @Inject constructor(): PushProvider {
override fun getDistributorNames(): List<String> {
TODO("Not yet implemented")
}
override suspend fun registerWith(matrixClient: MatrixClient, distributorName: String) {
TODO("Not yet implemented")
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl
package io.element.android.libraries.push.providers.unifiedpush
import android.content.Context
import android.content.SharedPreferences

View File

@@ -14,19 +14,9 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.unifiedpush
import android.content.Context
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.push.api.model.BackgroundSyncMode
import io.element.android.libraries.push.api.store.PushDataStore
import io.element.android.libraries.push.impl.PushersManager
import io.element.android.libraries.push.impl.UnifiedPushHelper
import io.element.android.libraries.push.impl.UnifiedPushStore
import org.unifiedpush.android.connector.UnifiedPush
import timber.log.Timber
import javax.inject.Inject
package io.element.android.libraries.push.providers.unifiedpush
/*
class UnregisterUnifiedPushUseCase @Inject constructor(
@ApplicationContext private val context: Context,
private val pushDataStore: PushDataStore,
@@ -50,3 +40,4 @@ class UnregisterUnifiedPushUseCase @Inject constructor(
UnifiedPush.unregisterApp(context)
}
}
*/

View File

@@ -14,40 +14,29 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.unifiedpush
package io.element.android.libraries.push.providers.unifiedpush
import android.content.Context
import android.content.Intent
import android.widget.Toast
import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.push.api.model.BackgroundSyncMode
import io.element.android.libraries.push.api.store.PushDataStore
import io.element.android.libraries.push.impl.PushersManager
import io.element.android.libraries.push.impl.UnifiedPushHelper
import io.element.android.libraries.push.impl.UnifiedPushStore
import io.element.android.libraries.push.impl.log.pushLoggerTag
import io.element.android.libraries.push.impl.push.PushHandler
import io.element.android.libraries.push.providers.api.PushHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.unifiedpush.android.connector.MessagingReceiver
import timber.log.Timber
import javax.inject.Inject
private val loggerTag = LoggerTag("Unified", pushLoggerTag)
private val loggerTag = LoggerTag("VectorUnifiedPushMessagingReceiver")
class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
@Inject lateinit var pushersManager: PushersManager
@Inject lateinit var pushParser: UnifiedPushParser
//@Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var pushDataStore: PushDataStore
// @Inject lateinit var pushDataStore: PushDataStore
@Inject lateinit var pushHandler: PushHandler
@Inject lateinit var guardServiceStarter: GuardServiceStarter
@Inject lateinit var unifiedPushStore: UnifiedPushStore
@Inject lateinit var unifiedPushHelper: UnifiedPushHelper
// @Inject lateinit var unifiedPushStore: UnifiedPushStore
// @Inject lateinit var unifiedPushHelper: UnifiedPushHelper
private val coroutineScope = CoroutineScope(SupervisorJob())
@@ -77,6 +66,8 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
}
override fun onNewEndpoint(context: Context, endpoint: String, instance: String) {
TODO()
/*
Timber.tag(loggerTag.value).i("onNewEndpoint: adding $endpoint")
if (pushDataStore.areNotificationEnabledForDevice() /* TODO EAx && activeSessionHolder.hasActiveSession() */) {
// If the endpoint has changed
@@ -99,16 +90,22 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_DISABLED
pushDataStore.setFdroidSyncBackgroundMode(mode)
guardServiceStarter.stop()
*/
}
override fun onRegistrationFailed(context: Context, instance: String) {
TODO()
/*
Toast.makeText(context, "Push service registration failed", Toast.LENGTH_SHORT).show()
val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME
pushDataStore.setFdroidSyncBackgroundMode(mode)
guardServiceStarter.start()
*/
}
override fun onUnregistered(context: Context, instance: String) {
TODO()
/*
Timber.tag(loggerTag.value).d("Unifiedpush: Unregistered")
val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME
pushDataStore.setFdroidSyncBackgroundMode(mode)
@@ -120,5 +117,6 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
Timber.tag(loggerTag.value).d("Probably unregistering a non existing pusher")
}
}
*/
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.unifiedpush
package io.element.android.libraries.push.providers.unifiedpush
import com.squareup.anvil.annotations.ContributesTo
import io.element.android.libraries.di.AppScope

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush.di
import com.squareup.anvil.annotations.ContributesTo
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.push.providers.api.PushProvider
import io.element.android.libraries.push.providers.unifiedpush.UnifiedPushProvider
@Module
@ContributesTo(AppScope::class)
interface UnifiedPushModule {
@Binds
@IntoSet
fun bind(pushProvider: UnifiedPushProvider): PushProvider
}

View File

@@ -14,12 +14,12 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.unifiedpush
package io.element.android.libraries.push.providers.unifiedpush
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.push.impl.push.PushData
import io.element.android.libraries.push.providers.api.PushData
import org.junit.Test
class UnifiedPushParserTest {

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id("io.element.android-library")
}
android {
namespace = "io.element.android.libraries.pushstore.api"
}
dependencies {
}

View File

@@ -14,10 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.userpushstore
const val NOTIFICATION_METHOD_FIREBASE = "NOTIFICATION_METHOD_FIREBASE"
const val NOTIFICATION_METHOD_UNIFIEDPUSH = "NOTIFICATION_METHOD_UNIFIEDPUSH"
package io.element.android.libraries.pushstore.api
/**
* Store data related to push about a user.
@@ -26,7 +23,7 @@ interface UserPushStore {
/**
* [NOTIFICATION_METHOD_FIREBASE] or [NOTIFICATION_METHOD_UNIFIEDPUSH].
*/
suspend fun getNotificationMethod(): String
suspend fun getNotificationMethod(): String?
suspend fun setNotificationMethod(value: String)
@@ -36,5 +33,3 @@ interface UserPushStore {
suspend fun reset()
}
suspend fun UserPushStore.isFirebase(): Boolean = getNotificationMethod() == NOTIFICATION_METHOD_FIREBASE

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.pushstore.api
/**
* Store data related to push about a user.
*/
interface UserPushStoreFactory {
fun create(userId: String): UserPushStore
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id("io.element.android-library")
alias(libs.plugins.anvil)
}
android {
namespace = "io.element.android.libraries.push.pushstore.impl"
}
anvil {
generateDaggerFactories.set(true)
}
dependencies {
implementation(libs.dagger)
implementation(projects.libraries.architecture)
implementation(projects.libraries.core)
implementation(projects.libraries.pushstore.api)
implementation(projects.libraries.sessionStorage.api)
implementation(libs.androidx.corektx)
implementation(libs.androidx.datastore.preferences)
}

View File

@@ -14,28 +14,32 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.userpushstore
package io.element.android.libraries.pushstore.impl
import android.content.Context
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.pushstore.api.UserPushStore
import io.element.android.libraries.pushstore.api.UserPushStoreFactory
import io.element.android.libraries.sessionstorage.api.observer.SessionListener
import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
import javax.inject.Inject
@SingleIn(AppScope::class)
class UserPushStoreFactory @Inject constructor(
@ContributesBinding(AppScope::class, boundType = UserPushStoreFactory::class)
class DefaultUserPushStoreFactory @Inject constructor(
@ApplicationContext private val context: Context,
private val sessionObserver: SessionObserver,
) : SessionListener {
) : UserPushStoreFactory, SessionListener {
init {
observeSessions()
}
// We can have only one class accessing a single data store, so keep a cache of them.
private val cache = mutableMapOf<String, UserPushStore>()
fun create(userId: String): UserPushStore {
override fun create(userId: String): UserPushStore {
return cache.getOrPut(userId) {
UserPushStoreDataStore(
context = context,

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.userpushstore
package io.element.android.libraries.pushstore.impl
import android.content.Context
import androidx.datastore.core.DataStore
@@ -22,6 +22,7 @@ import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import io.element.android.libraries.pushstore.api.UserPushStore
import kotlinx.coroutines.flow.first
/**
@@ -35,8 +36,8 @@ class UserPushStoreDataStore(
private val notificationMethod = stringPreferencesKey("notificationMethod")
private val currentPushKey = stringPreferencesKey("currentPushKey")
override suspend fun getNotificationMethod(): String {
return context.dataStore.data.first()[notificationMethod] ?: NOTIFICATION_METHOD_FIREBASE
override suspend fun getNotificationMethod(): String? {
return context.dataStore.data.first()[notificationMethod]
}
override suspend fun setNotificationMethod(value: String) {

View File

@@ -75,6 +75,12 @@ fun DependencyHandlerScope.allLibrariesImpl() {
implementation(project(":libraries:core"))
implementation(project(":libraries:permissions:impl"))
implementation(project(":libraries:push:impl"))
implementation(project(":libraries:push:impl"))
// Comment to not include firebase in the project
implementation(project(":libraries:pushproviders:firebase"))
// Comment to not include unified push in the project
// implementation(project(":libraries:pushproviders:unifiedpush"))
implementation(project(":libraries:pushstore:impl"))
implementation(project(":libraries:architecture"))
implementation(project(":libraries:dateformatter:impl"))
implementation(project(":libraries:di"))