diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkData.kt index 47f8bf4505..626fc49e8e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkData.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkData.kt @@ -24,9 +24,9 @@ import android.net.Uri */ sealed class PermalinkData { - data class EventLink(val roomIdOrAlias: String, val eventId: String) : PermalinkData() + data class EventLink(val roomIdOrAlias: String, val eventId: String, val isRoomAlias: Boolean) : PermalinkData() - data class RoomLink(val roomIdOrAlias: String) : PermalinkData() + data class RoomLink(val roomIdOrAlias: String, val isRoomAlias: Boolean) : PermalinkData() data class UserLink(val userId: String) : PermalinkData() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkParser.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkParser.kt index 531f4ae523..588fbce9d0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkParser.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkParser.kt @@ -18,6 +18,8 @@ package im.vector.matrix.android.api.permalinks import android.net.Uri import im.vector.matrix.android.api.MatrixPatterns +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext /** * This class turns an uri to a [PermalinkData] @@ -60,16 +62,23 @@ object PermalinkParser { return PermalinkData.FallbackLink(uri) } return when { - MatrixPatterns.isUserId(identifier) -> PermalinkData.UserLink(userId = identifier) - MatrixPatterns.isGroupId(identifier) -> PermalinkData.GroupLink(groupId = identifier) - MatrixPatterns.isRoomId(identifier) -> { + MatrixPatterns.isUserId(identifier) -> PermalinkData.UserLink(userId = identifier) + MatrixPatterns.isGroupId(identifier) -> PermalinkData.GroupLink(groupId = identifier) + MatrixPatterns.isRoomId(identifier) -> { if (!extraParameter.isNullOrEmpty() && MatrixPatterns.isEventId(extraParameter)) { - PermalinkData.EventLink(roomIdOrAlias = identifier, eventId = extraParameter) + PermalinkData.EventLink(roomIdOrAlias = identifier, eventId = extraParameter, isRoomAlias = false) } else { - PermalinkData.RoomLink(roomIdOrAlias = identifier) + PermalinkData.RoomLink(roomIdOrAlias = identifier, isRoomAlias = false) } } - else -> PermalinkData.FallbackLink(uri) + MatrixPatterns.isRoomAlias(identifier) -> { + if (!extraParameter.isNullOrEmpty() && MatrixPatterns.isEventId(extraParameter)) { + PermalinkData.EventLink(roomIdOrAlias = identifier, eventId = extraParameter, isRoomAlias = true) + } else { + PermalinkData.RoomLink(roomIdOrAlias = identifier, isRoomAlias = true) + } + } + else -> PermalinkData.FallbackLink(uri) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt index 98abce5898..75de4e44a6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt @@ -18,6 +18,7 @@ package im.vector.matrix.android.api.session.room import androidx.lifecycle.LiveData import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.internal.session.room.alias.RoomAliasDescription import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.api.util.Cancelable @@ -74,4 +75,10 @@ interface RoomService { */ fun markAllAsRead(roomIds: List, callback: MatrixCallback): Cancelable + + /** + * Resolve a room alias to a room ID. + */ + fun getRoomIdByAlias(roomAlias: String, + callback: MatrixCallback): Cancelable } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt index 447ba563de..4807ae605c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt @@ -19,6 +19,7 @@ package im.vector.matrix.android.api.session.room.model import im.vector.matrix.android.api.session.room.model.tag.RoomTag import im.vector.matrix.android.api.session.room.send.UserDraft import im.vector.matrix.android.api.session.room.timeline.TimelineEvent +import io.realm.RealmList /** * This class holds some data of a room. @@ -29,6 +30,8 @@ data class RoomSummary( val displayName: String = "", val topic: String = "", val avatarUrl: String = "", + val canonicalAlias: String? = null, + val aliases: List = emptyList(), val isDirect: Boolean = false, val latestPreviewableEvent: TimelineEvent? = null, val otherMemberIds: List = emptyList(), diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt index 2577bec581..eeb340eacb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt @@ -68,7 +68,9 @@ internal class RoomSummaryMapper @Inject constructor( membership = roomSummaryEntity.membership, versioningState = roomSummaryEntity.versioningState, readMarkerId = roomSummaryEntity.readMarkerId, - userDrafts = roomSummaryEntity.userDrafts?.userDrafts?.map { DraftMapper.map(it) } ?: emptyList() + userDrafts = roomSummaryEntity.userDrafts?.userDrafts?.map { DraftMapper.map(it) } ?: emptyList(), + canonicalAlias = roomSummaryEntity.canonicalAlias, + aliases = roomSummaryEntity.aliases.toList() ) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt index 47904380a0..b5b1644e74 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt @@ -40,6 +40,8 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "", var tags: RealmList = RealmList(), var userDrafts: UserDraftsEntity? = null, var breadcrumbsIndex: Int = NOT_IN_BREADCRUMBS + var canonicalAlias: String? = null, + var aliases: RealmList = RealmList() ) : RealmObject() { private var membershipStr: String = Membership.NONE.name diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/RoomSummaryEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/RoomSummaryEntityQueries.kt index 79473f3b10..07f5133128 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/RoomSummaryEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/RoomSummaryEntityQueries.kt @@ -32,6 +32,18 @@ internal fun RoomSummaryEntity.Companion.where(realm: Realm, roomId: String? = n return query } +internal fun RoomSummaryEntity.Companion.findByAlias(realm: Realm, roomAlias: String): RoomSummaryEntity? { + val roomSummary = realm.where() + .equalTo(RoomSummaryEntityFields.CANONICAL_ALIAS, roomAlias) + .findFirst() + if (roomSummary != null) { + return roomSummary + } + return realm.where() + .`in`(RoomSummaryEntityFields.ALIASES.`$`, arrayOf(roomAlias)) + .findFirst() +} + internal fun RoomSummaryEntity.Companion.getOrCreate(realm: Realm, roomId: String): RoomSummaryEntity { return where(realm, roomId).findFirst() ?: realm.createObject(roomId) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt index 22caf76eaf..a45e41295c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt @@ -21,6 +21,7 @@ import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.RoomService +import im.vector.matrix.android.internal.session.room.alias.RoomAliasDescription import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.VersioningState import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams @@ -30,6 +31,7 @@ import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.session.room.alias.GetRoomIdByAliasTask import im.vector.matrix.android.internal.session.room.create.CreateRoomTask import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask import im.vector.matrix.android.internal.session.room.read.MarkAllRoomsReadTask @@ -45,9 +47,11 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona private val joinRoomTask: JoinRoomTask, private val markAllRoomsReadTask: MarkAllRoomsReadTask, private val updateBreadcrumbsTask: UpdateBreadcrumbsTask, + private val roomIdByAliasTask: GetRoomIdByAliasTask, private val roomFactory: RoomFactory, private val taskExecutor: TaskExecutor) : RoomService { + override fun createRoom(createRoomParams: CreateRoomParams, callback: MatrixCallback): Cancelable { return createRoomTask .configureWith(createRoomParams) { @@ -111,4 +115,12 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona } .executeBy(taskExecutor) } + + override fun getRoomIdByAlias(roomAlias: String, callback: MatrixCallback): Cancelable { + return roomIdByAliasTask + .configureWith(GetRoomIdByAliasTask.Params(roomAlias)) { + this.callback = callback + } + .executeBy(taskExecutor) + } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt index 40164d1697..c5b3f03d35 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt @@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.session.room import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.internal.session.room.alias.RoomAliasDescription import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.api.session.room.model.create.CreateRoomResponse import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams @@ -258,4 +259,12 @@ internal interface RoomAPI { fun reportContent(@Path("roomId") roomId: String, @Path("eventId") eventId: String, @Body body: ReportContentBody): Call + + /** + * Get the room ID associated to the room alias. + * + * @param roomAlias the room alias. + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}") + fun getRoomIdByAlias(@Path("roomAlias") roomAlias: String): Call } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt index 1aca492b94..cc786a7493 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt @@ -24,6 +24,8 @@ import im.vector.matrix.android.api.session.room.RoomDirectoryService import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.internal.session.DefaultFileService import im.vector.matrix.android.internal.session.SessionScope +import im.vector.matrix.android.internal.session.room.alias.DefaultGetRoomIdByAliasTask +import im.vector.matrix.android.internal.session.room.alias.GetRoomIdByAliasTask import im.vector.matrix.android.internal.session.room.create.CreateRoomTask import im.vector.matrix.android.internal.session.room.create.DefaultCreateRoomTask import im.vector.matrix.android.internal.session.room.directory.DefaultGetPublicRoomTask @@ -133,4 +135,7 @@ internal abstract class RoomModule { @Binds abstract fun bindFetchEditHistoryTask(fetchEditHistoryTask: DefaultFetchEditHistoryTask): FetchEditHistoryTask + + @Binds + abstract fun bindGetRoomIdByAliasTask(getRoomIdByAliasTask: DefaultGetRoomIdByAliasTask): GetRoomIdByAliasTask } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt index 1158c08984..11bc98164b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt @@ -20,6 +20,8 @@ import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.Membership +import im.vector.matrix.android.api.session.room.model.RoomAliasesContent +import im.vector.matrix.android.api.session.room.model.RoomCanonicalAliasContent import im.vector.matrix.android.api.session.room.model.RoomTopicContent import im.vector.matrix.android.internal.database.mapper.ContentMapper import im.vector.matrix.android.internal.database.model.EventEntity @@ -68,7 +70,7 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId unreadNotifications: RoomSyncUnreadNotifications? = null, updateMembers: Boolean = false) { val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst() - ?: realm.createObject(roomId) + ?: realm.createObject(roomId) if (roomSummary != null) { if (roomSummary.heroes.isNotEmpty()) { @@ -91,15 +93,24 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId val latestPreviewableEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true, filterTypes = PREVIEWABLE_TYPES) val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev() + val lastCanonicalAliasEvent = EventEntity.where(realm, roomId, EventType.STATE_CANONICAL_ALIAS).prev() + val lastAliasesEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_ALIASES).prev() roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0 - // avoid this call if we are sure there are unread events - || !isEventRead(monarchy, userId, roomId, latestPreviewableEvent?.eventId) + // avoid this call if we are sure there are unread events + || !isEventRead(monarchy, userId, roomId, latestPreviewableEvent?.eventId) roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(roomId).toString() roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(roomId) roomSummaryEntity.topic = ContentMapper.map(lastTopicEvent?.content).toModel()?.topic roomSummaryEntity.latestPreviewableEvent = latestPreviewableEvent + roomSummaryEntity.canonicalAlias = ContentMapper.map(lastCanonicalAliasEvent?.content).toModel() + ?.canonicalAlias + + val roomAliases = ContentMapper.map(lastAliasesEvent?.content).toModel()?.aliases ?: emptyList() + roomSummaryEntity.aliases.clear() + roomSummaryEntity.aliases.addAll(roomAliases) + if (updateMembers) { val otherRoomMembers = RoomMembers(realm, roomId) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/alias/GetRoomIdByAliasTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/alias/GetRoomIdByAliasTask.kt new file mode 100644 index 0000000000..5ff2e47c70 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/alias/GetRoomIdByAliasTask.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2019 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 im.vector.matrix.android.internal.session.room.alias + +import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.internal.database.model.RoomSummaryEntity +import im.vector.matrix.android.internal.database.query.findByAlias +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.room.RoomAPI +import im.vector.matrix.android.internal.task.Task +import io.realm.Realm +import javax.inject.Inject + +internal interface GetRoomIdByAliasTask : Task { + data class Params( + val roomAlias: String + ) +} + +internal class DefaultGetRoomIdByAliasTask @Inject constructor(private val monarchy: Monarchy, + private val roomAPI: RoomAPI) : GetRoomIdByAliasTask { + + override suspend fun execute(params: GetRoomIdByAliasTask.Params): String? { + val roomId = Realm.getInstance(monarchy.realmConfiguration).use { + RoomSummaryEntity.findByAlias(it, params.roomAlias)?.roomId + } + if (roomId != null) { + return roomId + } + return executeRequest { + apiCall = roomAPI.getRoomIdByAlias(params.roomAlias) + }.roomId + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/alias/RoomAliasDescription.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/alias/RoomAliasDescription.kt new file mode 100644 index 0000000000..8d352cfed9 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/alias/RoomAliasDescription.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2019 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 im.vector.matrix.android.internal.session.room.alias + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class RoomAliasDescription( + /** + * The room ID for this alias. + */ + @Json(name = "room_id") val roomId: String, + + /** + * A list of servers that are aware of this room ID. + */ + @Json(name = "servers") val servers: List = emptyList() +)