From 14502573bf1f1a667d93612833576953571c038f Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 12 Jan 2021 20:10:52 +0100 Subject: [PATCH] DialPad: integrate getThirdPartyUser with protocol and add helper for create DM --- .../session/thirdparty/ThirdPartyService.kt | 8 +++ .../thirdparty/model/ThirdPartyUser.kt | 37 ++++++++++++ .../thirdparty/DefaultThirdPartyService.kt | 11 +++- .../thirdparty/GetThirdPartyUserTask.kt | 43 ++++++++++++++ .../session/thirdparty/ThirdPartyAPI.kt | 16 +++++- .../session/thirdparty/ThirdPartyModule.kt | 3 + .../vector/app/core/error/ErrorFormatter.kt | 6 +- .../call/dialpad/CallDialPadBottomSheet.kt | 22 +++++++- .../features/call/dialpad/DialpadLookup.kt | 43 ++++++++++++++ .../features/call/webrtc/WebRtcCallManager.kt | 9 ++- .../features/createdirect/DirectRoomHelper.kt | 56 +++++++++++++++++++ .../home/room/detail/RoomDetailAction.kt | 2 + .../home/room/detail/RoomDetailViewModel.kt | 34 +++++++---- .../matrixto/MatrixToBottomSheetViewModel.kt | 43 +++++--------- .../usercode/UserCodeSharedViewModel.kt | 44 ++++----------- vector/src/main/res/values/strings.xml | 3 + 16 files changed, 298 insertions(+), 82 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/model/ThirdPartyUser.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/GetThirdPartyUserTask.kt create mode 100644 vector/src/main/java/im/vector/app/features/call/dialpad/DialpadLookup.kt create mode 100644 vector/src/main/java/im/vector/app/features/createdirect/DirectRoomHelper.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/ThirdPartyService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/ThirdPartyService.kt index 8b590b0cf6..11d5bbcfe6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/ThirdPartyService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/ThirdPartyService.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.api.session.thirdparty import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol +import org.matrix.android.sdk.api.session.thirdparty.model.ThirdPartyUser import org.matrix.android.sdk.api.util.Cancelable interface ThirdPartyService { @@ -28,4 +29,11 @@ interface ThirdPartyService { */ suspend fun getThirdPartyProtocols(): Map + /** + * Retrieve a Matrix User ID linked to a user on the third party service, given a set of user parameters. + * @param protocol Required. The name of the protocol. + * @param fields One or more custom fields that are passed to the AS to help identify the user. + */ + suspend fun getThirdPartyUser(protocol: String, fields: Map = emptyMap()): List + } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/model/ThirdPartyUser.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/model/ThirdPartyUser.kt new file mode 100644 index 0000000000..38870e60d6 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/thirdparty/model/ThirdPartyUser.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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 org.matrix.android.sdk.api.session.thirdparty.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.api.util.JsonDict + +@JsonClass(generateAdapter = true) +data class ThirdPartyUser( + /* + Required. A Matrix User ID represting a third party user. + */ + @Json(name = "userid") val userId: String, + /* + Required. The protocol ID that the third party location is a part of. + */ + @Json(name = "protocol") val protocol: String, + /* + Required. Information used to identify this third party location. + */ + @Json(name = "fields") val fields: JsonDict +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/DefaultThirdPartyService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/DefaultThirdPartyService.kt index 0a9e48a2a5..381ffd8589 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/DefaultThirdPartyService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/DefaultThirdPartyService.kt @@ -18,13 +18,22 @@ package org.matrix.android.sdk.internal.session.thirdparty import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol import org.matrix.android.sdk.api.session.thirdparty.ThirdPartyService +import org.matrix.android.sdk.api.session.thirdparty.model.ThirdPartyUser import javax.inject.Inject -internal class DefaultThirdPartyService @Inject constructor(private val getThirdPartyProtocolTask: GetThirdPartyProtocolsTask) +internal class DefaultThirdPartyService @Inject constructor(private val getThirdPartyProtocolTask: GetThirdPartyProtocolsTask, + private val getThirdPartyUserTask: GetThirdPartyUserTask) : ThirdPartyService { override suspend fun getThirdPartyProtocols(): Map { return getThirdPartyProtocolTask.execute(Unit) } + override suspend fun getThirdPartyUser(protocol: String, fields: Map): List { + val taskParams = GetThirdPartyUserTask.Params( + protocol = protocol, + fields = fields + ) + return getThirdPartyUserTask.execute(taskParams) + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/GetThirdPartyUserTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/GetThirdPartyUserTask.kt new file mode 100644 index 0000000000..aa6a46938f --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/GetThirdPartyUserTask.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.internal.session.thirdparty + +import org.greenrobot.eventbus.EventBus +import org.matrix.android.sdk.api.session.thirdparty.model.ThirdPartyUser +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal interface GetThirdPartyUserTask : Task> { + + data class Params( + val protocol: String, + val fields: Map = emptyMap() + ) +} + +internal class DefaultGetThirdPartyUserTask @Inject constructor( + private val thirdPartyAPI: ThirdPartyAPI, + private val eventBus: EventBus +) : GetThirdPartyUserTask { + + override suspend fun execute(params: GetThirdPartyUserTask.Params): List { + return executeRequest(eventBus) { + apiCall = thirdPartyAPI.getThirdPartyUser(params.protocol, params.fields) + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/ThirdPartyAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/ThirdPartyAPI.kt index 626855d097..2e25909f05 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/ThirdPartyAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/ThirdPartyAPI.kt @@ -21,6 +21,7 @@ import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol +import org.matrix.android.sdk.api.session.thirdparty.model.ThirdPartyUser import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.internal.network.NetworkConstants import org.matrix.android.sdk.internal.session.room.alias.GetAliasesResponse @@ -47,15 +48,28 @@ import retrofit2.http.POST import retrofit2.http.PUT import retrofit2.http.Path import retrofit2.http.Query +import retrofit2.http.QueryMap internal interface ThirdPartyAPI { /** * Get the third party server protocols. * - * Ref: https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-thirdparty-protocols + * Ref: https://matrix.org/docs/spec/client_server/r0.6.1.html#get-matrix-client-r0-thirdparty-protocols */ @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "thirdparty/protocols") fun thirdPartyProtocols(): Call> + + /** + * Retrieve a Matrix User ID linked to a user on the third party service, given a set of user parameters. + * + * Ref: https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-thirdparty-user-protocol + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "thirdparty/protocols/user/{protocol}") + fun getThirdPartyUser(@Path("protocol") protocol: String, @QueryMap params: Map?): Call> + + + + } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/ThirdPartyModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/ThirdPartyModule.kt index 670dd234f4..bf6e981158 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/ThirdPartyModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/ThirdPartyModule.kt @@ -42,4 +42,7 @@ internal abstract class ThirdPartyModule { @Binds abstract fun bindGetThirdPartyProtocolsTask(task: DefaultGetThirdPartyProtocolsTask): GetThirdPartyProtocolsTask + @Binds + abstract fun bindGetThirdPartyUserTask(task: DefaultGetThirdPartyUserTask): GetThirdPartyUserTask + } diff --git a/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt b/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt index b9bc935890..8fa027c4fd 100644 --- a/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt +++ b/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt @@ -18,6 +18,7 @@ package im.vector.app.core.error import im.vector.app.R import im.vector.app.core.resources.StringProvider +import im.vector.app.features.call.dialpad.DialPadLookup import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.MatrixError import org.matrix.android.sdk.api.failure.isInvalidPassword @@ -109,8 +110,9 @@ class DefaultErrorFormatter @Inject constructor( throwable.localizedMessage } } - is SsoFlowNotSupportedYet -> stringProvider.getString(R.string.error_sso_flow_not_supported_yet) - else -> throwable.localizedMessage + is SsoFlowNotSupportedYet -> stringProvider.getString(R.string.error_sso_flow_not_supported_yet) + is DialPadLookup.Failure -> stringProvider.getString(R.string.call_dial_pad_lookup_error) + else -> throwable.localizedMessage } ?: stringProvider.getString(R.string.unknown_error) } diff --git a/vector/src/main/java/im/vector/app/features/call/dialpad/CallDialPadBottomSheet.kt b/vector/src/main/java/im/vector/app/features/call/dialpad/CallDialPadBottomSheet.kt index 4fd5e54f66..996b6b6a63 100644 --- a/vector/src/main/java/im/vector/app/features/call/dialpad/CallDialPadBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/call/dialpad/CallDialPadBottomSheet.kt @@ -63,7 +63,7 @@ class CallDialPadBottomSheet private constructor() : VectorBaseBottomSheetDialog putBoolean(DialPadFragment.EXTRA_ENABLE_OK, showActions) putString(DialPadFragment.EXTRA_REGION_CODE, VectorLocale.applicationLocale.country) } - callback = this@CallDialPadBottomSheet.callback + callback = DialPadFragmentCallbackWrapper(this@CallDialPadBottomSheet.callback) }.also { addChildFragment(R.id.callDialPadFragmentContainer, it) } @@ -83,6 +83,24 @@ class CallDialPadBottomSheet private constructor() : VectorBaseBottomSheetDialog private fun setCallbackToFragment(callback: DialPadFragment.Callback?) { if (!isAdded) return val dialPadFragment = childFragmentManager.findFragmentById(R.id.callDialPadFragmentContainer) as? DialPadFragment - dialPadFragment?.callback = callback + dialPadFragment?.callback = DialPadFragmentCallbackWrapper(callback) } + + private inner class DialPadFragmentCallbackWrapper(val callback: DialPadFragment.Callback?): DialPadFragment.Callback{ + + override fun onDigitAppended(digit: String) { + callback?.onDigitAppended(digit) + } + + override fun onOkClicked(formatted: String?, raw: String?) { + callback?.onOkClicked(formatted, raw) + dismiss() + } + + + } + } + + + diff --git a/vector/src/main/java/im/vector/app/features/call/dialpad/DialpadLookup.kt b/vector/src/main/java/im/vector/app/features/call/dialpad/DialpadLookup.kt new file mode 100644 index 0000000000..c48d0cfb5b --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/call/dialpad/DialpadLookup.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 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.app.features.call.dialpad + +import im.vector.app.features.call.webrtc.WebRtcCallManager +import im.vector.app.features.createdirect.DirectRoomHelper +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.session.Session + +class DialPadLookup(val session: Session, + val directRoomHelper: DirectRoomHelper, + val callManager: WebRtcCallManager +) { + + class Failure : Throwable() + data class Result(val userId: String, val roomId: String) + + suspend fun lookupPhoneNumber(phoneNumber: String): Result? { + val supportedProtocolKey = callManager.supportedPSTNProtocol ?: return null + val thirdPartyUser = tryOrNull { + session.getThirdPartyUser(supportedProtocolKey, fields = mapOf( + "m.id.phone" to phoneNumber + )).firstOrNull() + } ?: throw Failure() + + val roomId = directRoomHelper.ensureDMExists(thirdPartyUser.userId) + return Result(userId = thirdPartyUser.userId, roomId = roomId) + } +} diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt index 870e9d82f8..92caba579d 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt @@ -100,17 +100,20 @@ class WebRtcCallManager @Inject constructor( private var peerConnectionFactory: PeerConnectionFactory? = null private val executor = Executors.newSingleThreadExecutor() private val dispatcher = executor.asCoroutineDispatcher() - var supportsPSTNProtocol: Boolean = false + var supportedPSTNProtocol: String? = null private set + val supportsPSTNProtocol: Boolean + get() = supportedPSTNProtocol != null + private val rootEglBase by lazy { EglUtils.rootEglBase } private var isInBackground: Boolean = true init { GlobalScope.launch { - supportsPSTNProtocol = currentSession?.getSupportedPSTN(3) != null - if (supportsPSTNProtocol) { + supportedPSTNProtocol = currentSession?.getSupportedPSTN(3) + if (supportedPSTNProtocol != null) { pstnSupportListeners.forEach { it.onPSTNSupportUpdated() } } } diff --git a/vector/src/main/java/im/vector/app/features/createdirect/DirectRoomHelper.kt b/vector/src/main/java/im/vector/app/features/createdirect/DirectRoomHelper.kt new file mode 100644 index 0000000000..b32efbfea3 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/createdirect/DirectRoomHelper.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 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.app.features.createdirect + +import im.vector.app.features.raw.wellknown.getElementWellknown +import im.vector.app.features.raw.wellknown.isE2EByDefault +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.raw.RawService +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams +import org.matrix.android.sdk.internal.util.awaitCallback +import java.lang.IllegalStateException +import javax.inject.Inject + +class DirectRoomHelper @Inject constructor( + private val rawService: RawService, + private val session: Session +) { + + suspend fun ensureDMExists(userId: String): String { + val existingRoomId = tryOrNull { session.getExistingDirectRoomWithUser(userId) } + val roomId: String + if (existingRoomId != null) { + roomId = existingRoomId + } else { + val adminE2EByDefault = rawService.getElementWellknown(session.myUserId) + ?.isE2EByDefault() + ?: true + + val roomParams = CreateRoomParams().apply { + invitedUserIds.add(userId) + setDirectMessage() + enableEncryptionIfInvitedUsersSupportIt = adminE2EByDefault + } + roomId = awaitCallback { + session.createRoom(roomParams, it) + } + } + return roomId + } +} + diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt index 9bf8b9c310..98ad6c454c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt @@ -72,6 +72,8 @@ sealed class RoomDetailAction : VectorViewModelAction { data class IgnoreUser(val userId: String?) : RoomDetailAction() object ResendAll : RoomDetailAction() + + data class StartCallWithPhoneNumber(val phoneNumber: String, val videoCall: Boolean): RoomDetailAction() data class StartCall(val isVideo: Boolean) : RoomDetailAction() data class AcceptCall(val callId: String): RoomDetailAction() object EndCall : RoomDetailAction() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index a8cd200814..bb23ec08be 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -32,9 +32,11 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.subscribeLogError +import im.vector.app.features.call.dialpad.DialPadLookup import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.command.CommandParser import im.vector.app.features.command.ParsedCommand +import im.vector.app.features.createdirect.DirectRoomHelper import im.vector.app.features.crypto.verification.SupportedVerificationMethodsProvider import im.vector.app.features.home.room.detail.composer.rainbow.RainbowGenerator import im.vector.app.features.home.room.detail.sticker.StickerPickerActionHandler @@ -115,6 +117,7 @@ class RoomDetailViewModel @AssistedInject constructor( private val typingHelper: TypingHelper, private val callManager: WebRtcCallManager, private val chatEffectManager: ChatEffectManager, + private val directRoomHelper: DirectRoomHelper, timelineSettingsFactory: TimelineSettingsFactory ) : VectorViewModel(initialState), Timeline.Listener, ChatEffectManager.Delegate { @@ -264,6 +267,7 @@ class RoomDetailViewModel @AssistedInject constructor( is RoomDetailAction.TapOnFailedToDecrypt -> handleTapOnFailedToDecrypt(action) is RoomDetailAction.SelectStickerAttachment -> handleSelectStickerAttachment() is RoomDetailAction.OpenIntegrationManager -> handleOpenIntegrationManager() + is RoomDetailAction.StartCallWithPhoneNumber -> handleStartCallWithPhoneNumber(action) is RoomDetailAction.StartCall -> handleStartCall(action) is RoomDetailAction.AcceptCall -> handleAcceptCall(action) is RoomDetailAction.EndCall -> handleEndCall() @@ -287,6 +291,17 @@ class RoomDetailViewModel @AssistedInject constructor( }.exhaustive } + private fun handleStartCallWithPhoneNumber(action: RoomDetailAction.StartCallWithPhoneNumber) { + viewModelScope.launch { + try { + val result = DialPadLookup(session, directRoomHelper, callManager).lookupPhoneNumber(action.phoneNumber) ?: return@launch + callManager.startOutgoingCall(result.roomId, result.userId, action.videoCall) + } catch (failure: Throwable) { + _viewEvents.post(RoomDetailViewEvents.ActionFailure(action, failure)) + } + } + } + private fun handleAcceptCall(action: RoomDetailAction.AcceptCall) { callManager.getCallById(action.callId)?.also { _viewEvents.post(RoomDetailViewEvents.DisplayAndAcceptCall(it)) @@ -317,18 +332,15 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun handleOpenOrCreateDm(action: RoomDetailAction.OpenOrCreateDm) { - val existingDmRoomId = session.getExistingDirectRoomWithUser(action.userId) - if (existingDmRoomId == null) { - // First create a direct room - viewModelScope.launch(Dispatchers.IO) { - val roomId = awaitCallback { - session.createDirectRoom(action.userId, it) - } - _viewEvents.post(RoomDetailViewEvents.OpenRoom(roomId)) + viewModelScope.launch { + val roomId = try { + directRoomHelper.ensureDMExists(action.userId) + } catch (failure: Throwable) { + _viewEvents.post(RoomDetailViewEvents.ActionFailure(action, failure)) + return@launch } - } else { - if (existingDmRoomId != initialState.roomId) { - _viewEvents.post(RoomDetailViewEvents.OpenRoom(existingDmRoomId)) + if (roomId != initialState.roomId) { + _viewEvents.post(RoomDetailViewEvents.OpenRoom(roomId = roomId)) } } } diff --git a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt index 6e8a530c9a..180a71d822 100644 --- a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt @@ -30,6 +30,7 @@ import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import im.vector.app.features.createdirect.DirectRoomHelper import im.vector.app.features.raw.wellknown.getElementWellknown import im.vector.app.features.raw.wellknown.isE2EByDefault import kotlinx.coroutines.Dispatchers @@ -48,6 +49,7 @@ class MatrixToBottomSheetViewModel @AssistedInject constructor( @Assisted initialState: MatrixToBottomSheetState, private val session: Session, private val stringProvider: StringProvider, + private val directRoomHelper: DirectRoomHelper, private val rawService: RawService) : VectorViewModel(initialState) { @AssistedInject.Factory @@ -125,42 +127,23 @@ class MatrixToBottomSheetViewModel @AssistedInject constructor( } private fun handleStartChatting(action: MatrixToAction.StartChattingWithUser) { - val mxId = action.matrixItem.id - val existing = session.getExistingDirectRoomWithUser(mxId) - if (existing != null) { - // navigate to this room - _viewEvents.post(MatrixToViewEvents.NavigateToRoom(existing)) - } else { + viewModelScope.launch { setState { copy(startChattingState = Loading()) } - // we should create the room then navigate - viewModelScope.launch(Dispatchers.IO) { - val adminE2EByDefault = rawService.getElementWellknown(session.myUserId) - ?.isE2EByDefault() - ?: true - - val roomParams = CreateRoomParams() - .apply { - invitedUserIds.add(mxId) - setDirectMessage() - enableEncryptionIfInvitedUsersSupportIt = adminE2EByDefault - } - - val roomId = try { - awaitCallback { session.createRoom(roomParams, it) } - } catch (failure: Throwable) { - setState { - copy(startChattingState = Fail(Exception(stringProvider.getString(R.string.invite_users_to_room_failure)))) - } - return@launch - } + val roomId = try { + directRoomHelper.ensureDMExists(action.matrixItem.id) + } catch (failure: Throwable) { setState { - // we can hide this button has we will navigate out - copy(startChattingState = Uninitialized) + copy(startChattingState = Fail(Exception(stringProvider.getString(R.string.invite_users_to_room_failure)))) } - _viewEvents.post(MatrixToViewEvents.NavigateToRoom(roomId)) + return@launch } + setState { + // we can hide this button has we will navigate out + copy(startChattingState = Uninitialized) + } + _viewEvents.post(MatrixToViewEvents.NavigateToRoom(roomId)) } } } diff --git a/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt index 45b6f0ee65..51871a58ec 100644 --- a/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt @@ -26,8 +26,7 @@ import com.squareup.inject.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.features.raw.wellknown.getElementWellknown -import im.vector.app.features.raw.wellknown.isE2EByDefault +import im.vector.app.features.createdirect.DirectRoomHelper import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull @@ -35,7 +34,6 @@ import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.permalinks.PermalinkData import org.matrix.android.sdk.api.session.permalinks.PermalinkParser -import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.internal.util.awaitCallback @@ -44,6 +42,7 @@ class UserCodeSharedViewModel @AssistedInject constructor( @Assisted val initialState: UserCodeState, private val session: Session, private val stringProvider: StringProvider, + private val directRoomHelper: DirectRoomHelper, private val rawService: RawService) : VectorViewModel(initialState) { companion object : MvRxViewModelFactory { @@ -95,39 +94,20 @@ class UserCodeSharedViewModel @AssistedInject constructor( private fun handleStartChatting(withUser: UserCodeActions.StartChattingWithUser) { val mxId = withUser.matrixItem.id - val existing = session.getExistingDirectRoomWithUser(mxId) setState { copy(mode = UserCodeState.Mode.SHOW) } - if (existing != null) { - // navigate to this room - _viewEvents.post(UserCodeShareViewEvents.NavigateToRoom(existing)) - } else { - // we should create the room then navigate - _viewEvents.post(UserCodeShareViewEvents.ShowWaitingScreen) - viewModelScope.launch(Dispatchers.IO) { - val adminE2EByDefault = rawService.getElementWellknown(session.myUserId) - ?.isE2EByDefault() - ?: true - - val roomParams = CreateRoomParams() - .apply { - invitedUserIds.add(mxId) - setDirectMessage() - enableEncryptionIfInvitedUsersSupportIt = adminE2EByDefault - } - - val roomId = - try { - awaitCallback { session.createRoom(roomParams, it) } - } catch (failure: Throwable) { - _viewEvents.post(UserCodeShareViewEvents.ToastMessage(stringProvider.getString(R.string.invite_users_to_room_failure))) - return@launch - } finally { - _viewEvents.post(UserCodeShareViewEvents.HideWaitingScreen) - } - _viewEvents.post(UserCodeShareViewEvents.NavigateToRoom(roomId)) + _viewEvents.post(UserCodeShareViewEvents.ShowWaitingScreen) + viewModelScope.launch(Dispatchers.IO) { + val roomId = try { + directRoomHelper.ensureDMExists(mxId) + } catch (failure: Throwable) { + _viewEvents.post(UserCodeShareViewEvents.ToastMessage(stringProvider.getString(R.string.invite_users_to_room_failure))) + return@launch + } finally { + _viewEvents.post(UserCodeShareViewEvents.HideWaitingScreen) } + _viewEvents.post(UserCodeShareViewEvents.NavigateToRoom(roomId)) } } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 7e4e813e6c..0a06db9cf7 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2774,4 +2774,7 @@ This call has ended Call back Dial pad + "There was an error looking up the phone number" + +