Permalink: handle via parameters

This commit is contained in:
ganfra 2020-08-26 16:37:48 +02:00
parent 9970398cf2
commit b986bfd509
6 changed files with 91 additions and 21 deletions

View File

@ -25,7 +25,12 @@ import android.net.Uri
*/ */
sealed class PermalinkData { 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<String>
) : PermalinkData()
data class UserLink(val userId: String) : PermalinkData() data class UserLink(val userId: String) : PermalinkData()

View File

@ -19,6 +19,10 @@ package org.matrix.android.sdk.api.session.permalinks
import android.net.Uri import android.net.Uri
import org.matrix.android.sdk.api.MatrixPatterns 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] * This class turns an uri to a [PermalinkData]
@ -40,14 +44,13 @@ object PermalinkParser {
if (!uri.toString().startsWith(PermalinkService.MATRIX_TO_URL_BASE)) { if (!uri.toString().startsWith(PermalinkService.MATRIX_TO_URL_BASE)) {
return PermalinkData.FallbackLink(uri) return PermalinkData.FallbackLink(uri)
} }
val fragment = uri.fragment val fragment = uri.fragment
if (fragment.isNullOrEmpty()) { if (fragment.isNullOrEmpty()) {
return PermalinkData.FallbackLink(uri) return PermalinkData.FallbackLink(uri)
} }
val indexOfQuery = fragment.indexOf("?") val indexOfQuery = fragment.indexOf("?")
val safeFragment = if (indexOfQuery != -1) fragment.substring(0, indexOfQuery) else fragment val safeFragment = if (indexOfQuery != -1) fragment.substring(0, indexOfQuery) else fragment
val viaQueryParameters = fragment.getViaParameters(indexOfQuery)
// we are limiting to 2 params // we are limiting to 2 params
val params = safeFragment val params = safeFragment
@ -65,17 +68,58 @@ object PermalinkParser {
PermalinkData.RoomLink( PermalinkData.RoomLink(
roomIdOrAlias = identifier, roomIdOrAlias = identifier,
isRoomAlias = false, isRoomAlias = false,
eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) } eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) },
viaParameters = viaQueryParameters
) )
} }
MatrixPatterns.isRoomAlias(identifier) -> { MatrixPatterns.isRoomAlias(identifier) -> {
PermalinkData.RoomLink( PermalinkData.RoomLink(
roomIdOrAlias = identifier, roomIdOrAlias = identifier,
isRoomAlias = true, isRoomAlias = true,
eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) } eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) },
viaParameters = viaQueryParameters
) )
} }
else -> PermalinkData.FallbackLink(uri) else -> PermalinkData.FallbackLink(uri)
} }
} }
private fun String.getViaParameters(indexOfQuery: Int): List<String> {
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<String>()
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)
}
} }

View File

@ -56,7 +56,25 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti
if (deepLink == null) { if (deepLink == null) {
return Single.just(false) 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<Boolean> {
return when (permalinkData) {
is PermalinkData.RoomLink -> { is PermalinkData.RoomLink -> {
permalinkData.getRoomId() permalinkData.getRoomId()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -66,8 +84,7 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti
openRoom( openRoom(
context = context, context = context,
roomId = roomId, roomId = roomId,
roomAlias = permalinkData.getRoomAliasOrNull(), permalinkData = permalinkData,
eventId = permalinkData.eventId,
buildTask = buildTask buildTask = buildTask
) )
} }
@ -87,7 +104,7 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti
is PermalinkData.FallbackLink -> { is PermalinkData.FallbackLink -> {
Single.just(false) Single.just(false)
} }
}.onErrorReturnItem(false) }
} }
private fun PermalinkData.RoomLink.getRoomId(): Single<Optional<String>> { private fun PermalinkData.RoomLink.getRoomId(): Single<Optional<String>> {
@ -110,13 +127,20 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti
/** /**
* Open room either joined, or not * 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 val session = activeSessionHolder.getSafeActiveSession() ?: return
if (roomId == null) { if (roomId == null) {
context.toast(R.string.room_error_not_found) context.toast(R.string.room_error_not_found)
return return
} }
val roomSummary = session.getRoomSummary(roomId) val roomSummary = session.getRoomSummary(roomId)
val eventId = permalinkData.eventId
val roomAlias = permalinkData.getRoomAliasOrNull()
return when { return when {
roomSummary?.membership?.isActive().orFalse() -> { roomSummary?.membership?.isActive().orFalse() -> {
navigator.openRoom(context, roomId, eventId, buildTask) navigator.openRoom(context, roomId, eventId, buildTask)
@ -128,7 +152,8 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti
roomAlias = roomAlias ?: roomSummary?.canonicalAlias, roomAlias = roomAlias ?: roomSummary?.canonicalAlias,
roomName = roomSummary?.displayName, roomName = roomSummary?.displayName,
avatarUrl = roomSummary?.avatarUrl, avatarUrl = roomSummary?.avatarUrl,
buildTask = buildTask buildTask = buildTask,
homeServers = permalinkData.viaParameters
) )
navigator.openRoomPreview(context, roomPreviewData) navigator.openRoomPreview(context, roomPreviewData)
} }

View File

@ -38,7 +38,7 @@ data class RoomPreviewData(
val topic: String? = null, val topic: String? = null,
val worldReadable: Boolean = false, val worldReadable: Boolean = false,
val avatarUrl: String? = null, val avatarUrl: String? = null,
val homeServer: String? = null, val homeServers: List<String> = emptyList(),
val buildTask: Boolean = false val buildTask: Boolean = false
) : Parcelable { ) : Parcelable {
val matrixItem: MatrixItem val matrixItem: MatrixItem
@ -64,7 +64,7 @@ class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable {
topic = publicRoom.topic, topic = publicRoom.topic,
worldReadable = publicRoom.worldReadable, worldReadable = publicRoom.worldReadable,
avatarUrl = publicRoom.avatarUrl, avatarUrl = publicRoom.avatarUrl,
homeServer = roomDirectoryData.homeServer homeServers = listOfNotNull(roomDirectoryData.homeServer)
) )
return newIntent(context, roomPreviewData) return newIntent(context, roomPreviewData)
} }

View File

@ -106,10 +106,7 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini
Timber.w("Try to join an already joining room. Should not happen") Timber.w("Try to join an already joining room. Should not happen")
return@withState return@withState
} }
val viaServers = state.homeServer?.let { session.joinRoom(state.roomId, viaServers = state.homeServers, callback = object : MatrixCallback<Unit> {
listOf(it)
} ?: emptyList()
session.joinRoom(state.roomId, viaServers = viaServers, callback = object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) { override fun onSuccess(data: Unit) {
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data. // 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 // Instead, we wait for the room to be joined

View File

@ -24,10 +24,9 @@ data class RoomPreviewViewState(
val roomId: String = "", val roomId: String = "",
val roomAlias: String? = null, val roomAlias: String? = null,
/** /**
* The server name (might be null) * Can be empty when the server is the current user's home server.
* Set null when the server is the current user's home server.
*/ */
val homeServer: String? = null, val homeServers: List<String> = emptyList(),
// Current state of the room in preview // Current state of the room in preview
val roomJoinState: JoinState = JoinState.NOT_JOINED, val roomJoinState: JoinState = JoinState.NOT_JOINED,
// Last error of join room request // Last error of join room request
@ -37,6 +36,6 @@ data class RoomPreviewViewState(
constructor(args: RoomPreviewData) : this( constructor(args: RoomPreviewData) : this(
roomId = args.roomId, roomId = args.roomId,
roomAlias = args.roomAlias, roomAlias = args.roomAlias,
homeServer = args.homeServer homeServers = args.homeServers
) )
} }