diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkData.kt index 85632d6e83..1da65b3002 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkData.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkData.kt @@ -25,7 +25,12 @@ import android.net.Uri */ sealed class PermalinkData { - data class RoomLink(val roomIdOrAlias: String, val isRoomAlias: Boolean, val eventId: String?) : PermalinkData() + data class RoomLink( + val roomIdOrAlias: String, + val isRoomAlias: Boolean, + val eventId: String?, + val viaParameters: List + ) : PermalinkData() data class UserLink(val userId: String) : PermalinkData() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt index dd6847f1e3..d741367376 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt @@ -19,6 +19,10 @@ package org.matrix.android.sdk.api.session.permalinks import android.net.Uri import org.matrix.android.sdk.api.MatrixPatterns +import java.io.UnsupportedEncodingException +import java.net.URLEncoder +import java.util.ArrayList +import java.util.Collections /** * This class turns an uri to a [PermalinkData] @@ -40,14 +44,13 @@ object PermalinkParser { if (!uri.toString().startsWith(PermalinkService.MATRIX_TO_URL_BASE)) { return PermalinkData.FallbackLink(uri) } - val fragment = uri.fragment if (fragment.isNullOrEmpty()) { return PermalinkData.FallbackLink(uri) } - val indexOfQuery = fragment.indexOf("?") val safeFragment = if (indexOfQuery != -1) fragment.substring(0, indexOfQuery) else fragment + val viaQueryParameters = fragment.getViaParameters(indexOfQuery) // we are limiting to 2 params val params = safeFragment @@ -65,17 +68,58 @@ object PermalinkParser { PermalinkData.RoomLink( roomIdOrAlias = identifier, isRoomAlias = false, - eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) } + eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) }, + viaParameters = viaQueryParameters ) } MatrixPatterns.isRoomAlias(identifier) -> { PermalinkData.RoomLink( roomIdOrAlias = identifier, isRoomAlias = true, - eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) } + eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) }, + viaParameters = viaQueryParameters ) } else -> PermalinkData.FallbackLink(uri) } } + + private fun String.getViaParameters(indexOfQuery: Int): List { + val query = try { + substring(indexOfQuery + 1) + } catch (e: IndexOutOfBoundsException) { + return emptyList() + } + val encodedKey = try { + URLEncoder.encode("via", "UTF-8") + } catch (e: UnsupportedEncodingException) { + return emptyList() + } + val values = ArrayList() + var start = 0 + do { + val nextAmpersand = query.indexOf('&', start) + val end = if (nextAmpersand != -1) nextAmpersand else query.length + var separator = query.indexOf('=', start) + if (separator > end || separator == -1) { + separator = end + } + if (separator - start == encodedKey.length + && query.regionMatches(start, encodedKey, 0, encodedKey.length)) { + if (separator == end) { + values.add("") + } else { + values.add(Uri.decode(query.substring(separator + 1, end))) + } + } + + // Move start to end of name. + start = if (nextAmpersand != -1) { + nextAmpersand + 1 + } else { + break + } + } while (true) + return Collections.unmodifiableList(values) + } } diff --git a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt index cd26027f94..4ca16888a5 100644 --- a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt +++ b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt @@ -56,7 +56,25 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti if (deepLink == null) { return Single.just(false) } - return when (val permalinkData = PermalinkParser.parse(deepLink)) { + return Single + .fromCallable { + PermalinkParser.parse(deepLink) + } + .subscribeOn(Schedulers.computation()) + .observeOn(AndroidSchedulers.mainThread()) + .flatMap { permalinkData -> + handlePermalink(permalinkData, context, navigationInterceptor, buildTask) + } + .onErrorReturnItem(false) + } + + private fun handlePermalink( + permalinkData: PermalinkData, + context: Context, + navigationInterceptor: NavigationInterceptor?, + buildTask: Boolean + ): Single { + return when (permalinkData) { is PermalinkData.RoomLink -> { permalinkData.getRoomId() .observeOn(AndroidSchedulers.mainThread()) @@ -66,8 +84,7 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti openRoom( context = context, roomId = roomId, - roomAlias = permalinkData.getRoomAliasOrNull(), - eventId = permalinkData.eventId, + permalinkData = permalinkData, buildTask = buildTask ) } @@ -87,7 +104,7 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti is PermalinkData.FallbackLink -> { Single.just(false) } - }.onErrorReturnItem(false) + } } private fun PermalinkData.RoomLink.getRoomId(): Single> { @@ -110,13 +127,20 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti /** * Open room either joined, or not */ - private fun openRoom(context: Context, roomId: String?, roomAlias: String?, eventId: String?, buildTask: Boolean) { + private fun openRoom( + context: Context, + roomId: String?, + permalinkData: PermalinkData.RoomLink, + buildTask: Boolean + ) { val session = activeSessionHolder.getSafeActiveSession() ?: return if (roomId == null) { context.toast(R.string.room_error_not_found) return } val roomSummary = session.getRoomSummary(roomId) + val eventId = permalinkData.eventId + val roomAlias = permalinkData.getRoomAliasOrNull() return when { roomSummary?.membership?.isActive().orFalse() -> { navigator.openRoom(context, roomId, eventId, buildTask) @@ -128,7 +152,8 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti roomAlias = roomAlias ?: roomSummary?.canonicalAlias, roomName = roomSummary?.displayName, avatarUrl = roomSummary?.avatarUrl, - buildTask = buildTask + buildTask = buildTask, + homeServers = permalinkData.viaParameters ) navigator.openRoomPreview(context, roomPreviewData) } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt index d304594474..8bbaa14ab0 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt @@ -38,7 +38,7 @@ data class RoomPreviewData( val topic: String? = null, val worldReadable: Boolean = false, val avatarUrl: String? = null, - val homeServer: String? = null, + val homeServers: List = emptyList(), val buildTask: Boolean = false ) : Parcelable { val matrixItem: MatrixItem @@ -64,7 +64,7 @@ class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { topic = publicRoom.topic, worldReadable = publicRoom.worldReadable, avatarUrl = publicRoom.avatarUrl, - homeServer = roomDirectoryData.homeServer + homeServers = listOfNotNull(roomDirectoryData.homeServer) ) return newIntent(context, roomPreviewData) } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt index 04b7c33fb2..900ba537b5 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt @@ -106,10 +106,7 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini Timber.w("Try to join an already joining room. Should not happen") return@withState } - val viaServers = state.homeServer?.let { - listOf(it) - } ?: emptyList() - session.joinRoom(state.roomId, viaServers = viaServers, callback = object : MatrixCallback { + session.joinRoom(state.roomId, viaServers = state.homeServers, callback = object : MatrixCallback { override fun onSuccess(data: Unit) { // We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data. // Instead, we wait for the room to be joined diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt index f461225e2d..6816e54481 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt @@ -24,10 +24,9 @@ data class RoomPreviewViewState( val roomId: String = "", val roomAlias: String? = null, /** - * The server name (might be null) - * Set null when the server is the current user's home server. + * Can be empty when the server is the current user's home server. */ - val homeServer: String? = null, + val homeServers: List = emptyList(), // Current state of the room in preview val roomJoinState: JoinState = JoinState.NOT_JOINED, // Last error of join room request @@ -37,6 +36,6 @@ data class RoomPreviewViewState( constructor(args: RoomPreviewData) : this( roomId = args.roomId, roomAlias = args.roomAlias, - homeServer = args.homeServer + homeServers = args.homeServers ) }