Cleanup + Add per user store.

This commit is contained in:
Benoit Marty
2023-03-31 15:55:14 +02:00
parent 961d0ecdbc
commit b9276aa60b
29 changed files with 351 additions and 202 deletions

View File

@@ -46,7 +46,7 @@ class LoggedInPresenter @Inject constructor(
override fun present(): LoggedInState {
LaunchedEffect(Unit) {
// Ensure pusher is registered
pushService.registerPusher(matrixClient)
pushService.registerFirebasePusher(matrixClient)
}
val permissionsState = postNotificationPermissionsPresenter.present()

View File

@@ -28,7 +28,7 @@ interface PushService {
fun notificationStyleChanged()
// Ensure pusher is registered
suspend fun registerPusher(matrixClient: MatrixClient)
suspend fun registerFirebasePusher(matrixClient: MatrixClient)
suspend fun testPush()
}

View File

@@ -26,7 +26,7 @@
android:value="true" />
<service
android:name="VectorFirebaseMessagingService"
android:name=".firebase.VectorFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
@@ -35,7 +35,7 @@
<!-- UnifiedPush -->
<receiver
android:name="VectorUnifiedPushMessagingReceiver"
android:name=".unifiedpush.VectorUnifiedPushMessagingReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
@@ -48,7 +48,7 @@
</receiver>
<receiver
android:name="KeepInternalDistributor"
android:name=".unifiedpush.KeepInternalDistributor"
android:enabled="true"
android:exported="false">
<intent-filter>

View File

@@ -1,49 +0,0 @@
/*
* Copyright (c) 2021 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
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope
import javax.inject.Inject
// TODO Move away
/**
* This interface defines 2 flags so you can handle auto accept invites.
* At the moment we only have [CompileTimeAutoAcceptInvites] implementation.
*/
interface AutoAcceptInvites {
/**
* Enable auto-accept invites. It means, as soon as you got an invite from the sync, it will try to join it.
*/
val isEnabled: Boolean
/**
* Hide invites from the UI (from notifications, notification count and room list). By default invites are hidden when [isEnabled] is true
*/
val hideInvites: Boolean
get() = isEnabled
}
fun AutoAcceptInvites.showInvites() = !hideInvites
/**
* Simple compile time implementation of AutoAcceptInvites flags.
*/
@ContributesBinding(AppScope::class)
class CompileTimeAutoAcceptInvites @Inject constructor() : AutoAcceptInvites {
override val isEnabled = false
}

View File

@@ -20,13 +20,17 @@ 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 javax.inject.Inject
@ContributesBinding(AppScope::class)
class DefaultPushService @Inject constructor(
private val notificationDrawerManager: NotificationDrawerManager,
private val pusherManager: PushersManager,
private val pushersManager: PushersManager,
private val fcmHelper: FcmHelper,
) : PushService {
override fun setCurrentRoom(roomId: String?) {
notificationDrawerManager.setCurrentRoom(roomId)
@@ -40,11 +44,14 @@ class DefaultPushService @Inject constructor(
notificationDrawerManager.notificationStyleChanged()
}
override suspend fun registerPusher(matrixClient: MatrixClient) {
pusherManager.registerPusher(matrixClient)
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 suspend fun testPush() {
pusherManager.testPush()
pushersManager.testPush()
}
}

View File

@@ -23,8 +23,12 @@ import io.element.android.libraries.matrix.api.pusher.SetHttpPusherData
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.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.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
internal const val DEFAULT_PUSHER_FILE_TAG = "mobile"
@@ -40,6 +44,7 @@ class PushersManager @Inject constructor(
private val pushClientSecret: PushClientSecret,
private val sessionStore: SessionStore,
private val matrixAuthenticationService: MatrixAuthenticationService,
private val userPushStoreFactory: UserPushStoreFactory,
private val fcmHelper: FcmHelper,
) {
suspend fun testPush() {
@@ -54,29 +59,54 @@ class PushersManager @Inject constructor(
}
suspend fun enqueueRegisterPusherWithFcmKey(pushKey: String) {
return enqueueRegisterPusher(pushKey, PushConfig.pusher_http_url)
// return onNewFirebaseToken(pushKey, PushConfig.pusher_http_url)
TODO()
}
// TODO Rename
suspend fun enqueueRegisterPusher(
suspend fun onNewUnifiedPushEndpoint(
pushKey: String,
gateway: String
) {
TODO()
}
suspend fun onNewFirebaseToken(firebaseToken: String) {
fcmHelper.storeFcmToken(firebaseToken)
// Register the pusher for all the sessions
sessionStore.getAllSessions().forEach { sessionData ->
val client = matrixAuthenticationService.restoreSession(SessionId(sessionData.userId)).getOrNull()
client ?: return@forEach
client.pushersService().setHttpPusher(createHttpPusher(pushKey, gateway, sessionData.userId))
// TODO EAx Close sessions
sessionStore.getAllSessions().toUserList().forEach { userId ->
val userDataStore = userPushStoreFactory.create(userId)
if (userDataStore.isFirebase()) {
val client = matrixAuthenticationService.restoreSession(SessionId(userId)).getOrNull()
client ?: return@forEach
registerPusher(client, firebaseToken, PushConfig.pusher_http_url)
// TODO EAx Close sessions
} else {
Timber.d("This session is not using Firebase pusher")
}
}
}
suspend fun registerPusher(matrixClient: MatrixClient) {
val pushKey = fcmHelper.getFcmToken() ?: return
// Register the pusher for the session
matrixClient.pushersService().setHttpPusher(
createHttpPusher(pushKey, PushConfig.pusher_http_url, matrixClient.sessionId.value)
)
/**
* Register a pusher to the server if not done yet.
*/
suspend fun registerPusher(matrixClient: MatrixClient, pushKey: String, gateway: String) {
val userDataStore = userPushStoreFactory.create(matrixClient.sessionId.value)
if (userDataStore.getCurrentRegisteredPushKey() == pushKey) {
Timber.d("Unnecessary to register again the same pusher")
} else {
// Register the pusher to the server
matrixClient.pushersService().setHttpPusher(
createHttpPusher(pushKey, gateway, matrixClient.sessionId.value)
).fold(
{
userDataStore.setCurrentRegisteredPushKey(pushKey)
},
{ throwable ->
Timber.e(throwable, "Unable to register the pusher")
}
)
}
}
private suspend fun createHttpPusher(

View File

@@ -27,7 +27,6 @@ import org.unifiedpush.android.connector.UnifiedPush
import timber.log.Timber
import java.net.URL
import javax.inject.Inject
import io.element.android.libraries.ui.strings.R as StringR
class UnifiedPushHelper @Inject constructor(
@ApplicationContext private val context: Context,

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,13 +14,16 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl
package io.element.android.libraries.push.impl.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
class EnsureFcmTokenIsRetrievedUseCase @Inject constructor(
private val unifiedPushHelper: UnifiedPushHelper,
private val fcmHelper: FcmHelper,
private val unifiedPushHelper: UnifiedPushHelper,
private val fcmHelper: FcmHelper,
// private val activeSessionHolder: ActiveSessionHolder,
) {

View File

@@ -0,0 +1,33 @@
/*
* 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.firebase
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.push.impl.push.PushData
import javax.inject.Inject
class FirebasePushParser @Inject constructor() {
fun parse(message: Map<String, String?>): PushData {
val pushDataFirebase = PushDataFirebase(
eventId = message["event_id"],
roomId = message["room_id"],
unread = message["unread"]?.let { tryOrNull { Integer.parseInt(it) } },
clientSecret = message["cs"],
)
return pushDataFirebase.toPushData()
}
}

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,10 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.model
package io.element.android.libraries.push.impl.firebase
import io.element.android.libraries.matrix.api.core.MatrixPatterns
import io.element.android.libraries.push.impl.push.PushData
/**
* In this case, the format is:
@@ -31,14 +32,14 @@ import io.element.android.libraries.matrix.api.core.MatrixPatterns
* </pre>
* .
*/
data class PushDataFcm(
data class PushDataFirebase(
val eventId: String?,
val roomId: String?,
var unread: Int?,
val clientSecret: String?
)
fun PushDataFcm.toPushData() = PushData(
fun PushDataFirebase.toPushData() = PushData(
eventId = eventId?.takeIf { MatrixPatterns.isEventId(it) },
roomId = roomId?.takeIf { MatrixPatterns.isRoomId(it) },
unread = unread,

View File

@@ -14,58 +14,50 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl
package io.element.android.libraries.push.impl.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.api.store.PushDataStore
import io.element.android.libraries.push.impl.config.PushConfig
import io.element.android.libraries.push.impl.di.FirebaseMessagingServiceBindings
import io.element.android.libraries.push.impl.parser.PushParser
import io.element.android.libraries.push.impl.PushersManager
import io.element.android.libraries.push.impl.push.PushHandler
import io.element.android.libraries.push.impl.log.pushLoggerTag
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
private val loggerTag = LoggerTag("Push", LoggerTag.SYNC)
private val loggerTag = LoggerTag("Firebase", pushLoggerTag)
class VectorFirebaseMessagingService : FirebaseMessagingService() {
@Inject lateinit var fcmHelper: FcmHelper
@Inject lateinit var pushDataStore: PushDataStore
// @Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var pushersManager: PushersManager
@Inject lateinit var pushParser: PushParser
@Inject lateinit var vectorPushHandler: VectorPushHandler
@Inject lateinit var unifiedPushHelper: UnifiedPushHelper
@Inject
lateinit var pushParser: FirebasePushParser
@Inject
lateinit var pushHandler: PushHandler
private val coroutineScope = CoroutineScope(SupervisorJob())
override fun onCreate() {
super.onCreate()
applicationContext.bindings<FirebaseMessagingServiceBindings>().inject(this)
applicationContext.bindings<VectorFirebaseMessagingServiceBindings>().inject(this)
}
override fun onNewToken(token: String) {
Timber.tag(loggerTag.value).d("New Firebase token")
fcmHelper.storeFcmToken(token)
if (
// pushDataStore.areNotificationEnabledForDevice() &&
// TODO EAx activeSessionHolder.hasActiveSession() &&
unifiedPushHelper.isEmbeddedDistributor()
) {
coroutineScope.launch {
pushersManager.enqueueRegisterPusher(token, PushConfig.pusher_http_url)
}
coroutineScope.launch {
pushersManager.onNewFirebaseToken(token)
}
}
override fun onMessageReceived(message: RemoteMessage) {
Timber.tag(loggerTag.value).d("New Firebase message")
pushParser.parsePushDataFcm(message.data).let {
vectorPushHandler.handle(it)
pushParser.parse(message.data).let {
pushHandler.handle(it)
}
}
}

View File

@@ -14,13 +14,12 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.di
package io.element.android.libraries.push.impl.firebase
import com.squareup.anvil.annotations.ContributesTo
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.push.impl.VectorFirebaseMessagingService
@ContributesTo(AppScope::class)
interface FirebaseMessagingServiceBindings {
interface VectorFirebaseMessagingServiceBindings {
fun inject(service: VectorFirebaseMessagingService)
}

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.impl.log
import io.element.android.libraries.core.log.logger.LoggerTag
internal val pushLoggerTag = LoggerTag("Push")

View File

@@ -16,12 +16,7 @@
package io.element.android.libraries.push.impl.notifications
import io.element.android.libraries.push.impl.AutoAcceptInvites
import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent
import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent
import io.element.android.libraries.push.impl.notifications.model.shouldIgnoreMessageEventInRoom
import io.element.android.libraries.push.impl.notifications.model.*
import timber.log.Timber
import javax.inject.Inject
@@ -29,13 +24,12 @@ private typealias ProcessedEvents = List<ProcessedEvent<NotifiableEvent>>
class NotifiableEventProcessor @Inject constructor(
private val outdatedDetector: OutdatedEventDetector,
private val autoAcceptInvites: AutoAcceptInvites
) {
fun process(queuedEvents: List<NotifiableEvent>, currentRoomId: String?, currentThreadId: String?, renderedEvents: ProcessedEvents): ProcessedEvents {
val processedEvents = queuedEvents.map {
val type = when (it) {
is InviteNotifiableEvent -> if (autoAcceptInvites.hideInvites) ProcessedEvent.Type.REMOVE else ProcessedEvent.Type.KEEP
is InviteNotifiableEvent -> ProcessedEvent.Type.KEEP
is NotifiableMessageEvent -> when {
it.shouldIgnoreMessageEventInRoom(currentRoomId, currentThreadId) -> {
ProcessedEvent.Type.REMOVE

View File

@@ -1,57 +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.parser
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.push.impl.model.PushData
import io.element.android.libraries.push.impl.model.PushDataFcm
import io.element.android.libraries.push.impl.model.PushDataUnifiedPush
import io.element.android.libraries.push.impl.model.toPushData
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import javax.inject.Inject
/**
* Parse the received data from Push. Json format are different depending on the source.
*
* Notifications received by FCM are formatted by the matrix gateway [1]. The data send to FCM is the content
* of the "notification" attribute of the json sent to the gateway [2][3].
* On the other side, with UnifiedPush, the content of the message received is the content posted to the push
* gateway endpoint [3].
*
* *Note*: If we want to get the same content with FCM and unifiedpush, we can do a new sygnal pusher [4].
*
* [1] https://github.com/matrix-org/sygnal/blob/main/sygnal/gcmpushkin.py
* [2] https://github.com/matrix-org/sygnal/blob/main/sygnal/gcmpushkin.py#L366
* [3] https://spec.matrix.org/latest/push-gateway-api/
* [4] https://github.com/p1gp1g/sygnal/blob/unifiedpush/sygnal/upfcmpushkin.py (Not tested for a while)
*/
class PushParser @Inject constructor() {
fun parsePushDataUnifiedPush(message: ByteArray): PushData? {
return tryOrNull { Json.decodeFromString<PushDataUnifiedPush>(String(message)) }?.toPushData()
}
fun parsePushDataFcm(message: Map<String, String?>): PushData {
val pushDataFcm = PushDataFcm(
eventId = message["event_id"],
roomId = message["room_id"],
unread = message["unread"]?.let { tryOrNull { Integer.parseInt(it) } },
clientSecret = message["cs"],
)
return pushDataFcm.toPushData()
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.model
package io.element.android.libraries.push.impl.push
/**
* Represent parsed data that the app has received from a Push content.

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.impl.push
import android.content.Context
import android.content.Intent
@@ -30,19 +30,24 @@ import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.core.SessionId
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.push.impl.model.PushData
import io.element.android.libraries.push.impl.notifications.NotifiableEventResolver
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.log.pushLoggerTag
import io.element.android.libraries.push.impl.store.DefaultPushDataStore
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import timber.log.Timber
import javax.inject.Inject
private val loggerTag = LoggerTag("Push", LoggerTag.SYNC)
private val loggerTag = LoggerTag("Push", pushLoggerTag)
class VectorPushHandler @Inject constructor(
class PushHandler @Inject constructor(
private val notificationDrawerManager: NotificationDrawerManager,
private val notifiableEventResolver: NotifiableEventResolver,
// private val activeSessionHolder: ActiveSessionHolder,

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 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.impl.unifiedpush
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl
package io.element.android.libraries.push.impl.unifiedpush
import android.content.BroadcastReceiver
import android.content.Context

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,10 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.model
package io.element.android.libraries.push.impl.unifiedpush
import io.element.android.libraries.matrix.api.core.MatrixPatterns
import io.element.android.libraries.push.impl.push.PushData
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

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.impl.unifiedpush
import android.content.Context
import io.element.android.libraries.di.ApplicationContext

View File

@@ -0,0 +1,29 @@
/*
* 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.unifiedpush
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.push.impl.push.PushData
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import javax.inject.Inject
class UnifiedPushParser @Inject constructor() {
fun parse(message: ByteArray): PushData? {
return tryOrNull { Json.decodeFromString<PushDataUnifiedPush>(String(message)) }?.toPushData()
}
}

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,15 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl
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

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,18 +14,18 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl
package io.element.android.libraries.push.impl.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.di.VectorUnifiedPushMessagingReceiverBindings
import io.element.android.libraries.push.impl.parser.PushParser
import io.element.android.libraries.push.impl.*
import io.element.android.libraries.push.impl.log.pushLoggerTag
import io.element.android.libraries.push.impl.push.PushHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
@@ -34,15 +34,15 @@ import org.unifiedpush.android.connector.MessagingReceiver
import timber.log.Timber
import javax.inject.Inject
private val loggerTag = LoggerTag("Push", LoggerTag.SYNC)
private val loggerTag = LoggerTag("Unified", pushLoggerTag)
class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
@Inject lateinit var pushersManager: PushersManager
@Inject lateinit var pushParser: PushParser
@Inject lateinit var pushParser: UnifiedPushParser
//@Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var pushDataStore: PushDataStore
@Inject lateinit var vectorPushHandler: VectorPushHandler
@Inject lateinit var pushHandler: PushHandler
@Inject lateinit var guardServiceStarter: GuardServiceStarter
@Inject lateinit var unifiedPushStore: UnifiedPushStore
@Inject lateinit var unifiedPushHelper: UnifiedPushHelper
@@ -64,8 +64,8 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
*/
override fun onMessage(context: Context, message: ByteArray, instance: String) {
Timber.tag(loggerTag.value).d("New message")
pushParser.parsePushDataUnifiedPush(message)?.let {
vectorPushHandler.handle(it)
pushParser.parse(message)?.let {
pushHandler.handle(it)
} ?: run {
Timber.tag(loggerTag.value).w("Invalid received data Json format")
}
@@ -82,7 +82,7 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
unifiedPushHelper.storeCustomOrDefaultGateway(endpoint) {
unifiedPushHelper.getPushGateway()?.let {
coroutineScope.launch {
pushersManager.enqueueRegisterPusher(endpoint, it)
pushersManager.onNewUnifiedPushEndpoint(endpoint, it)
}
}
}

View File

@@ -14,11 +14,10 @@
* limitations under the License.
*/
package io.element.android.libraries.push.impl.di
package io.element.android.libraries.push.impl.unifiedpush
import com.squareup.anvil.annotations.ContributesTo
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.push.impl.VectorUnifiedPushMessagingReceiver
@ContributesTo(AppScope::class)
interface VectorUnifiedPushMessagingReceiverBindings {

View File

@@ -0,0 +1,40 @@
/*
* 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.impl.userpushstore
const val NOTIFICATION_METHOD_FIREBASE = "NOTIFICATION_METHOD_FIREBASE"
const val NOTIFICATION_METHOD_UNIFIEDPUSH = "NOTIFICATION_METHOD_UNIFIEDPUSH"
/**
* Store data related to push about a user.
*/
interface UserPushStore {
/**
* NOTIFICATION_METHOD_FIREBASE or NOTIFICATION_METHOD_UNIFIEDPUSH
*/
suspend fun getNotificationMethod(): String
suspend fun setNotificationMethod(value: String)
suspend fun getCurrentRegisteredPushKey(): String?
suspend fun setCurrentRegisteredPushKey(value: String)
suspend fun reset()
}
suspend fun UserPushStore.isFirebase(): Boolean = getNotificationMethod() == NOTIFICATION_METHOD_FIREBASE

View File

@@ -0,0 +1,63 @@
/*
* 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.impl.userpushstore
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.first
/**
* Store data related to push about a user.
*/
class UserPushStoreDataStore(
private val context: Context,
userId: String,
) : UserPushStore {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "push_store_$userId")
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 setNotificationMethod(value: String) {
context.dataStore.edit {
it[notificationMethod] = value
}
}
override suspend fun getCurrentRegisteredPushKey(): String? {
return context.dataStore.data.first()[currentPushKey]
}
override suspend fun setCurrentRegisteredPushKey(value: String) {
context.dataStore.edit {
it[currentPushKey] = value
}
}
override suspend fun reset() {
context.dataStore.edit {
it.clear()
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.impl.userpushstore
import android.content.Context
import io.element.android.libraries.di.ApplicationContext
import javax.inject.Inject
class UserPushStoreFactory @Inject constructor(
@ApplicationContext private val context: Context,
) {
fun create(userId: String): UserPushStore {
return UserPushStoreDataStore(
context = context,
userId = userId
)
}
}

View File

@@ -26,3 +26,7 @@ interface SessionStore {
suspend fun getLatestSession(): SessionData?
suspend fun removeSession(sessionId: String)
}
fun List<SessionData>.toUserList(): List<String> {
return map { it.userId }
}