Display room invitation notification (#735)
* Notifications: Add some extra mappings so we keep the original contents and can pass it later to an UI layer * Fix notifications not appearing for a room if the app was on that room when it went to background. * Modernize how we create spannable strings for notifications, remove unneeded dependency * Remove actions from invite notifications temporarily * Add `NotificationDrawerManager` interface to be able to clear membership notifications when accepting or rejecting a room invite * Fix tests * Add comment to clarify some weird behaviours * Address review comments * Set circle shape for `largeBitmap` in message notifications * Fix no avatar in DM rooms * Fix rebase issues * Add invite list pending intent: - Refactor pending intents. - Make `DeepLinkData` a sealed interface. - Fix and add tests. * Rename `navigate__` functions to `attach__` * Add an extra test case for the `InviteList` deep link * Address most review comments. * Fix rebase issue * Add fallback notification type, allow dismissing invite notifications. Fallback notifications have a different underlying type and can be dismissed at will. * Fix tests
This commit is contained in:
parent
4afdccb3ed
commit
9170c5eb71
55 changed files with 905 additions and 327 deletions
|
|
@ -18,3 +18,7 @@ package io.element.android.libraries.deeplink
|
|||
|
||||
internal const val SCHEME = "elementx"
|
||||
internal const val HOST = "open"
|
||||
|
||||
object DeepLinkPaths {
|
||||
const val INVITE_LIST = "invites"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import io.element.android.libraries.matrix.api.core.ThreadId
|
|||
import javax.inject.Inject
|
||||
|
||||
class DeepLinkCreator @Inject constructor() {
|
||||
fun create(sessionId: SessionId, roomId: RoomId?, threadId: ThreadId?): String {
|
||||
fun room(sessionId: SessionId, roomId: RoomId?, threadId: ThreadId?): String {
|
||||
return buildString {
|
||||
append("$SCHEME://$HOST/")
|
||||
append(sessionId.value)
|
||||
|
|
@ -36,4 +36,13 @@ class DeepLinkCreator @Inject constructor() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun inviteList(sessionId: SessionId): String {
|
||||
return buildString {
|
||||
append("$SCHEME://$HOST/")
|
||||
append(sessionId.value)
|
||||
append("/")
|
||||
append(DeepLinkPaths.INVITE_LIST)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,16 @@ 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.core.ThreadId
|
||||
|
||||
data class DeeplinkData(
|
||||
val sessionId: SessionId,
|
||||
val roomId: RoomId? = null,
|
||||
val threadId: ThreadId? = null,
|
||||
)
|
||||
sealed interface DeeplinkData {
|
||||
/** Session id is common for all deep links. */
|
||||
val sessionId: SessionId
|
||||
|
||||
/** The target is the root of the app, with the given [sessionId]. */
|
||||
data class Root(override val sessionId: SessionId) : DeeplinkData
|
||||
|
||||
/** The target is a room, with the given [sessionId], [roomId] and optionally a [threadId]. */
|
||||
data class Room(override val sessionId: SessionId, val roomId: RoomId, val threadId: ThreadId?) : DeeplinkData
|
||||
|
||||
/** The target is the invites list, with the given [sessionId]. */
|
||||
data class InviteList(override val sessionId: SessionId) : DeeplinkData
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package io.element.android.libraries.deeplink
|
|||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import io.element.android.libraries.core.data.tryOrNull
|
||||
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.core.ThreadId
|
||||
|
|
@ -36,12 +37,21 @@ class DeeplinkParser @Inject constructor() {
|
|||
if (host != HOST) return null
|
||||
val pathBits = path.orEmpty().split("/").drop(1)
|
||||
val sessionId = pathBits.elementAtOrNull(0)?.let(::SessionId) ?: return null
|
||||
val roomId = pathBits.elementAtOrNull(1)?.let(::RoomId)
|
||||
val threadId = pathBits.elementAtOrNull(2)?.let(::ThreadId)
|
||||
return DeeplinkData(
|
||||
sessionId = sessionId,
|
||||
roomId = roomId,
|
||||
threadId = threadId,
|
||||
)
|
||||
val screenPathComponent = pathBits.elementAtOrNull(1)
|
||||
val roomId = tryOrNull { screenPathComponent?.let(::RoomId) }
|
||||
|
||||
return when {
|
||||
roomId != null -> {
|
||||
val threadId = pathBits.elementAtOrNull(2)?.let(::ThreadId)
|
||||
DeeplinkData.Room(sessionId, roomId, threadId)
|
||||
}
|
||||
screenPathComponent == DeepLinkPaths.INVITE_LIST -> {
|
||||
DeeplinkData.InviteList(sessionId)
|
||||
}
|
||||
screenPathComponent == null -> {
|
||||
DeeplinkData.Root(sessionId)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,13 +25,20 @@ import org.junit.Test
|
|||
class DeepLinkCreatorTest {
|
||||
|
||||
@Test
|
||||
fun create() {
|
||||
fun room() {
|
||||
val sut = DeepLinkCreator()
|
||||
assertThat(sut.create(A_SESSION_ID, null, null))
|
||||
assertThat(sut.room(A_SESSION_ID, null, null))
|
||||
.isEqualTo("elementx://open/@alice:server.org")
|
||||
assertThat(sut.create(A_SESSION_ID, A_ROOM_ID, null))
|
||||
assertThat(sut.room(A_SESSION_ID, A_ROOM_ID, null))
|
||||
.isEqualTo("elementx://open/@alice:server.org/!aRoomId:domain")
|
||||
assertThat(sut.create(A_SESSION_ID, A_ROOM_ID, A_THREAD_ID))
|
||||
assertThat(sut.room(A_SESSION_ID, A_ROOM_ID, A_THREAD_ID))
|
||||
.isEqualTo("elementx://open/@alice:server.org/!aRoomId:domain/\$aThreadId")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun inviteList() {
|
||||
val sut = DeepLinkCreator()
|
||||
assertThat(sut.inviteList(A_SESSION_ID))
|
||||
.isEqualTo("elementx://open/@alice:server.org/invites")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ class DeeplinkParserTest {
|
|||
"elementx://open/@alice:server.org/!aRoomId:domain"
|
||||
const val A_URI_WITH_ROOM_WITH_THREAD =
|
||||
"elementx://open/@alice:server.org/!aRoomId:domain/\$aThreadId"
|
||||
const val A_URI_FOR_INVITE_LIST =
|
||||
"elementx://open/@alice:server.org/invites"
|
||||
}
|
||||
|
||||
private val sut = DeeplinkParser()
|
||||
|
|
@ -43,11 +45,13 @@ class DeeplinkParserTest {
|
|||
@Test
|
||||
fun `nominal cases`() {
|
||||
assertThat(sut.getFromIntent(createIntent(A_URI)))
|
||||
.isEqualTo(DeeplinkData(A_SESSION_ID, null, null))
|
||||
.isEqualTo(DeeplinkData.Root(A_SESSION_ID))
|
||||
assertThat(sut.getFromIntent(createIntent(A_URI_WITH_ROOM)))
|
||||
.isEqualTo(DeeplinkData(A_SESSION_ID, A_ROOM_ID, null))
|
||||
.isEqualTo(DeeplinkData.Room(A_SESSION_ID, A_ROOM_ID, null))
|
||||
assertThat(sut.getFromIntent(createIntent(A_URI_WITH_ROOM_WITH_THREAD)))
|
||||
.isEqualTo(DeeplinkData(A_SESSION_ID, A_ROOM_ID, A_THREAD_ID))
|
||||
.isEqualTo(DeeplinkData.Room(A_SESSION_ID, A_ROOM_ID, A_THREAD_ID))
|
||||
assertThat(sut.getFromIntent(createIntent(A_URI_FOR_INVITE_LIST)))
|
||||
.isEqualTo(DeeplinkData.InviteList(A_SESSION_ID))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue