mirror of
https://github.com/element-hq/element-android
synced 2024-11-25 02:45:37 +03:00
Merge pull request #706 from vector-im/feature/handle_matrix_to
Feature/handle matrix to
This commit is contained in:
commit
4a11d028c0
29 changed files with 500 additions and 227 deletions
|
@ -5,7 +5,8 @@ Features ✨:
|
||||||
- Implement soft logout (#281)
|
- Implement soft logout (#281)
|
||||||
|
|
||||||
Improvements 🙌:
|
Improvements 🙌:
|
||||||
-
|
- Handle navigation to room via room alias (#201)
|
||||||
|
- Open matrix.to link in RiotX (#57)
|
||||||
|
|
||||||
Other changes:
|
Other changes:
|
||||||
- Use same default room colors than Riot-Web
|
- Use same default room colors than Riot-Web
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.rx
|
|
||||||
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
|
||||||
import io.reactivex.CompletableEmitter
|
|
||||||
|
|
||||||
internal class MatrixCallbackCompletable<T>(private val completableEmitter: CompletableEmitter) : MatrixCallback<T> {
|
|
||||||
|
|
||||||
override fun onSuccess(data: T) {
|
|
||||||
completableEmitter.onComplete()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
completableEmitter.tryOnError(failure)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Cancelable.toCompletable(completableEmitter: CompletableEmitter) {
|
|
||||||
completableEmitter.setCancellable {
|
|
||||||
this.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* 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.rx
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
import io.reactivex.Completable
|
||||||
|
import io.reactivex.Single
|
||||||
|
|
||||||
|
fun <T> singleBuilder(builder: (callback: MatrixCallback<T>) -> Cancelable): Single<T> = Single.create {
|
||||||
|
val callback: MatrixCallback<T> = object : MatrixCallback<T> {
|
||||||
|
override fun onSuccess(data: T) {
|
||||||
|
it.onSuccess(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
it.tryOnError(failure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val cancelable = builder(callback)
|
||||||
|
it.setCancellable {
|
||||||
|
cancelable.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> completableBuilder(builder: (callback: MatrixCallback<T>) -> Cancelable): Completable = Completable.create {
|
||||||
|
val callback: MatrixCallback<T> = object : MatrixCallback<T> {
|
||||||
|
override fun onSuccess(data: T) {
|
||||||
|
it.onComplete()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
it.tryOnError(failure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val cancelable = builder(callback)
|
||||||
|
it.setCancellable {
|
||||||
|
cancelable.cancel()
|
||||||
|
}
|
||||||
|
}
|
|
@ -53,13 +53,13 @@ class RxRoom(private val room: Room) {
|
||||||
return room.getMyReadReceiptLive().asObservable()
|
return room.getMyReadReceiptLive().asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadRoomMembersIfNeeded(): Single<Unit> = Single.create {
|
fun loadRoomMembersIfNeeded(): Single<Unit> = singleBuilder {
|
||||||
room.loadRoomMembersIfNeeded(MatrixCallbackSingle(it)).toSingle(it)
|
room.loadRoomMembersIfNeeded(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun joinRoom(reason: String? = null,
|
fun joinRoom(reason: String? = null,
|
||||||
viaServers: List<String> = emptyList()): Single<Unit> = Single.create {
|
viaServers: List<String> = emptyList()): Single<Unit> = singleBuilder {
|
||||||
room.join(reason, viaServers, MatrixCallbackSingle(it)).toSingle(it)
|
room.join(reason, viaServers, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveEventReadReceipts(eventId: String): Observable<List<ReadReceipt>> {
|
fun liveEventReadReceipts(eventId: String): Observable<List<ReadReceipt>> {
|
||||||
|
|
|
@ -66,20 +66,25 @@ class RxSession(private val session: Session) {
|
||||||
return session.livePagedUsers(filter).asObservable()
|
return session.livePagedUsers(filter).asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createRoom(roomParams: CreateRoomParams): Single<String> = Single.create {
|
fun createRoom(roomParams: CreateRoomParams): Single<String> = singleBuilder {
|
||||||
session.createRoom(roomParams, MatrixCallbackSingle(it)).toSingle(it)
|
session.createRoom(roomParams, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun searchUsersDirectory(search: String,
|
fun searchUsersDirectory(search: String,
|
||||||
limit: Int,
|
limit: Int,
|
||||||
excludedUserIds: Set<String>): Single<List<User>> = Single.create {
|
excludedUserIds: Set<String>): Single<List<User>> = singleBuilder {
|
||||||
session.searchUsersDirectory(search, limit, excludedUserIds, MatrixCallbackSingle(it)).toSingle(it)
|
session.searchUsersDirectory(search, limit, excludedUserIds, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun joinRoom(roomId: String,
|
fun joinRoom(roomId: String,
|
||||||
reason: String? = null,
|
reason: String? = null,
|
||||||
viaServers: List<String> = emptyList()): Single<Unit> = Single.create {
|
viaServers: List<String> = emptyList()): Single<Unit> = singleBuilder {
|
||||||
session.joinRoom(roomId, reason, viaServers, MatrixCallbackSingle(it)).toSingle(it)
|
session.joinRoom(roomId, reason, viaServers, it)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRoomIdByAlias(roomAlias: String,
|
||||||
|
searchOnServer: Boolean): Single<Optional<String>> = singleBuilder {
|
||||||
|
session.getRoomIdByAlias(roomAlias, searchOnServer, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,7 @@ import android.net.Uri
|
||||||
*/
|
*/
|
||||||
sealed class PermalinkData {
|
sealed class PermalinkData {
|
||||||
|
|
||||||
data class EventLink(val roomIdOrAlias: String, val eventId: String) : PermalinkData()
|
data class RoomLink(val roomIdOrAlias: String, val isRoomAlias: Boolean, val eventId: String?) : PermalinkData()
|
||||||
|
|
||||||
data class RoomLink(val roomIdOrAlias: String) : PermalinkData()
|
|
||||||
|
|
||||||
data class UserLink(val userId: String) : PermalinkData()
|
data class UserLink(val userId: String) : PermalinkData()
|
||||||
|
|
||||||
|
|
|
@ -63,11 +63,16 @@ object PermalinkParser {
|
||||||
MatrixPatterns.isUserId(identifier) -> PermalinkData.UserLink(userId = identifier)
|
MatrixPatterns.isUserId(identifier) -> PermalinkData.UserLink(userId = identifier)
|
||||||
MatrixPatterns.isGroupId(identifier) -> PermalinkData.GroupLink(groupId = identifier)
|
MatrixPatterns.isGroupId(identifier) -> PermalinkData.GroupLink(groupId = identifier)
|
||||||
MatrixPatterns.isRoomId(identifier) -> {
|
MatrixPatterns.isRoomId(identifier) -> {
|
||||||
if (!extraParameter.isNullOrEmpty() && MatrixPatterns.isEventId(extraParameter)) {
|
val eventId = extraParameter.takeIf {
|
||||||
PermalinkData.EventLink(roomIdOrAlias = identifier, eventId = extraParameter)
|
!it.isNullOrEmpty() && MatrixPatterns.isEventId(it)
|
||||||
} else {
|
|
||||||
PermalinkData.RoomLink(roomIdOrAlias = identifier)
|
|
||||||
}
|
}
|
||||||
|
PermalinkData.RoomLink(roomIdOrAlias = identifier, isRoomAlias = false, eventId = eventId)
|
||||||
|
}
|
||||||
|
MatrixPatterns.isRoomAlias(identifier) -> {
|
||||||
|
val eventId = extraParameter.takeIf {
|
||||||
|
!it.isNullOrEmpty() && MatrixPatterns.isEventId(it)
|
||||||
|
}
|
||||||
|
PermalinkData.RoomLink(roomIdOrAlias = identifier, isRoomAlias = true, eventId = eventId)
|
||||||
}
|
}
|
||||||
else -> PermalinkData.FallbackLink(uri)
|
else -> PermalinkData.FallbackLink(uri)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
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.session.room.model.create.CreateRoomParams
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
import im.vector.matrix.android.api.util.Optional
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface defines methods to get rooms. It's implemented at the session level.
|
* This interface defines methods to get rooms. It's implemented at the session level.
|
||||||
|
@ -74,4 +75,11 @@ interface RoomService {
|
||||||
*/
|
*/
|
||||||
fun markAllAsRead(roomIds: List<String>,
|
fun markAllAsRead(roomIds: List<String>,
|
||||||
callback: MatrixCallback<Unit>): Cancelable
|
callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a room alias to a room ID.
|
||||||
|
*/
|
||||||
|
fun getRoomIdByAlias(roomAlias: String,
|
||||||
|
searchOnServer: Boolean,
|
||||||
|
callback: MatrixCallback<Optional<String>>): Cancelable
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,6 @@ interface MembershipService {
|
||||||
/**
|
/**
|
||||||
* Join the room, or accept an invitation.
|
* Join the room, or accept an invitation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fun join(reason: String? = null,
|
fun join(reason: String? = null,
|
||||||
viaServers: List<String> = emptyList(),
|
viaServers: List<String> = emptyList(),
|
||||||
callback: MatrixCallback<Unit>): Cancelable
|
callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
|
@ -29,6 +29,8 @@ data class RoomSummary(
|
||||||
val displayName: String = "",
|
val displayName: String = "",
|
||||||
val topic: String = "",
|
val topic: String = "",
|
||||||
val avatarUrl: String = "",
|
val avatarUrl: String = "",
|
||||||
|
val canonicalAlias: String? = null,
|
||||||
|
val aliases: List<String> = emptyList(),
|
||||||
val isDirect: Boolean = false,
|
val isDirect: Boolean = false,
|
||||||
val latestPreviewableEvent: TimelineEvent? = null,
|
val latestPreviewableEvent: TimelineEvent? = null,
|
||||||
val otherMemberIds: List<String> = emptyList(),
|
val otherMemberIds: List<String> = emptyList(),
|
||||||
|
|
|
@ -68,7 +68,9 @@ internal class RoomSummaryMapper @Inject constructor(
|
||||||
membership = roomSummaryEntity.membership,
|
membership = roomSummaryEntity.membership,
|
||||||
versioningState = roomSummaryEntity.versioningState,
|
versioningState = roomSummaryEntity.versioningState,
|
||||||
readMarkerId = roomSummaryEntity.readMarkerId,
|
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()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,10 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
|
||||||
var hasUnreadMessages: Boolean = false,
|
var hasUnreadMessages: Boolean = false,
|
||||||
var tags: RealmList<RoomTagEntity> = RealmList(),
|
var tags: RealmList<RoomTagEntity> = RealmList(),
|
||||||
var userDrafts: UserDraftsEntity? = null,
|
var userDrafts: UserDraftsEntity? = null,
|
||||||
var breadcrumbsIndex: Int = NOT_IN_BREADCRUMBS
|
var breadcrumbsIndex: Int = NOT_IN_BREADCRUMBS,
|
||||||
|
var canonicalAlias: String? = null,
|
||||||
|
var aliases: RealmList<String> = RealmList(),
|
||||||
|
var flatAliases: String = ""
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
|
||||||
private var membershipStr: String = Membership.NONE.name
|
private var membershipStr: String = Membership.NONE.name
|
||||||
|
|
|
@ -32,6 +32,18 @@ internal fun RoomSummaryEntity.Companion.where(realm: Realm, roomId: String? = n
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun RoomSummaryEntity.Companion.findByAlias(realm: Realm, roomAlias: String): RoomSummaryEntity? {
|
||||||
|
val roomSummary = realm.where<RoomSummaryEntity>()
|
||||||
|
.equalTo(RoomSummaryEntityFields.CANONICAL_ALIAS, roomAlias)
|
||||||
|
.findFirst()
|
||||||
|
if (roomSummary != null) {
|
||||||
|
return roomSummary
|
||||||
|
}
|
||||||
|
return realm.where<RoomSummaryEntity>()
|
||||||
|
.contains(RoomSummaryEntityFields.FLAT_ALIASES, "|$roomAlias")
|
||||||
|
.findFirst()
|
||||||
|
}
|
||||||
|
|
||||||
internal fun RoomSummaryEntity.Companion.getOrCreate(realm: Realm, roomId: String): RoomSummaryEntity {
|
internal fun RoomSummaryEntity.Companion.getOrCreate(realm: Realm, roomId: String): RoomSummaryEntity {
|
||||||
return where(realm, roomId).findFirst() ?: realm.createObject(roomId)
|
return where(realm, roomId).findFirst() ?: realm.createObject(roomId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,11 +25,13 @@ 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.VersioningState
|
||||||
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
import im.vector.matrix.android.api.util.Optional
|
||||||
import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper
|
import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper
|
||||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
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.RoomSummaryEntity
|
||||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields
|
import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
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.create.CreateRoomTask
|
||||||
import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask
|
import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask
|
||||||
import im.vector.matrix.android.internal.session.room.read.MarkAllRoomsReadTask
|
import im.vector.matrix.android.internal.session.room.read.MarkAllRoomsReadTask
|
||||||
|
@ -45,6 +47,7 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona
|
||||||
private val joinRoomTask: JoinRoomTask,
|
private val joinRoomTask: JoinRoomTask,
|
||||||
private val markAllRoomsReadTask: MarkAllRoomsReadTask,
|
private val markAllRoomsReadTask: MarkAllRoomsReadTask,
|
||||||
private val updateBreadcrumbsTask: UpdateBreadcrumbsTask,
|
private val updateBreadcrumbsTask: UpdateBreadcrumbsTask,
|
||||||
|
private val roomIdByAliasTask: GetRoomIdByAliasTask,
|
||||||
private val roomFactory: RoomFactory,
|
private val roomFactory: RoomFactory,
|
||||||
private val taskExecutor: TaskExecutor) : RoomService {
|
private val taskExecutor: TaskExecutor) : RoomService {
|
||||||
|
|
||||||
|
@ -111,4 +114,12 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona
|
||||||
}
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getRoomIdByAlias(roomAlias: String, searchOnServer: Boolean, callback: MatrixCallback<Optional<String>>): Cancelable {
|
||||||
|
return roomIdByAliasTask
|
||||||
|
.configureWith(GetRoomIdByAliasTask.Params(roomAlias, searchOnServer)) {
|
||||||
|
this.callback = callback
|
||||||
|
}
|
||||||
|
.executeBy(taskExecutor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.Content
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
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.CreateRoomParams
|
||||||
import im.vector.matrix.android.api.session.room.model.create.CreateRoomResponse
|
import im.vector.matrix.android.api.session.room.model.create.CreateRoomResponse
|
||||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams
|
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams
|
||||||
|
@ -258,4 +259,12 @@ internal interface RoomAPI {
|
||||||
fun reportContent(@Path("roomId") roomId: String,
|
fun reportContent(@Path("roomId") roomId: String,
|
||||||
@Path("eventId") eventId: String,
|
@Path("eventId") eventId: String,
|
||||||
@Body body: ReportContentBody): Call<Unit>
|
@Body body: ReportContentBody): Call<Unit>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<RoomAliasDescription>
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.api.session.room.RoomService
|
||||||
import im.vector.matrix.android.internal.session.DefaultFileService
|
import im.vector.matrix.android.internal.session.DefaultFileService
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
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.CreateRoomTask
|
||||||
import im.vector.matrix.android.internal.session.room.create.DefaultCreateRoomTask
|
import im.vector.matrix.android.internal.session.room.create.DefaultCreateRoomTask
|
||||||
import im.vector.matrix.android.internal.session.room.directory.DefaultGetPublicRoomTask
|
import im.vector.matrix.android.internal.session.room.directory.DefaultGetPublicRoomTask
|
||||||
|
@ -133,4 +135,7 @@ internal abstract class RoomModule {
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindFetchEditHistoryTask(fetchEditHistoryTask: DefaultFetchEditHistoryTask): FetchEditHistoryTask
|
abstract fun bindFetchEditHistoryTask(fetchEditHistoryTask: DefaultFetchEditHistoryTask): FetchEditHistoryTask
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindGetRoomIdByAliasTask(getRoomIdByAliasTask: DefaultGetRoomIdByAliasTask): GetRoomIdByAliasTask
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.EventType
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
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.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.api.session.room.model.RoomTopicContent
|
||||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||||
|
@ -91,6 +93,8 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId
|
||||||
|
|
||||||
val latestPreviewableEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true, filterTypes = PREVIEWABLE_TYPES)
|
val latestPreviewableEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true, filterTypes = PREVIEWABLE_TYPES)
|
||||||
val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev()
|
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
|
roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0
|
||||||
// avoid this call if we are sure there are unread events
|
// avoid this call if we are sure there are unread events
|
||||||
|
@ -100,6 +104,13 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId
|
||||||
roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(roomId)
|
roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(roomId)
|
||||||
roomSummaryEntity.topic = ContentMapper.map(lastTopicEvent?.content).toModel<RoomTopicContent>()?.topic
|
roomSummaryEntity.topic = ContentMapper.map(lastTopicEvent?.content).toModel<RoomTopicContent>()?.topic
|
||||||
roomSummaryEntity.latestPreviewableEvent = latestPreviewableEvent
|
roomSummaryEntity.latestPreviewableEvent = latestPreviewableEvent
|
||||||
|
roomSummaryEntity.canonicalAlias = ContentMapper.map(lastCanonicalAliasEvent?.content).toModel<RoomCanonicalAliasContent>()
|
||||||
|
?.canonicalAlias
|
||||||
|
|
||||||
|
val roomAliases = ContentMapper.map(lastAliasesEvent?.content).toModel<RoomAliasesContent>()?.aliases ?: emptyList()
|
||||||
|
roomSummaryEntity.aliases.clear()
|
||||||
|
roomSummaryEntity.aliases.addAll(roomAliases)
|
||||||
|
roomSummaryEntity.flatAliases = roomAliases.joinToString(separator = "|", prefix = "|")
|
||||||
|
|
||||||
if (updateMembers) {
|
if (updateMembers) {
|
||||||
val otherRoomMembers = RoomMembers(realm, roomId)
|
val otherRoomMembers = RoomMembers(realm, roomId)
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* 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.api.util.Optional
|
||||||
|
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<GetRoomIdByAliasTask.Params, Optional<String>> {
|
||||||
|
data class Params(
|
||||||
|
val roomAlias: String,
|
||||||
|
val searchOnServer: Boolean
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DefaultGetRoomIdByAliasTask @Inject constructor(private val monarchy: Monarchy,
|
||||||
|
private val roomAPI: RoomAPI) : GetRoomIdByAliasTask {
|
||||||
|
|
||||||
|
override suspend fun execute(params: GetRoomIdByAliasTask.Params): Optional<String> {
|
||||||
|
var roomId = Realm.getInstance(monarchy.realmConfiguration).use {
|
||||||
|
RoomSummaryEntity.findByAlias(it, params.roomAlias)?.roomId
|
||||||
|
}
|
||||||
|
return if (roomId != null) {
|
||||||
|
Optional.from(roomId)
|
||||||
|
} else if (!params.searchOnServer) {
|
||||||
|
Optional.from<String>(null)
|
||||||
|
} else {
|
||||||
|
roomId = executeRequest<RoomAliasDescription> {
|
||||||
|
apiCall = roomAPI.getRoomIdByAlias(params.roomAlias)
|
||||||
|
}.roomId
|
||||||
|
Optional.from(roomId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,25 +14,20 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package im.vector.matrix.rx
|
package im.vector.matrix.android.internal.session.room.alias
|
||||||
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import com.squareup.moshi.Json
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import com.squareup.moshi.JsonClass
|
||||||
import io.reactivex.SingleEmitter
|
|
||||||
|
|
||||||
internal class MatrixCallbackSingle<T>(private val singleEmitter: SingleEmitter<T>) : MatrixCallback<T> {
|
@JsonClass(generateAdapter = true)
|
||||||
|
internal data class RoomAliasDescription(
|
||||||
|
/**
|
||||||
|
* The room ID for this alias.
|
||||||
|
*/
|
||||||
|
@Json(name = "room_id") val roomId: String,
|
||||||
|
|
||||||
override fun onSuccess(data: T) {
|
/**
|
||||||
singleEmitter.onSuccess(data)
|
* A list of servers that are aware of this room ID.
|
||||||
}
|
*/
|
||||||
|
@Json(name = "servers") val servers: List<String> = emptyList()
|
||||||
override fun onFailure(failure: Throwable) {
|
)
|
||||||
singleEmitter.tryOnError(failure)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> Cancelable.toSingle(singleEmitter: SingleEmitter<T>) {
|
|
||||||
singleEmitter.setCancellable {
|
|
||||||
this.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -65,7 +65,13 @@
|
||||||
<activity android:name=".features.roomdirectory.RoomDirectoryActivity" />
|
<activity android:name=".features.roomdirectory.RoomDirectoryActivity" />
|
||||||
<activity android:name=".features.roomdirectory.roompreview.RoomPreviewActivity" />
|
<activity android:name=".features.roomdirectory.roompreview.RoomPreviewActivity" />
|
||||||
<activity android:name=".features.home.room.filtered.FilteredRoomsActivity" />
|
<activity android:name=".features.home.room.filtered.FilteredRoomsActivity" />
|
||||||
<activity android:name=".features.home.room.detail.RoomDetailActivity" />
|
<activity
|
||||||
|
android:name=".features.home.room.detail.RoomDetailActivity"
|
||||||
|
android:parentActivityName=".features.home.HomeActivity">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
|
android:value=".features.home.HomeActivity" />
|
||||||
|
</activity>
|
||||||
<activity android:name=".features.debug.DebugMenuActivity" />
|
<activity android:name=".features.debug.DebugMenuActivity" />
|
||||||
<activity android:name=".features.home.createdirect.CreateDirectRoomActivity" />
|
<activity android:name=".features.home.createdirect.CreateDirectRoomActivity" />
|
||||||
<activity android:name=".features.webview.VectorWebViewActivity" />
|
<activity android:name=".features.webview.VectorWebViewActivity" />
|
||||||
|
@ -102,6 +108,18 @@
|
||||||
<activity
|
<activity
|
||||||
android:name=".features.signout.soft.SoftLogoutActivity"
|
android:name=".features.signout.soft.SoftLogoutActivity"
|
||||||
android:windowSoftInputMode="adjustResize" />
|
android:windowSoftInputMode="adjustResize" />
|
||||||
|
<activity android:name=".features.permalink.PermalinkHandlerActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="http" />
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="matrix.to" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
<!-- Services -->
|
<!-- Services -->
|
||||||
|
|
||||||
<service
|
<service
|
||||||
|
|
|
@ -41,6 +41,7 @@ import im.vector.riotx.features.login.LoginActivity
|
||||||
import im.vector.riotx.features.media.ImageMediaViewerActivity
|
import im.vector.riotx.features.media.ImageMediaViewerActivity
|
||||||
import im.vector.riotx.features.media.VideoMediaViewerActivity
|
import im.vector.riotx.features.media.VideoMediaViewerActivity
|
||||||
import im.vector.riotx.features.navigation.Navigator
|
import im.vector.riotx.features.navigation.Navigator
|
||||||
|
import im.vector.riotx.features.permalink.PermalinkHandlerActivity
|
||||||
import im.vector.riotx.features.rageshake.BugReportActivity
|
import im.vector.riotx.features.rageshake.BugReportActivity
|
||||||
import im.vector.riotx.features.rageshake.BugReporter
|
import im.vector.riotx.features.rageshake.BugReporter
|
||||||
import im.vector.riotx.features.rageshake.RageShake
|
import im.vector.riotx.features.rageshake.RageShake
|
||||||
|
@ -132,6 +133,8 @@ interface ScreenComponent {
|
||||||
|
|
||||||
fun inject(activity: SoftLogoutActivity)
|
fun inject(activity: SoftLogoutActivity)
|
||||||
|
|
||||||
|
fun inject(permalinkHandlerActivity: PermalinkHandlerActivity)
|
||||||
|
|
||||||
@Component.Factory
|
@Component.Factory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
fun create(vectorComponent: VectorComponent,
|
fun create(vectorComponent: VectorComponent,
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.riotx.features.home
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.net.Uri
|
|
||||||
import im.vector.matrix.android.api.permalinks.PermalinkData
|
|
||||||
import im.vector.matrix.android.api.permalinks.PermalinkParser
|
|
||||||
import im.vector.matrix.android.api.session.Session
|
|
||||||
import im.vector.riotx.features.navigation.Navigator
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class PermalinkHandler @Inject constructor(private val session: Session,
|
|
||||||
private val navigator: Navigator) {
|
|
||||||
|
|
||||||
fun launch(context: Context, deepLink: String?, navigateToRoomInterceptor: NavigateToRoomInterceptor? = null): Boolean {
|
|
||||||
val uri = deepLink?.let { Uri.parse(it) }
|
|
||||||
return launch(context, uri, navigateToRoomInterceptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun launch(context: Context, deepLink: Uri?, navigateToRoomInterceptor: NavigateToRoomInterceptor? = null): Boolean {
|
|
||||||
if (deepLink == null) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return when (val permalinkData = PermalinkParser.parse(deepLink)) {
|
|
||||||
is PermalinkData.EventLink -> {
|
|
||||||
if (navigateToRoomInterceptor?.navToRoom(permalinkData.roomIdOrAlias, permalinkData.eventId) != true) {
|
|
||||||
openRoom(context, permalinkData.roomIdOrAlias, permalinkData.eventId)
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
is PermalinkData.RoomLink -> {
|
|
||||||
if (navigateToRoomInterceptor?.navToRoom(permalinkData.roomIdOrAlias) != true) {
|
|
||||||
openRoom(context, permalinkData.roomIdOrAlias)
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
is PermalinkData.GroupLink -> {
|
|
||||||
navigator.openGroupDetail(permalinkData.groupId, context)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
is PermalinkData.UserLink -> {
|
|
||||||
navigator.openUserDetail(permalinkData.userId, context)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
is PermalinkData.FallbackLink -> {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open room either joined, or not unknown
|
|
||||||
*/
|
|
||||||
private fun openRoom(context: Context, roomIdOrAlias: String, eventId: String? = null) {
|
|
||||||
if (session.getRoom(roomIdOrAlias) != null) {
|
|
||||||
navigator.openRoom(context, roomIdOrAlias, eventId)
|
|
||||||
} else {
|
|
||||||
navigator.openNotJoinedRoom(context, roomIdOrAlias, eventId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NavigateToRoomInterceptor {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the navigation has been intercepted
|
|
||||||
*/
|
|
||||||
fun navToRoom(roomId: String, eventId: String? = null): Boolean
|
|
||||||
}
|
|
|
@ -86,8 +86,8 @@ import im.vector.riotx.features.autocomplete.command.CommandAutocompletePolicy
|
||||||
import im.vector.riotx.features.autocomplete.user.AutocompleteUserPresenter
|
import im.vector.riotx.features.autocomplete.user.AutocompleteUserPresenter
|
||||||
import im.vector.riotx.features.command.Command
|
import im.vector.riotx.features.command.Command
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
import im.vector.riotx.features.home.NavigateToRoomInterceptor
|
import im.vector.riotx.features.permalink.NavigateToRoomInterceptor
|
||||||
import im.vector.riotx.features.home.PermalinkHandler
|
import im.vector.riotx.features.permalink.PermalinkHandler
|
||||||
import im.vector.riotx.features.home.getColorFromUserId
|
import im.vector.riotx.features.home.getColorFromUserId
|
||||||
import im.vector.riotx.features.home.room.detail.composer.TextComposerAction
|
import im.vector.riotx.features.home.room.detail.composer.TextComposerAction
|
||||||
import im.vector.riotx.features.home.room.detail.composer.TextComposerView
|
import im.vector.riotx.features.home.room.detail.composer.TextComposerView
|
||||||
|
@ -113,6 +113,8 @@ import im.vector.riotx.features.reactions.EmojiReactionPickerActivity
|
||||||
import im.vector.riotx.features.settings.VectorPreferences
|
import im.vector.riotx.features.settings.VectorPreferences
|
||||||
import im.vector.riotx.features.share.SharedData
|
import im.vector.riotx.features.share.SharedData
|
||||||
import im.vector.riotx.features.themes.ThemeUtils
|
import im.vector.riotx.features.themes.ThemeUtils
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import kotlinx.android.synthetic.main.fragment_room_detail.*
|
import kotlinx.android.synthetic.main.fragment_room_detail.*
|
||||||
import kotlinx.android.synthetic.main.merge_composer_layout.view.*
|
import kotlinx.android.synthetic.main.merge_composer_layout.view.*
|
||||||
|
@ -851,8 +853,9 @@ class RoomDetailFragment @Inject constructor(
|
||||||
// TimelineEventController.Callback ************************************************************
|
// TimelineEventController.Callback ************************************************************
|
||||||
|
|
||||||
override fun onUrlClicked(url: String): Boolean {
|
override fun onUrlClicked(url: String): Boolean {
|
||||||
val managed = permalinkHandler.launch(requireActivity(), url, object : NavigateToRoomInterceptor {
|
permalinkHandler
|
||||||
override fun navToRoom(roomId: String, eventId: String?): Boolean {
|
.launch(requireActivity(), url, object : NavigateToRoomInterceptor {
|
||||||
|
override fun navToRoom(roomId: String?, eventId: String?): Boolean {
|
||||||
// Same room?
|
// Same room?
|
||||||
if (roomId == roomDetailArgs.roomId) {
|
if (roomId == roomDetailArgs.roomId) {
|
||||||
// Navigation to same room
|
// Navigation to same room
|
||||||
|
@ -864,17 +867,19 @@ class RoomDetailFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not handled
|
// Not handled
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe { managed ->
|
||||||
if (!managed) {
|
if (!managed) {
|
||||||
// Open in external browser, in a new Tab
|
// Open in external browser, in a new Tab
|
||||||
openUrlInExternalBrowser(requireContext(), url)
|
openUrlInExternalBrowser(requireContext(), url)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.disposeOnDestroyView()
|
||||||
// In fact it is always managed
|
// In fact it is always managed
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1022,12 +1027,15 @@ class RoomDetailFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRoomCreateLinkClicked(url: String) {
|
override fun onRoomCreateLinkClicked(url: String) {
|
||||||
permalinkHandler.launch(requireContext(), url, object : NavigateToRoomInterceptor {
|
permalinkHandler
|
||||||
override fun navToRoom(roomId: String, eventId: String?): Boolean {
|
.launch(requireContext(), url, object : NavigateToRoomInterceptor {
|
||||||
|
override fun navToRoom(roomId: String?, eventId: String?): Boolean {
|
||||||
requireActivity().finish()
|
requireActivity().finish()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.subscribe()
|
||||||
|
.disposeOnDestroyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onReadReceiptsClicked(readReceipts: List<ReadReceiptData>) {
|
override fun onReadReceiptsClicked(readReceipts: List<ReadReceiptData>) {
|
||||||
|
|
|
@ -38,15 +38,46 @@ import im.vector.riotx.features.share.SharedData
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
import androidx.core.app.TaskStackBuilder
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class DefaultNavigator @Inject constructor() : Navigator {
|
class DefaultNavigator @Inject constructor() : Navigator {
|
||||||
|
|
||||||
override fun openRoom(context: Context, roomId: String, eventId: String?) {
|
override fun openRoom(context: Context, roomId: String, eventId: String?, buildTask: Boolean) {
|
||||||
val args = RoomDetailArgs(roomId, eventId)
|
val args = RoomDetailArgs(roomId, eventId)
|
||||||
val intent = RoomDetailActivity.newIntent(context, args)
|
val intent = RoomDetailActivity.newIntent(context, args)
|
||||||
|
if (buildTask) {
|
||||||
|
val stackBuilder = TaskStackBuilder.create(context)
|
||||||
|
stackBuilder.addNextIntentWithParentStack(intent)
|
||||||
|
stackBuilder.startActivities()
|
||||||
|
} else {
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun openNotJoinedRoom(context: Context, roomIdOrAlias: String?, eventId: String?, buildTask: Boolean) {
|
||||||
|
if (context is VectorBaseActivity) {
|
||||||
|
context.notImplemented("Open not joined room")
|
||||||
|
} else {
|
||||||
|
context.toast(R.string.not_implemented)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun openGroupDetail(groupId: String, context: Context, buildTask: Boolean) {
|
||||||
|
if (context is VectorBaseActivity) {
|
||||||
|
context.notImplemented("Open group detail")
|
||||||
|
} else {
|
||||||
|
context.toast(R.string.not_implemented)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun openUserDetail(userId: String, context: Context, buildTask: Boolean) {
|
||||||
|
if (context is VectorBaseActivity) {
|
||||||
|
context.notImplemented("Open user detail")
|
||||||
|
} else {
|
||||||
|
context.toast(R.string.not_implemented)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun openRoomForSharing(activity: Activity, roomId: String, sharedData: SharedData) {
|
override fun openRoomForSharing(activity: Activity, roomId: String, sharedData: SharedData) {
|
||||||
val args = RoomDetailArgs(roomId, null, sharedData)
|
val args = RoomDetailArgs(roomId, null, sharedData)
|
||||||
|
@ -55,14 +86,6 @@ class DefaultNavigator @Inject constructor() : Navigator {
|
||||||
activity.finish()
|
activity.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openNotJoinedRoom(context: Context, roomIdOrAlias: String, eventId: String?) {
|
|
||||||
if (context is VectorBaseActivity) {
|
|
||||||
context.notImplemented("Open not joined room")
|
|
||||||
} else {
|
|
||||||
context.toast(R.string.not_implemented)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun openRoomPreview(publicRoom: PublicRoom, context: Context) {
|
override fun openRoomPreview(publicRoom: PublicRoom, context: Context) {
|
||||||
val intent = RoomPreviewActivity.getIntent(context, publicRoom)
|
val intent = RoomPreviewActivity.getIntent(context, publicRoom)
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
|
@ -105,14 +128,6 @@ class DefaultNavigator @Inject constructor() : Navigator {
|
||||||
context.startActivity(KeysBackupManageActivity.intent(context))
|
context.startActivity(KeysBackupManageActivity.intent(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openGroupDetail(groupId: String, context: Context) {
|
|
||||||
Timber.v("Open group detail $groupId")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun openUserDetail(userId: String, context: Context) {
|
|
||||||
Timber.v("Open user detail $userId")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun openRoomSettings(context: Context, roomId: String) {
|
override fun openRoomSettings(context: Context, roomId: String) {
|
||||||
Timber.v("Open room settings$roomId")
|
Timber.v("Open room settings$roomId")
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,11 @@ import im.vector.riotx.features.share.SharedData
|
||||||
|
|
||||||
interface Navigator {
|
interface Navigator {
|
||||||
|
|
||||||
fun openRoom(context: Context, roomId: String, eventId: String? = null)
|
fun openRoom(context: Context, roomId: String, eventId: String? = null, buildTask: Boolean = false)
|
||||||
|
|
||||||
fun openRoomForSharing(activity: Activity, roomId: String, sharedData: SharedData)
|
fun openRoomForSharing(activity: Activity, roomId: String, sharedData: SharedData)
|
||||||
|
|
||||||
fun openNotJoinedRoom(context: Context, roomIdOrAlias: String, eventId: String? = null)
|
fun openNotJoinedRoom(context: Context, roomIdOrAlias: String?, eventId: String? = null, buildTask: Boolean = false)
|
||||||
|
|
||||||
fun openRoomPreview(publicRoom: PublicRoom, context: Context)
|
fun openRoomPreview(publicRoom: PublicRoom, context: Context)
|
||||||
|
|
||||||
|
@ -47,9 +47,9 @@ interface Navigator {
|
||||||
|
|
||||||
fun openKeysBackupManager(context: Context)
|
fun openKeysBackupManager(context: Context)
|
||||||
|
|
||||||
fun openGroupDetail(groupId: String, context: Context)
|
fun openGroupDetail(groupId: String, context: Context, buildTask: Boolean = false)
|
||||||
|
|
||||||
fun openUserDetail(userId: String, context: Context)
|
fun openUserDetail(userId: String, context: Context, buildTask: Boolean = false)
|
||||||
|
|
||||||
fun openRoomSettings(context: Context, roomId: String)
|
fun openRoomSettings(context: Context, roomId: String)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* 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.riotx.features.permalink
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
import im.vector.matrix.android.api.permalinks.PermalinkData
|
||||||
|
import im.vector.matrix.android.api.permalinks.PermalinkParser
|
||||||
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.matrix.android.api.util.Optional
|
||||||
|
import im.vector.matrix.rx.rx
|
||||||
|
import im.vector.riotx.features.navigation.Navigator
|
||||||
|
import io.reactivex.Single
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class PermalinkHandler @Inject constructor(private val session: Session,
|
||||||
|
private val navigator: Navigator) {
|
||||||
|
|
||||||
|
fun launch(
|
||||||
|
context: Context,
|
||||||
|
deepLink: String?,
|
||||||
|
navigateToRoomInterceptor: NavigateToRoomInterceptor? = null,
|
||||||
|
buildTask: Boolean = false
|
||||||
|
): Single<Boolean> {
|
||||||
|
val uri = deepLink?.let { Uri.parse(it) }
|
||||||
|
return launch(context, uri, navigateToRoomInterceptor, buildTask)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun launch(
|
||||||
|
context: Context,
|
||||||
|
deepLink: Uri?,
|
||||||
|
navigateToRoomInterceptor: NavigateToRoomInterceptor? = null,
|
||||||
|
buildTask: Boolean = false
|
||||||
|
): Single<Boolean> {
|
||||||
|
if (deepLink == null) {
|
||||||
|
return Single.just(false)
|
||||||
|
}
|
||||||
|
return when (val permalinkData = PermalinkParser.parse(deepLink)) {
|
||||||
|
is PermalinkData.RoomLink -> {
|
||||||
|
permalinkData.getRoomId()
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.map {
|
||||||
|
val roomId = it.getOrNull()
|
||||||
|
if (navigateToRoomInterceptor?.navToRoom(roomId) != true) {
|
||||||
|
openRoom(context, roomId, permalinkData.eventId, buildTask)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PermalinkData.GroupLink -> {
|
||||||
|
navigator.openGroupDetail(permalinkData.groupId, context, buildTask)
|
||||||
|
Single.just(true)
|
||||||
|
}
|
||||||
|
is PermalinkData.UserLink -> {
|
||||||
|
navigator.openUserDetail(permalinkData.userId, context, buildTask)
|
||||||
|
Single.just(true)
|
||||||
|
}
|
||||||
|
is PermalinkData.FallbackLink -> {
|
||||||
|
Single.just(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PermalinkData.RoomLink.getRoomId(): Single<Optional<String>> {
|
||||||
|
return if (isRoomAlias) {
|
||||||
|
// At the moment we are not fetching on the server as we don't handle not join room
|
||||||
|
session.rx().getRoomIdByAlias(roomIdOrAlias, false).subscribeOn(Schedulers.io())
|
||||||
|
} else {
|
||||||
|
Single.just(Optional.from(roomIdOrAlias))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open room either joined, or not unknown
|
||||||
|
*/
|
||||||
|
private fun openRoom(context: Context, roomId: String?, eventId: String? = null, buildTask: Boolean) {
|
||||||
|
return if (roomId != null && session.getRoom(roomId) != null) {
|
||||||
|
navigator.openRoom(context, roomId, eventId, buildTask)
|
||||||
|
} else {
|
||||||
|
navigator.openNotJoinedRoom(context, roomId, eventId, buildTask)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NavigateToRoomInterceptor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the navigation has been intercepted
|
||||||
|
*/
|
||||||
|
fun navToRoom(roomId: String?, eventId: String? = null): Boolean
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* 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.riotx.features.permalink
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.di.ActiveSessionHolder
|
||||||
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
|
import im.vector.riotx.core.extensions.replaceFragment
|
||||||
|
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||||
|
import im.vector.riotx.core.utils.toast
|
||||||
|
import im.vector.riotx.features.home.LoadingFragment
|
||||||
|
import im.vector.riotx.features.login.LoginActivity
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import kotlinx.android.synthetic.debug.activity_test_material_theme.*
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class PermalinkHandlerActivity : VectorBaseActivity() {
|
||||||
|
|
||||||
|
@Inject lateinit var permalinkHandler: PermalinkHandler
|
||||||
|
@Inject lateinit var sessionHolder: ActiveSessionHolder
|
||||||
|
|
||||||
|
override fun injectWith(injector: ScreenComponent) {
|
||||||
|
injector.inject(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_simple)
|
||||||
|
if (isFirstCreation()) {
|
||||||
|
replaceFragment(R.id.simpleFragmentContainer, LoadingFragment::class.java)
|
||||||
|
}
|
||||||
|
// If we are not logged in, open login screen.
|
||||||
|
// In the future, we might want to relaunch the process after login.
|
||||||
|
if (!sessionHolder.hasActiveSession()) {
|
||||||
|
startLoginActivity()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val uri = intent.dataString
|
||||||
|
permalinkHandler.launch(this, uri, buildTask = true)
|
||||||
|
.delay(500, TimeUnit.MILLISECONDS)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe { isHandled ->
|
||||||
|
if (!isHandled) {
|
||||||
|
toast(R.string.permalink_malformed)
|
||||||
|
}
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
.disposeOnDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startLoginActivity() {
|
||||||
|
val intent = LoginActivity.newIntent(this, null)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
startActivity(intent)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,8 +27,6 @@ import im.vector.riotx.core.dialogs.withColoredButton
|
||||||
import im.vector.riotx.core.extensions.cleanup
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
import im.vector.riotx.core.extensions.configureWith
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.extensions.hideKeyboard
|
import im.vector.riotx.core.extensions.hideKeyboard
|
||||||
import im.vector.riotx.features.MainActivity
|
|
||||||
import im.vector.riotx.features.MainActivityArgs
|
|
||||||
import im.vector.riotx.features.login.AbstractLoginFragment
|
import im.vector.riotx.features.login.AbstractLoginFragment
|
||||||
import im.vector.riotx.features.login.LoginAction
|
import im.vector.riotx.features.login.LoginAction
|
||||||
import im.vector.riotx.features.login.LoginMode
|
import im.vector.riotx.features.login.LoginMode
|
||||||
|
|
|
@ -161,4 +161,5 @@
|
||||||
<string name="soft_logout_clear_data_dialog_submit">Clear data</string>
|
<string name="soft_logout_clear_data_dialog_submit">Clear data</string>
|
||||||
<string name="soft_logout_sso_not_same_user_error">The current session is for user %1$s and you provide credentials for user %2$s. This is not supported by RiotX.\nPlease first clear data, then sign in again on another account.</string>
|
<string name="soft_logout_sso_not_same_user_error">The current session is for user %1$s and you provide credentials for user %2$s. This is not supported by RiotX.\nPlease first clear data, then sign in again on another account.</string>
|
||||||
|
|
||||||
|
<string name="permalink_malformed">Your matrix.to link was malformed</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue