Retrieve notification - WIP
This commit is contained in:
committed by
Benoit Marty
parent
be8ce499d0
commit
1f09f5f0eb
@@ -52,9 +52,11 @@ import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.MAIN_SPACE
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.ui.di.MatrixUIBindings
|
||||
import io.element.android.libraries.push.api.PushService
|
||||
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import kotlin.coroutines.coroutineContext
|
||||
|
||||
@@ -69,6 +71,7 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||
private val verifySessionEntryPoint: VerifySessionEntryPoint,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
snackbarDispatcher: SnackbarDispatcher,
|
||||
private val pushService: PushService,
|
||||
) : BackstackNode<LoggedInFlowNode.NavTarget>(
|
||||
backstack = BackStack(
|
||||
initialElement = NavTarget.RoomList,
|
||||
@@ -111,6 +114,10 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||
// TODO We do not support Space yet, so directly navigate to main space
|
||||
appNavigationStateService.onNavigateToSpace(MAIN_SPACE)
|
||||
loggedInFlowProcessor.observeEvents(coroutineScope)
|
||||
runBlocking {
|
||||
// TODO
|
||||
pushService.registerPusher(inputs.matrixClient.sessionId)
|
||||
}
|
||||
},
|
||||
onDestroy = {
|
||||
val imageLoaderFactory = bindings<MatrixUIBindings>().notLoggedInImageLoaderFactory()
|
||||
|
||||
@@ -19,6 +19,7 @@ package io.element.android.libraries.matrix.api
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.media.MediaResolver
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationService
|
||||
import io.element.android.libraries.matrix.api.pusher.PushersService
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
@@ -35,6 +36,7 @@ interface MatrixClient : Closeable {
|
||||
fun mediaResolver(): MediaResolver
|
||||
fun sessionVerificationService(): SessionVerificationService
|
||||
fun pushersService(): PushersService
|
||||
fun notificationService(): NotificationService
|
||||
suspend fun logout()
|
||||
suspend fun loadUserDisplayName(): Result<String>
|
||||
suspend fun loadUserAvatarURLString(): Result<String?>
|
||||
|
||||
@@ -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.matrix.api.notification
|
||||
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
|
||||
data class NotificationData(
|
||||
val item: MatrixTimelineItem,
|
||||
val title: String,
|
||||
val subtitle: String?,
|
||||
val isNoisy: Boolean,
|
||||
val avatarUrl: String?,
|
||||
)
|
||||
@@ -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.matrix.api.notification
|
||||
|
||||
interface NotificationService {
|
||||
fun getNotification(userId: String, roomId: String, eventId: String): NotificationData?
|
||||
}
|
||||
@@ -21,12 +21,14 @@ import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.media.MediaResolver
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationService
|
||||
import io.element.android.libraries.matrix.api.pusher.PushersService
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import io.element.android.libraries.matrix.impl.media.RustMediaResolver
|
||||
import io.element.android.libraries.matrix.impl.notification.RustNotificationService
|
||||
import io.element.android.libraries.matrix.impl.pushers.RustPushersService
|
||||
import io.element.android.libraries.matrix.impl.room.RustMatrixRoom
|
||||
import io.element.android.libraries.matrix.impl.room.RustRoomSummaryDataSource
|
||||
@@ -63,6 +65,7 @@ class RustMatrixClient constructor(
|
||||
|
||||
private val verificationService = RustSessionVerificationService()
|
||||
private val pushersService = RustPushersService(client)
|
||||
private val notificationService = RustNotificationService(baseDirectory)
|
||||
private var slidingSyncUpdateJob: Job? = null
|
||||
|
||||
private val clientDelegate = object : ClientDelegate {
|
||||
@@ -167,6 +170,8 @@ class RustMatrixClient constructor(
|
||||
|
||||
override fun pushersService(): PushersService = pushersService
|
||||
|
||||
override fun notificationService(): NotificationService = notificationService
|
||||
|
||||
override fun startSync() {
|
||||
if (isSyncing.compareAndSet(false, true)) {
|
||||
slidingSyncObserverToken = slidingSync.sync()
|
||||
|
||||
@@ -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.matrix.impl.notification
|
||||
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationData
|
||||
import io.element.android.libraries.matrix.impl.timeline.MatrixTimelineItemMapper
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.event.EventMessageMapper
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper
|
||||
import org.matrix.rustcomponents.sdk.NotificationItem
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import javax.inject.Inject
|
||||
|
||||
class NotificationMapper @Inject constructor() {
|
||||
// TODO Inject and remove duplicate?
|
||||
private val timelineItemFactory = MatrixTimelineItemMapper(
|
||||
virtualTimelineItemMapper = VirtualTimelineItemMapper(),
|
||||
eventTimelineItemMapper = EventTimelineItemMapper(
|
||||
contentMapper = TimelineEventContentMapper(
|
||||
eventMessageMapper = EventMessageMapper()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
fun map(notificationItem: NotificationItem): NotificationData {
|
||||
return notificationItem.use {
|
||||
NotificationData(
|
||||
item = timelineItemFactory.map(it.item),
|
||||
title = it.title,
|
||||
subtitle = it.subtitle,
|
||||
isNoisy = it.isNoisy,
|
||||
avatarUrl = it.avatarUrl,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.matrix.impl.notification
|
||||
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationData
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationService
|
||||
import java.io.File
|
||||
|
||||
class RustNotificationService(
|
||||
private val baseDirectory: File,
|
||||
) : NotificationService {
|
||||
private val notificationMapper: NotificationMapper = NotificationMapper()
|
||||
|
||||
override fun getNotification(userId: String, roomId: String, eventId: String): NotificationData? {
|
||||
return org.matrix.rustcomponents.sdk.NotificationService(
|
||||
basePath = File(baseDirectory, "sessions").absolutePath,
|
||||
userId = userId
|
||||
).use {
|
||||
// TODO Not implemented yet, see https://github.com/matrix-org/matrix-rust-sdk/issues/1628
|
||||
it.getNotificationItem(roomId, eventId)?.let { notificationItem ->
|
||||
notificationMapper.map(notificationItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,4 +25,5 @@ android {
|
||||
dependencies {
|
||||
implementation(libs.androidx.corektx)
|
||||
implementation(libs.coroutines.core)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
}
|
||||
|
||||
@@ -16,10 +16,15 @@
|
||||
|
||||
package io.element.android.libraries.push.api
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
|
||||
interface PushService {
|
||||
fun setCurrentRoom(roomId: String?)
|
||||
fun setCurrentThread(threadId: String?)
|
||||
fun notificationStyleChanged()
|
||||
|
||||
// Ensure pusher is registered
|
||||
suspend fun registerPusher(userId: UserId)
|
||||
|
||||
suspend fun testPush()
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package io.element.android.libraries.push.impl
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.push.api.PushService
|
||||
import io.element.android.libraries.push.impl.notifications.NotificationDrawerManager
|
||||
import javax.inject.Inject
|
||||
@@ -39,6 +40,10 @@ class DefaultPushService @Inject constructor(
|
||||
notificationDrawerManager.notificationStyleChanged()
|
||||
}
|
||||
|
||||
override suspend fun registerPusher(userId: UserId) {
|
||||
pusherManager.registerPusher(userId)
|
||||
}
|
||||
|
||||
override suspend fun testPush() {
|
||||
pusherManager.testPush()
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package io.element.android.libraries.push.impl
|
||||
|
||||
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.UserId
|
||||
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
|
||||
@@ -39,6 +40,7 @@ class PushersManager @Inject constructor(
|
||||
private val pushClientSecret: PushClientSecret,
|
||||
private val sessionStore: SessionStore,
|
||||
private val matrixAuthenticationService: MatrixAuthenticationService,
|
||||
private val fcmHelper: FcmHelper,
|
||||
) {
|
||||
suspend fun testPush() {
|
||||
pushGatewayNotifyRequest.execute(
|
||||
@@ -55,6 +57,7 @@ class PushersManager @Inject constructor(
|
||||
return enqueueRegisterPusher(pushKey, PushConfig.pusher_http_url)
|
||||
}
|
||||
|
||||
// TODO Rename
|
||||
suspend fun enqueueRegisterPusher(
|
||||
pushKey: String,
|
||||
gateway: String
|
||||
@@ -68,6 +71,14 @@ class PushersManager @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun registerPusher(userId: UserId) {
|
||||
val pushKey = fcmHelper.getFcmToken() ?: return
|
||||
// Register the pusher for the session
|
||||
val client = matrixAuthenticationService.restoreSession(userId).getOrNull() ?: return
|
||||
client.pushersService().setHttpPusher(createHttpPusher(pushKey, PushConfig.pusher_http_url, userId.value))
|
||||
// Close sessions?
|
||||
}
|
||||
|
||||
private suspend fun createHttpPusher(
|
||||
pushKey: String,
|
||||
gateway: String,
|
||||
|
||||
@@ -27,6 +27,8 @@ 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.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.clientsecret.PushClientSecret
|
||||
import io.element.android.libraries.push.impl.model.PushData
|
||||
@@ -34,11 +36,7 @@ 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 kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.*
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -53,7 +51,8 @@ class VectorPushHandler @Inject constructor(
|
||||
private val pushClientSecret: PushClientSecret,
|
||||
private val actionIds: NotificationActionIds,
|
||||
@ApplicationContext private val context: Context,
|
||||
private val buildMeta: BuildMeta
|
||||
private val buildMeta: BuildMeta,
|
||||
private val matrixAuthenticationService: MatrixAuthenticationService,
|
||||
) {
|
||||
|
||||
private val coroutineScope = CoroutineScope(SupervisorJob())
|
||||
@@ -115,9 +114,38 @@ class VectorPushHandler @Inject constructor(
|
||||
Timber.tag(loggerTag.value).d("## handleInternal()")
|
||||
}
|
||||
|
||||
pushData.roomId ?: return
|
||||
pushData.eventId ?: return
|
||||
|
||||
val clientSecret = pushData.clientSecret
|
||||
val userId = if (clientSecret == null) {
|
||||
// Should not happen. In this case, restore default session
|
||||
null
|
||||
} else {
|
||||
// Get userId from client secret
|
||||
pushClientSecret.getUserIdFromSecret(clientSecret)
|
||||
} ?: run {
|
||||
matrixAuthenticationService.getLatestSessionId()?.value
|
||||
}
|
||||
|
||||
if (userId == null) {
|
||||
Timber.w("Unable to get a session")
|
||||
return
|
||||
}
|
||||
|
||||
// Restore session
|
||||
val session = matrixAuthenticationService.restoreSession(SessionId(userId)).getOrNull() ?: return
|
||||
// TODO EAx, no need for a session?
|
||||
val notificationData = session.notificationService().getNotification(
|
||||
userId = userId,
|
||||
roomId = pushData.roomId,
|
||||
eventId = pushData.eventId,
|
||||
)
|
||||
|
||||
Timber.w("Notification: $notificationData")
|
||||
// TODO Display notification
|
||||
|
||||
/* TODO EAx
|
||||
- Retrieve secret and use pushClientSecret
|
||||
- Open matching session
|
||||
- get the event
|
||||
- display the notif
|
||||
|
||||
|
||||
@@ -27,6 +27,5 @@ data class PushData(
|
||||
val eventId: String?,
|
||||
val roomId: String?,
|
||||
val unread: Int?,
|
||||
|
||||
// TODO EAx Client secret
|
||||
val clientSecret: String?,
|
||||
)
|
||||
|
||||
@@ -25,19 +25,22 @@ import io.element.android.libraries.matrix.api.core.MatrixPatterns
|
||||
* "event_id":"$anEventId",
|
||||
* "room_id":"!aRoomId",
|
||||
* "unread":"1",
|
||||
* "prio":"high"
|
||||
* "prio":"high",
|
||||
* "cs":"<client_secret>"
|
||||
* }
|
||||
* </pre>
|
||||
* .
|
||||
*/
|
||||
data class PushDataFcm(
|
||||
val eventId: String?,
|
||||
val roomId: String?,
|
||||
var unread: Int?,
|
||||
val eventId: String?,
|
||||
val roomId: String?,
|
||||
var unread: Int?,
|
||||
val clientSecret: String?
|
||||
)
|
||||
|
||||
fun PushDataFcm.toPushData() = PushData(
|
||||
eventId = eventId?.takeIf { MatrixPatterns.isEventId(it) },
|
||||
roomId = roomId?.takeIf { MatrixPatterns.isRoomId(it) },
|
||||
unread = unread
|
||||
eventId = eventId?.takeIf { MatrixPatterns.isEventId(it) },
|
||||
roomId = roomId?.takeIf { MatrixPatterns.isRoomId(it) },
|
||||
unread = unread,
|
||||
clientSecret = clientSecret,
|
||||
)
|
||||
|
||||
@@ -38,7 +38,7 @@ import kotlinx.serialization.Serializable
|
||||
*/
|
||||
@Serializable
|
||||
data class PushDataUnifiedPush(
|
||||
val notification: PushDataUnifiedPushNotification?
|
||||
val notification: PushDataUnifiedPushNotification?
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@@ -50,11 +50,12 @@ data class PushDataUnifiedPushNotification(
|
||||
|
||||
@Serializable
|
||||
data class PushDataUnifiedPushCounts(
|
||||
@SerialName("unread") val unread: Int?
|
||||
@SerialName("unread") val unread: Int?
|
||||
)
|
||||
|
||||
fun PushDataUnifiedPush.toPushData() = PushData(
|
||||
eventId = notification?.eventId?.takeIf { MatrixPatterns.isEventId(it) },
|
||||
roomId = notification?.roomId?.takeIf { MatrixPatterns.isRoomId(it) },
|
||||
unread = notification?.counts?.unread
|
||||
eventId = notification?.eventId?.takeIf { MatrixPatterns.isEventId(it) },
|
||||
roomId = notification?.roomId?.takeIf { MatrixPatterns.isRoomId(it) },
|
||||
unread = notification?.counts?.unread,
|
||||
clientSecret = null // TODO EAx check how client secret will be sent through UnifiedPush
|
||||
)
|
||||
|
||||
@@ -50,6 +50,7 @@ class PushParser @Inject constructor() {
|
||||
eventId = message["event_id"],
|
||||
roomId = message["room_id"],
|
||||
unread = message["unread"]?.let { tryOrNull { Integer.parseInt(it) } },
|
||||
clientSecret = message["cs"],
|
||||
)
|
||||
return pushDataFcm.toPushData()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user