Browse Source

UnifiedPush WIP

test/jme/compound-poc
Benoit Marty 1 year ago committed by Benoit Marty
parent
commit
20370656cd
  1. 10
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt
  2. 4
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt
  3. 2
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt
  4. 2
      libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/push/providers/api/PushProvider.kt
  5. 20
      libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/push/providers/firebase/FirebaseNewTokenHandler.kt
  6. 4
      libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/push/providers/firebase/FirebasePushProvider.kt
  7. 4
      libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/push/providers/unifiedpush/RegisterUnifiedPushUseCase.kt
  8. 56
      libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/push/providers/unifiedpush/UnifiedPushGatewayResolver.kt
  9. 89
      libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/push/providers/unifiedpush/UnifiedPushHelper.kt
  10. 27
      libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/push/providers/unifiedpush/UnifiedPushNewGatewayHandler.kt
  11. 8
      libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/push/providers/unifiedpush/UnifiedPushProvider.kt
  12. 19
      libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/push/providers/unifiedpush/UnifiedPushStore.kt
  13. 10
      libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/push/providers/unifiedpush/UnregisterUnifiedPushUseCase.kt
  14. 20
      libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/push/providers/unifiedpush/VectorUnifiedPushMessagingReceiver.kt
  15. 1
      libraries/pushstore/api/build.gradle.kts
  16. 4
      libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/UserPushStoreFactory.kt
  17. 2
      libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecret.kt
  18. 2
      libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecretFactory.kt
  19. 2
      libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecretStore.kt
  20. 9
      libraries/pushstore/impl/build.gradle.kts
  21. 8
      libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactory.kt
  22. 3
      libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/UserPushStoreDataStore.kt
  23. 3
      libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretFactoryImpl.kt
  24. 5
      libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretImpl.kt
  25. 3
      libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretStoreDataStore.kt
  26. 4
      libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/FakePushClientSecretFactory.kt
  27. 3
      libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/InMemoryPushClientSecretStore.kt
  28. 2
      libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretImplTest.kt

10
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt

@ -20,7 +20,7 @@ import com.squareup.anvil.annotations.ContributesBinding @@ -20,7 +20,7 @@ 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.clientsecret.PushClientSecret
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
import io.element.android.libraries.push.impl.notifications.NotificationDrawerManager
import io.element.android.libraries.push.providers.api.Distributor
import io.element.android.libraries.push.providers.api.PushProvider
@ -31,7 +31,6 @@ import javax.inject.Inject @@ -31,7 +31,6 @@ import javax.inject.Inject
class DefaultPushService @Inject constructor(
private val notificationDrawerManager: NotificationDrawerManager,
private val pushersManager: PushersManager,
private val pushClientSecret: PushClientSecret,
private val userPushStoreFactory: UserPushStoreFactory,
private val pushProviders: Set<@JvmSuppressWildcards PushProvider>,
) : PushService {
@ -47,16 +46,13 @@ class DefaultPushService @Inject constructor( @@ -47,16 +46,13 @@ class DefaultPushService @Inject constructor(
* Get current push provider, compare with provided one, then unregister and register if different, and store change
*/
override suspend fun registerWith(matrixClient: MatrixClient, pushProvider: PushProvider, distributor: Distributor) {
val userPushStore = userPushStoreFactory.create(matrixClient.sessionId.value)
val userPushStore = userPushStoreFactory.create(matrixClient.sessionId)
val currentPushProviderName = userPushStore.getPushProviderName()
if (currentPushProviderName != pushProvider.name) {
// Unregister previous one if any
pushProviders.find { it.name == currentPushProviderName }?.unregister(matrixClient)
}
val clientSecret = pushClientSecret.getSecretForUser(matrixClient.sessionId)
pushProvider.registerWith(matrixClient, distributor, clientSecret)
pushProvider.registerWith(matrixClient, distributor)
// Store new value
userPushStore.setPushProviderName(pushProvider.name)
}

4
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt

@ -24,7 +24,7 @@ import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService @@ -24,7 +24,7 @@ import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.pusher.SetHttpPusherData
import io.element.android.libraries.push.impl.clientsecret.PushClientSecret
import io.element.android.libraries.pushstore.api.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
@ -79,7 +79,7 @@ class PushersManager @Inject constructor( @@ -79,7 +79,7 @@ class PushersManager @Inject constructor(
* Register a pusher to the server if not done yet.
*/
override suspend fun registerPusher(matrixClient: MatrixClient, pushKey: String, gateway: String) {
val userDataStore = userPushStoreFactory.create(matrixClient.sessionId.value)
val userDataStore = userPushStoreFactory.create(matrixClient.sessionId)
if (userDataStore.getCurrentRegisteredPushKey() == pushKey) {
Timber.tag(loggerTag.value).d("Unnecessary to register again the same pusher")
} else {

2
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt

@ -30,7 +30,7 @@ import io.element.android.libraries.di.ApplicationContext @@ -30,7 +30,7 @@ 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
import io.element.android.libraries.push.impl.PushersManager
import io.element.android.libraries.push.impl.clientsecret.PushClientSecret
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
import io.element.android.libraries.push.impl.log.pushLoggerTag
import io.element.android.libraries.push.impl.notifications.NotifiableEventResolver
import io.element.android.libraries.push.impl.notifications.NotificationActionIds

2
libraries/pushproviders/api/src/main/kotlin/io/element/android/libraries/push/providers/api/PushProvider.kt

@ -37,7 +37,7 @@ interface PushProvider { @@ -37,7 +37,7 @@ interface PushProvider {
/**
* Register the pusher to the homeserver
*/
suspend fun registerWith(matrixClient: MatrixClient, distributor: Distributor, clientSecret: String)
suspend fun registerWith(matrixClient: MatrixClient, distributor: Distributor)
/**
* Unregister the pusher

20
libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/push/providers/firebase/FirebaseNewTokenHandler.kt

@ -18,7 +18,7 @@ package io.element.android.libraries.push.providers.firebase @@ -18,7 +18,7 @@ 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.matrix.api.core.asSessionId
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
@ -41,15 +41,17 @@ class FirebaseNewTokenHandler @Inject constructor( @@ -41,15 +41,17 @@ class FirebaseNewTokenHandler @Inject constructor(
suspend fun handle(firebaseToken: String) {
firebaseStore.storeFcmToken(firebaseToken)
// Register the pusher for all the sessions
sessionStore.getAllSessions().toUserList().forEach { userId ->
val userDataStore = userPushStoreFactory.create(userId)
if (userDataStore.getPushProviderName() == FirebaseConfig.name) {
matrixAuthenticationService.restoreSession(SessionId(userId)).getOrNull()?.use { client ->
pusherSubscriber.registerPusher(client, firebaseToken, FirebaseConfig.pusher_http_url)
sessionStore.getAllSessions().toUserList()
.map { it.asSessionId() }
.forEach { userId ->
val userDataStore = userPushStoreFactory.create(userId)
if (userDataStore.getPushProviderName() == FirebaseConfig.name) {
matrixAuthenticationService.restoreSession(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")
}
} else {
Timber.tag(loggerTag.value).d("This session is not using Firebase pusher")
}
}
}
}

4
libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/push/providers/firebase/FirebasePushProvider.kt

@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.MatrixClient @@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.push.providers.api.Distributor
import io.element.android.libraries.push.providers.api.PushProvider
import io.element.android.libraries.push.providers.api.PusherSubscriber
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
import timber.log.Timber
import javax.inject.Inject
@ -30,6 +31,7 @@ class FirebasePushProvider @Inject constructor( @@ -30,6 +31,7 @@ class FirebasePushProvider @Inject constructor(
private val firebaseStore: FirebaseStore,
private val firebaseTroubleshooter: FirebaseTroubleshooter,
private val pusherSubscriber: PusherSubscriber,
private val pushClientSecret: PushClientSecret,
) : PushProvider {
override val index = FirebaseConfig.index
override val name = FirebaseConfig.name
@ -38,7 +40,7 @@ class FirebasePushProvider @Inject constructor( @@ -38,7 +40,7 @@ class FirebasePushProvider @Inject constructor(
return listOf(Distributor("Firebase", "Firebase"))
}
override suspend fun registerWith(matrixClient: MatrixClient, distributor: Distributor, clientSecret: String) {
override suspend fun registerWith(matrixClient: MatrixClient, distributor: Distributor) {
val pushKey = firebaseStore.getFcmToken() ?: return Unit.also {
Timber.tag(loggerTag.value).w("Unable to register pusher, Firebase token is not known.")
}

4
libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/push/providers/unifiedpush/RegisterUnifiedPushUseCase.kt

@ -40,8 +40,8 @@ class RegisterUnifiedPushUseCase @Inject constructor( @@ -40,8 +40,8 @@ class RegisterUnifiedPushUseCase @Inject constructor(
val distributorValue = distributor.value
if (distributorValue.isNotEmpty()) {
saveAndRegisterApp(distributorValue, clientSecret)
val endpoint = unifiedPushStore.getEndpoint() ?: return RegisterUnifiedPushResult.Error
val gateway = unifiedPushStore.getPushGateway() ?: return RegisterUnifiedPushResult.Error
val endpoint = unifiedPushStore.getEndpoint(clientSecret) ?: return RegisterUnifiedPushResult.Error
val gateway = unifiedPushStore.getPushGateway(clientSecret) ?: return RegisterUnifiedPushResult.Error
pusherSubscriber.registerPusher(matrixClient, endpoint, gateway)
return RegisterUnifiedPushResult.Success
}

56
libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/push/providers/unifiedpush/UnifiedPushGatewayResolver.kt

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
/*
* 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.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.network.RetrofitFactory
import io.element.android.libraries.push.providers.unifiedpush.network.UnifiedPushApi
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.net.URL
import javax.inject.Inject
class UnifiedPushGatewayResolver @Inject constructor(
private val retrofitFactory: RetrofitFactory,
private val coroutineDispatchers: CoroutineDispatchers,
) {
suspend fun getGateway(endpoint: String): String? {
val gateway = UnifiedPushConfig.default_push_gateway_http_url
val url = URL(endpoint)
val custom = "${url.protocol}://${url.host}/_matrix/push/v1/notify"
Timber.i("Testing $custom")
try {
return withContext(coroutineDispatchers.io) {
val api = retrofitFactory.create("${url.protocol}://${url.host}")
.create(UnifiedPushApi::class.java)
try {
val discoveryResponse = api.discover()
if (discoveryResponse.unifiedpush.gateway == "matrix") {
Timber.d("Using custom gateway")
return@withContext custom
}
} catch (throwable: Throwable) {
Timber.tag("UnifiedPushHelper").e(throwable)
}
return@withContext gateway
}
} catch (e: Throwable) {
Timber.d(e, "Cannot try custom gateway")
}
return gateway
}
}

89
libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/push/providers/unifiedpush/UnifiedPushHelper.kt

@ -1,89 +0,0 @@ @@ -1,89 +0,0 @@
/*
* 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 android.content.Context
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.network.RetrofitFactory
import io.element.android.libraries.push.providers.unifiedpush.network.UnifiedPushApi
import io.element.android.services.toolbox.api.strings.StringProvider
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.net.URL
import javax.inject.Inject
class UnifiedPushHelper @Inject constructor(
@ApplicationContext private val context: Context,
private val unifiedPushStore: UnifiedPushStore,
private val stringProvider: StringProvider,
private val retrofitFactory: RetrofitFactory,
private val coroutineDispatchers: CoroutineDispatchers,
) {
suspend fun storeCustomOrDefaultGateway(endpoint: String) {
val gateway = UnifiedPushConfig.default_push_gateway_http_url
val parsed = URL(endpoint)
val custom = "${parsed.protocol}://${parsed.host}/_matrix/push/v1/notify"
Timber.i("Testing $custom")
try {
withContext(coroutineDispatchers.io) {
val api = retrofitFactory.create("${parsed.protocol}://${parsed.host}")
.create(UnifiedPushApi::class.java)
tryOrNull { api.discover() }
?.let { discoveryResponse ->
if (discoveryResponse.unifiedpush.gateway == "matrix") {
Timber.d("Using custom gateway")
unifiedPushStore.storePushGateway(custom)
}
}
}
return
} catch (e: Throwable) {
Timber.d(e, "Cannot try custom gateway")
}
unifiedPushStore.storePushGateway(gateway)
}
private fun isEmbeddedDistributor() = false
fun getPrivacyFriendlyUpEndpoint(): String? {
val endpoint = getEndpointOrToken()
if (endpoint.isNullOrEmpty()) return null
if (isEmbeddedDistributor()) {
return endpoint
}
return try {
val parsed = URL(endpoint)
"${parsed.protocol}://${parsed.host}/***"
} catch (e: Exception) {
Timber.e(e, "Error parsing unifiedpush endpoint")
null
}
}
fun getEndpointOrToken(): String? {
// TODO
return if (isEmbeddedDistributor()) "" // fcmHelper.getFcmToken()
else unifiedPushStore.getEndpoint()
}
fun getPushGateway(): String? {
return if (isEmbeddedDistributor()) "" // PushConfig.pusher_http_url
else unifiedPushStore.getPushGateway()
}
}

27
libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/push/providers/unifiedpush/UnifiedPushNewGatewayHandler.kt

@ -18,11 +18,9 @@ package io.element.android.libraries.push.providers.unifiedpush @@ -18,11 +18,9 @@ package io.element.android.libraries.push.providers.unifiedpush
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 io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
import timber.log.Timber
import javax.inject.Inject
@ -33,21 +31,22 @@ private val loggerTag = LoggerTag("UnifiedPushNewGatewayHandler") @@ -33,21 +31,22 @@ private val loggerTag = LoggerTag("UnifiedPushNewGatewayHandler")
*/
class UnifiedPushNewGatewayHandler @Inject constructor(
private val pusherSubscriber: PusherSubscriber,
private val sessionStore: SessionStore,
private val userPushStoreFactory: UserPushStoreFactory,
private val pushClientSecret: PushClientSecret,
private val matrixAuthenticationService: MatrixAuthenticationService,
) {
suspend fun handle(endpoint: String, pushGateway: String) {
// Register the pusher for all the sessions which are using UnifiedPush.
sessionStore.getAllSessions().toUserList().forEach { userId ->
val userDataStore = userPushStoreFactory.create(userId)
if (userDataStore.getPushProviderName() == UnifiedPushConfig.name) {
matrixAuthenticationService.restoreSession(SessionId(userId)).getOrNull()?.use { client ->
pusherSubscriber.registerPusher(client, endpoint, pushGateway)
}
} else {
Timber.tag(loggerTag.value).d("This session is not using UnifiedPush pusher")
suspend fun handle(endpoint: String, pushGateway: String, clientSecret: String) {
// Register the pusher for the session with this client secret, if is it using UnifiedPush.
val userId = pushClientSecret.getUserIdFromSecret(clientSecret) ?: return Unit.also {
Timber.w("Unable to retrieve session")
}
val userDataStore = userPushStoreFactory.create(userId)
if (userDataStore.getPushProviderName() == UnifiedPushConfig.name) {
matrixAuthenticationService.restoreSession(userId).getOrNull()?.use { client ->
pusherSubscriber.registerPusher(client, endpoint, pushGateway)
}
} else {
Timber.tag(loggerTag.value).d("This session is not using UnifiedPush pusher")
}
}
}

8
libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/push/providers/unifiedpush/UnifiedPushProvider.kt

@ -22,6 +22,7 @@ import io.element.android.libraries.di.ApplicationContext @@ -22,6 +22,7 @@ import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.push.providers.api.Distributor
import io.element.android.libraries.push.providers.api.PushProvider
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
import org.unifiedpush.android.connector.UnifiedPush
import javax.inject.Inject
@ -29,6 +30,7 @@ class UnifiedPushProvider @Inject constructor( @@ -29,6 +30,7 @@ class UnifiedPushProvider @Inject constructor(
@ApplicationContext private val context: Context,
private val registerUnifiedPushUseCase: RegisterUnifiedPushUseCase,
private val unRegisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase,
private val pushClientSecret: PushClientSecret,
) : PushProvider {
override val index = UnifiedPushConfig.index
override val name = UnifiedPushConfig.name
@ -45,12 +47,14 @@ class UnifiedPushProvider @Inject constructor( @@ -45,12 +47,14 @@ class UnifiedPushProvider @Inject constructor(
}
}
override suspend fun registerWith(matrixClient: MatrixClient, distributor: Distributor, clientSecret: String) {
override suspend fun registerWith(matrixClient: MatrixClient, distributor: Distributor) {
val clientSecret = pushClientSecret.getSecretForUser(matrixClient.sessionId)
registerUnifiedPushUseCase.execute(matrixClient, distributor, clientSecret)
}
override suspend fun unregister(matrixClient: MatrixClient) {
unRegisterUnifiedPushUseCase.execute()
val clientSecret = pushClientSecret.getSecretForUser(matrixClient.sessionId)
unRegisterUnifiedPushUseCase.execute(clientSecret)
}
override suspend fun troubleshoot(): Result<Unit> {

19
libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/push/providers/unifiedpush/UnifiedPushStore.kt

@ -23,9 +23,6 @@ import io.element.android.libraries.di.ApplicationContext @@ -23,9 +23,6 @@ import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.DefaultPreferences
import javax.inject.Inject
/**
* TODO EAx Store in BDD (for multisession).
*/
class UnifiedPushStore @Inject constructor(
@ApplicationContext val context: Context,
@DefaultPreferences private val defaultPrefs: SharedPreferences,
@ -35,8 +32,8 @@ class UnifiedPushStore @Inject constructor( @@ -35,8 +32,8 @@ class UnifiedPushStore @Inject constructor(
*
* @return the UnifiedPush Endpoint or null if not received
*/
fun getEndpoint(): String? {
return defaultPrefs.getString(PREFS_ENDPOINT_OR_TOKEN, null)
fun getEndpoint(clientSecret: String): String? {
return defaultPrefs.getString(PREFS_ENDPOINT_OR_TOKEN + clientSecret, null)
}
/**
@ -44,9 +41,9 @@ class UnifiedPushStore @Inject constructor( @@ -44,9 +41,9 @@ class UnifiedPushStore @Inject constructor(
*
* @param endpoint the endpoint to store
*/
fun storeUpEndpoint(endpoint: String?) {
fun storeUpEndpoint(endpoint: String?, clientSecret: String) {
defaultPrefs.edit {
putString(PREFS_ENDPOINT_OR_TOKEN, endpoint)
putString(PREFS_ENDPOINT_OR_TOKEN + clientSecret, endpoint)
}
}
@ -55,8 +52,8 @@ class UnifiedPushStore @Inject constructor( @@ -55,8 +52,8 @@ class UnifiedPushStore @Inject constructor(
*
* @return the Push Gateway or null if not defined
*/
fun getPushGateway(): String? {
return defaultPrefs.getString(PREFS_PUSH_GATEWAY, null)
fun getPushGateway(clientSecret: String): String? {
return defaultPrefs.getString(PREFS_PUSH_GATEWAY + clientSecret, null)
}
/**
@ -64,9 +61,9 @@ class UnifiedPushStore @Inject constructor( @@ -64,9 +61,9 @@ class UnifiedPushStore @Inject constructor(
*
* @param gateway the push gateway to store
*/
fun storePushGateway(gateway: String?) {
fun storePushGateway(gateway: String?, clientSecret: String) {
defaultPrefs.edit {
putString(PREFS_PUSH_GATEWAY, gateway)
putString(PREFS_PUSH_GATEWAY + clientSecret, gateway)
}
}

10
libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/push/providers/unifiedpush/UnregisterUnifiedPushUseCase.kt

@ -26,22 +26,22 @@ class UnregisterUnifiedPushUseCase @Inject constructor( @@ -26,22 +26,22 @@ class UnregisterUnifiedPushUseCase @Inject constructor(
@ApplicationContext private val context: Context,
//private val pushDataStore: PushDataStore,
private val unifiedPushStore: UnifiedPushStore,
private val unifiedPushHelper: UnifiedPushHelper,
private val unifiedPushGatewayResolver: UnifiedPushGatewayResolver,
) {
suspend fun execute(/*pushersManager: PushersManager?*/) {
suspend fun execute(clientSecret: String /*pushersManager: PushersManager?*/) {
//val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME
//pushDataStore.setFdroidSyncBackgroundMode(mode)
try {
unifiedPushHelper.getEndpointOrToken()?.let {
unifiedPushStore.getEndpoint(clientSecret)?.let {
Timber.d("Removing $it")
// TODO pushersManager?.unregisterPusher(it)
}
} catch (e: Exception) {
Timber.d(e, "Probably unregistering a non existing pusher")
}
unifiedPushStore.storeUpEndpoint(null)
unifiedPushStore.storePushGateway(null)
unifiedPushStore.storeUpEndpoint(null, clientSecret)
unifiedPushStore.storePushGateway(null, clientSecret)
UnifiedPush.unregisterApp(context)
}
}

20
libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/push/providers/unifiedpush/VectorUnifiedPushMessagingReceiver.kt

@ -21,7 +21,6 @@ import android.content.Intent @@ -21,7 +21,6 @@ import android.content.Intent
import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.push.providers.api.PushHandler
import io.element.android.libraries.push.providers.api.PusherSubscriber
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
@ -33,13 +32,10 @@ private val loggerTag = LoggerTag("VectorUnifiedPushMessagingReceiver") @@ -33,13 +32,10 @@ private val loggerTag = LoggerTag("VectorUnifiedPushMessagingReceiver")
class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
@Inject lateinit var pushParser: UnifiedPushParser
// @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 pusherSubscriber: PusherSubscriber
@Inject lateinit var unifiedPushGatewayResolver: UnifiedPushGatewayResolver
@Inject lateinit var newGatewayHandler: UnifiedPushNewGatewayHandler
private val coroutineScope = CoroutineScope(SupervisorJob())
@ -71,25 +67,23 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() { @@ -71,25 +67,23 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
/**
* Called when a new endpoint is to be used for sending push messages.
* You should send the endpoint to your application server and sync for missing notifications.
* TODO use [instance] for multi-account
*/
override fun onNewEndpoint(context: Context, endpoint: String, instance: String) {
Timber.tag(loggerTag.value).i("onNewEndpoint: adding $endpoint")
// If the endpoint has changed
// or the gateway has changed
if (unifiedPushHelper.getEndpointOrToken() != endpoint) {
unifiedPushStore.storeUpEndpoint(endpoint)
if (unifiedPushStore.getEndpoint(instance) != endpoint) {
unifiedPushStore.storeUpEndpoint(endpoint, instance)
coroutineScope.launch {
unifiedPushHelper.storeCustomOrDefaultGateway(endpoint)
unifiedPushHelper.getPushGateway()?.let { pushGateway ->
newGatewayHandler.handle(endpoint, pushGateway)
val gateway = unifiedPushGatewayResolver.getGateway(endpoint)
unifiedPushStore.storePushGateway(gateway, instance)
gateway?.let { pushGateway ->
newGatewayHandler.handle(endpoint, pushGateway, instance)
}
}
} else {
Timber.tag(loggerTag.value).i("onNewEndpoint: skipped")
}
//val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_DISABLED
//pushDataStore.setFdroidSyncBackgroundMode(mode)
guardServiceStarter.stop()
}

1
libraries/pushstore/api/build.gradle.kts

@ -23,4 +23,5 @@ android { @@ -23,4 +23,5 @@ android {
}
dependencies {
implementation(projects.libraries.matrix.api)
}

4
libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/UserPushStoreFactory.kt

@ -16,9 +16,11 @@ @@ -16,9 +16,11 @@
package io.element.android.libraries.pushstore.api
import io.element.android.libraries.matrix.api.core.SessionId
/**
* Store data related to push about a user.
*/
interface UserPushStoreFactory {
fun create(userId: String): UserPushStore
fun create(userId: SessionId): UserPushStore
}

2
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/clientsecret/PushClientSecret.kt → libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecret.kt

@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.clientsecret
package io.element.android.libraries.pushstore.api.clientsecret
import io.element.android.libraries.matrix.api.core.SessionId

2
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/clientsecret/PushClientSecretFactory.kt → libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecretFactory.kt

@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.clientsecret
package io.element.android.libraries.pushstore.api.clientsecret
interface PushClientSecretFactory {
fun create(): String

2
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/clientsecret/PushClientSecretStore.kt → libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/clientsecret/PushClientSecretStore.kt

@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.clientsecret
package io.element.android.libraries.pushstore.api.clientsecret
import io.element.android.libraries.matrix.api.core.SessionId

9
libraries/pushstore/impl/build.gradle.kts

@ -31,8 +31,17 @@ dependencies { @@ -31,8 +31,17 @@ dependencies {
implementation(libs.dagger)
implementation(projects.libraries.architecture)
implementation(projects.libraries.core)
implementation(projects.libraries.matrix.api)
implementation(projects.libraries.pushstore.api)
implementation(projects.libraries.sessionStorage.api)
implementation(libs.androidx.corektx)
implementation(libs.androidx.datastore.preferences)
testImplementation(libs.test.junit)
testImplementation(libs.test.mockk)
testImplementation(libs.test.truth)
testImplementation(libs.test.turbine)
testImplementation(libs.coroutines.test)
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.services.appnavstate.test)
}

8
libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactory.kt

@ -21,6 +21,8 @@ import com.squareup.anvil.annotations.ContributesBinding @@ -21,6 +21,8 @@ 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.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.asSessionId
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
@ -38,8 +40,8 @@ class DefaultUserPushStoreFactory @Inject constructor( @@ -38,8 +40,8 @@ class DefaultUserPushStoreFactory @Inject constructor(
}
// We can have only one class accessing a single data store, so keep a cache of them.
private val cache = mutableMapOf<String, UserPushStore>()
override fun create(userId: String): UserPushStore {
private val cache = mutableMapOf<SessionId, UserPushStore>()
override fun create(userId: SessionId): UserPushStore {
return cache.getOrPut(userId) {
UserPushStoreDataStore(
context = context,
@ -58,6 +60,6 @@ class DefaultUserPushStoreFactory @Inject constructor( @@ -58,6 +60,6 @@ class DefaultUserPushStoreFactory @Inject constructor(
override suspend fun onSessionDeleted(userId: String) {
// Delete the store
create(userId).reset()
create(userId.asSessionId()).reset()
}
}

3
libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/UserPushStoreDataStore.kt

@ -22,6 +22,7 @@ import androidx.datastore.preferences.core.Preferences @@ -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.matrix.api.core.SessionId
import io.element.android.libraries.pushstore.api.UserPushStore
import kotlinx.coroutines.flow.first
@ -30,7 +31,7 @@ import kotlinx.coroutines.flow.first @@ -30,7 +31,7 @@ import kotlinx.coroutines.flow.first
*/
class UserPushStoreDataStore(
private val context: Context,
userId: String,
userId: SessionId,
) : UserPushStore {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "push_store_$userId")
private val pushProviderName = stringPreferencesKey("pushProviderName")

3
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/clientsecret/PushClientSecretFactoryImpl.kt → libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretFactoryImpl.kt

@ -14,10 +14,11 @@ @@ -14,10 +14,11 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.clientsecret
package io.element.android.libraries.pushstore.impl.clientsecret
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecretFactory
import java.util.UUID
import javax.inject.Inject

5
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/clientsecret/PushClientSecretImpl.kt → libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretImpl.kt

@ -14,11 +14,14 @@ @@ -14,11 +14,14 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.clientsecret
package io.element.android.libraries.pushstore.impl.clientsecret
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecretFactory
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecretStore
import javax.inject.Inject
@ContributesBinding(AppScope::class)

3
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/clientsecret/PushClientSecretStoreDataStore.kt → libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretStoreDataStore.kt

@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.clientsecret
package io.element.android.libraries.pushstore.impl.clientsecret
import android.content.Context
import androidx.datastore.core.DataStore
@ -27,6 +27,7 @@ import io.element.android.libraries.di.AppScope @@ -27,6 +27,7 @@ import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.asSessionId
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecretStore
import kotlinx.coroutines.flow.first
import javax.inject.Inject

4
libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/clientsecret/FakePushClientSecretFactory.kt → libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/FakePushClientSecretFactory.kt

@ -14,7 +14,9 @@ @@ -14,7 +14,9 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.clientsecret
package io.element.android.libraries.pushstore.impl.clientsecret
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecretFactory
private const val A_SECRET_PREFIX = "A_SECRET_"

3
libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/clientsecret/InMemoryPushClientSecretStore.kt → libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/InMemoryPushClientSecretStore.kt

@ -14,9 +14,10 @@ @@ -14,9 +14,10 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.clientsecret
package io.element.android.libraries.pushstore.impl.clientsecret
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecretStore
class InMemoryPushClientSecretStore : PushClientSecretStore {
private val secrets = mutableMapOf<SessionId, String>()

2
libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/clientsecret/PushClientSecretImplTest.kt → libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretImplTest.kt

@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
@file:OptIn(ExperimentalCoroutinesApi::class)
package io.element.android.libraries.push.impl.clientsecret
package io.element.android.libraries.pushstore.impl.clientsecret
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.core.SessionId
Loading…
Cancel
Save