diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallSignalingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallSignalingService.kt index c6bdcd19c7..dc67aa536a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallSignalingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallSignalingService.kt @@ -16,12 +16,11 @@ package org.matrix.android.sdk.api.session.call -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable - interface CallSignalingService { - fun getTurnServer(callback: MatrixCallback): Cancelable + suspend fun getTurnServer(): TurnServerResponse + + fun getPSTNProtocolChecker(): PSTNProtocolChecker /** * Create an outgoing call diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/PSTNProtocolChecker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/PSTNProtocolChecker.kt new file mode 100644 index 0000000000..41486fad5a --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/PSTNProtocolChecker.kt @@ -0,0 +1,98 @@ +/* + * 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.call + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol +import org.matrix.android.sdk.internal.session.SessionScope +import org.matrix.android.sdk.internal.session.thirdparty.GetThirdPartyProtocolsTask +import org.matrix.android.sdk.internal.task.TaskExecutor +import timber.log.Timber +import java.util.concurrent.atomic.AtomicBoolean +import javax.inject.Inject + +private const val PSTN_VECTOR_KEY = "im.vector.protocol.pstn" +private const val PSTN_MATRIX_KEY = "m.protocol.pstn" + +/** + * This class is responsible for checking if the HS support the PSTN protocol. + * As long as the request succeed, it'll check only once by session. + */ +@SessionScope +class PSTNProtocolChecker @Inject internal constructor(private val taskExecutor: TaskExecutor, + private val getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask) { + + interface Listener { + fun onPSTNSupportUpdated() + } + + private var alreadyChecked = AtomicBoolean(false) + + private val pstnSupportListeners = emptyList().toMutableList() + + fun addListener(listener: Listener) { + pstnSupportListeners.add(listener) + } + + fun removeListener(listener: Listener) { + pstnSupportListeners.remove(listener) + } + + var supportedPSTNProtocol: String? = null + private set + + fun checkForPSTNSupportIfNeeded() { + if (alreadyChecked.get()) return + taskExecutor.executorScope.checkForPSTNSupport() + } + + private fun CoroutineScope.checkForPSTNSupport() = launch { + try { + supportedPSTNProtocol = getSupportedPSTN(3) + alreadyChecked.set(true) + if (supportedPSTNProtocol != null) { + pstnSupportListeners.forEach { + tryOrNull { it.onPSTNSupportUpdated() } + } + } + } catch (failure: Throwable) { + Timber.v("Fail to get supported PSTN, will check again next time.") + } + } + + private suspend fun getSupportedPSTN(maxTries: Int): String? { + val thirdPartyProtocols: Map = try { + getThirdPartyProtocolsTask.execute(Unit) + } catch (failure: Throwable) { + if (maxTries == 1) { + throw failure + } else { + // Wait for 10s before trying again + delay(10_000L) + return getSupportedPSTN(maxTries - 1) + } + } + return when { + thirdPartyProtocols.containsKey(PSTN_VECTOR_KEY) -> PSTN_VECTOR_KEY + thirdPartyProtocols.containsKey(PSTN_MATRIX_KEY) -> PSTN_MATRIX_KEY + else -> null + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/DefaultCallSignalingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/DefaultCallSignalingService.kt index 10690c59de..7d046cb642 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/DefaultCallSignalingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/DefaultCallSignalingService.kt @@ -16,16 +16,12 @@ package org.matrix.android.sdk.internal.session.call -import kotlinx.coroutines.Dispatchers -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.call.CallListener import org.matrix.android.sdk.api.session.call.CallSignalingService import org.matrix.android.sdk.api.session.call.MxCall +import org.matrix.android.sdk.api.session.call.PSTNProtocolChecker import org.matrix.android.sdk.api.session.call.TurnServerResponse -import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.internal.session.SessionScope -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.launchToCallback import timber.log.Timber import javax.inject.Inject @@ -34,14 +30,16 @@ internal class DefaultCallSignalingService @Inject constructor( private val callSignalingHandler: CallSignalingHandler, private val mxCallFactory: MxCallFactory, private val activeCallHandler: ActiveCallHandler, - private val taskExecutor: TaskExecutor, - private val turnServerDataSource: TurnServerDataSource + private val turnServerDataSource: TurnServerDataSource, + private val pstnProtocolChecker: PSTNProtocolChecker ) : CallSignalingService { - override fun getTurnServer(callback: MatrixCallback): Cancelable { - return taskExecutor.executorScope.launchToCallback(Dispatchers.Default, callback) { - turnServerDataSource.getTurnServer() - } + override suspend fun getTurnServer(): TurnServerResponse { + return turnServerDataSource.getTurnServer() + } + + override fun getPSTNProtocolChecker(): PSTNProtocolChecker { + return pstnProtocolChecker } override fun createOutgoingCall(roomId: String, otherUserId: String, isVideoCall: Boolean): MxCall { diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt index 25b2a80a85..8a2d56a5a2 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.call +import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MvRxViewModelFactory @@ -29,17 +30,16 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.call.audio.CallAudioManager import im.vector.app.features.call.webrtc.WebRtcCall import im.vector.app.features.call.webrtc.WebRtcCallManager -import org.matrix.android.sdk.api.MatrixCallback +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.session.call.MxCall import org.matrix.android.sdk.api.session.call.MxPeerConnectionState -import org.matrix.android.sdk.api.session.call.TurnServerResponse import org.matrix.android.sdk.api.session.room.model.call.supportCallTransfer import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem -import java.util.Timer -import java.util.TimerTask class VectorCallViewModel @AssistedInject constructor( @Assisted initialState: VectorCallViewState, @@ -50,7 +50,7 @@ class VectorCallViewModel @AssistedInject constructor( private var call: WebRtcCall? = null - private var connectionTimeoutTimer: Timer? = null + private var connectionTimeoutJob: Job? = null private var hasBeenConnectedOnce = false private val callListener = object : WebRtcCall.Listener { @@ -92,26 +92,20 @@ class VectorCallViewModel @AssistedInject constructor( val callState = call.state if (callState is CallState.Connected && callState.iceConnectionState == MxPeerConnectionState.CONNECTED) { hasBeenConnectedOnce = true - connectionTimeoutTimer?.cancel() - connectionTimeoutTimer = null + connectionTimeoutJob?.cancel() + connectionTimeoutJob = null } else { // do we reset as long as it's moving? - connectionTimeoutTimer?.cancel() + connectionTimeoutJob?.cancel() if (hasBeenConnectedOnce) { - connectionTimeoutTimer = Timer().apply { - schedule(object : TimerTask() { - override fun run() { - session.callSignalingService().getTurnServer(object : MatrixCallback { - override fun onFailure(failure: Throwable) { - _viewEvents.post(VectorCallViewEvents.ConnectionTimeout(null)) - } - - override fun onSuccess(data: TurnServerResponse) { - _viewEvents.post(VectorCallViewEvents.ConnectionTimeout(data)) - } - }) - } - }, 30_000) + connectionTimeoutJob = viewModelScope.launch { + delay(30_000) + try { + val turn = session.callSignalingService().getTurnServer() + _viewEvents.post(VectorCallViewEvents.ConnectionTimeout(turn)) + } catch (failure: Throwable) { + _viewEvents.post(VectorCallViewEvents.ConnectionTimeout(null)) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/PSTNProtocolChecker.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/PSTNProtocolChecker.kt deleted file mode 100644 index 6452bc3964..0000000000 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/PSTNProtocolChecker.kt +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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.webrtc - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import org.matrix.android.sdk.api.extensions.tryOrNull -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol -import timber.log.Timber -import java.util.concurrent.atomic.AtomicBoolean -import javax.inject.Inject -import javax.inject.Singleton - -private const val PSTN_VECTOR_KEY = "im.vector.protocol.pstn" -private const val PSTN_MATRIX_KEY = "m.protocol.pstn" - -@Singleton -class PSTNProtocolChecker @Inject constructor() { - - private var alreadyChecked = AtomicBoolean(false) - - private val pstnSupportListeners = emptyList().toMutableList() - fun addPstnSupportListener(listener: WebRtcCallManager.PSTNSupportListener) { - pstnSupportListeners.add(listener) - } - - fun removePstnSupportListener(listener: WebRtcCallManager.PSTNSupportListener) { - pstnSupportListeners.remove(listener) - } - - var supportedPSTNProtocol: String? = null - private set - - fun checkForPSTNSupportIfNeeded(currentSession: Session?) { - if (alreadyChecked.get()) return - GlobalScope.checkForPSTNSupport(currentSession) - } - - private fun CoroutineScope.checkForPSTNSupport(currentSession: Session?) = launch { - try { - supportedPSTNProtocol = currentSession?.getSupportedPSTN(3) - alreadyChecked.set(true) - if (supportedPSTNProtocol != null) { - pstnSupportListeners.forEach { - tryOrNull { it.onPSTNSupportUpdated() } - } - } - } catch (failure: Throwable) { - Timber.v("Fail to get supported PSTN, will check again next time.") - } - } -} - -suspend fun Session.getSupportedPSTN(maxTries: Int): String? { - val thirdPartyProtocols: Map = try { - thirdPartyService().getThirdPartyProtocols() - } catch (failure: Throwable) { - if (maxTries == 1) { - throw failure - } else { - // Wait for 10s before trying again - delay(10_000L) - return getSupportedPSTN(maxTries - 1) - } - } - return when { - thirdPartyProtocols.containsKey(PSTN_VECTOR_KEY) -> PSTN_VECTOR_KEY - thirdPartyProtocols.containsKey(PSTN_MATRIX_KEY) -> PSTN_MATRIX_KEY - else -> null - } -} diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt index c72d7c8a76..469fba4d5e 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt @@ -53,7 +53,6 @@ import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent import org.matrix.android.sdk.api.session.room.model.call.CallNegotiateContent import org.matrix.android.sdk.api.session.room.model.call.SdpType -import org.matrix.android.sdk.internal.util.awaitCallback import org.threeten.bp.Duration import org.webrtc.AudioSource import org.webrtc.AudioTrack @@ -420,9 +419,7 @@ class WebRtcCall(val mxCall: MxCall, private suspend fun getTurnServer(): TurnServerResponse? { return tryOrNull { - awaitCallback { - sessionProvider.get()?.callSignalingService()?.getTurnServer(it) - } + sessionProvider.get()?.callSignalingService()?.getTurnServer() } } 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 a2b82de0bb..2f8f84051e 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 @@ -32,6 +32,7 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.call.CallListener import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.session.call.MxCall +import org.matrix.android.sdk.api.session.call.PSTNProtocolChecker import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent @@ -57,34 +58,32 @@ import javax.inject.Singleton @Singleton class WebRtcCallManager @Inject constructor( private val context: Context, - private val activeSessionDataSource: ActiveSessionDataSource, - private val pstnProtocolChecker: PSTNProtocolChecker + private val activeSessionDataSource: ActiveSessionDataSource ) : CallListener, LifecycleObserver { private val currentSession: Session? get() = activeSessionDataSource.currentValue?.orNull() + private val pstnProtocolChecker: PSTNProtocolChecker? + get() = currentSession?.callSignalingService()?.getPSTNProtocolChecker() + interface CurrentCallListener { fun onCurrentCallChange(call: WebRtcCall?) {} fun onAudioDevicesChange() {} } - interface PSTNSupportListener { - fun onPSTNSupportUpdated() - } - val supportedPSTNProtocol: String? - get() = pstnProtocolChecker.supportedPSTNProtocol + get() = pstnProtocolChecker?.supportedPSTNProtocol val supportsPSTNProtocol: Boolean get() = supportedPSTNProtocol != null - fun addPstnSupportListener(listener: PSTNSupportListener) { - pstnProtocolChecker.addPstnSupportListener(listener) + fun addPstnSupportListener(listener: PSTNProtocolChecker.Listener) { + pstnProtocolChecker?.addListener(listener) } - fun removePstnSupportListener(listener: PSTNSupportListener) { - pstnProtocolChecker.removePstnSupportListener(listener) + fun removePstnSupportListener(listener: PSTNProtocolChecker.Listener) { + pstnProtocolChecker?.removeListener(listener) } private val currentCallsListeners = CopyOnWriteArrayList() @@ -116,7 +115,6 @@ class WebRtcCallManager @Inject constructor( @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun entersForeground() { isInBackground = false - checkForPSTNSupportIfNeeded() } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) @@ -157,7 +155,7 @@ class WebRtcCallManager @Inject constructor( } fun checkForPSTNSupportIfNeeded() { - pstnProtocolChecker.checkForPSTNSupportIfNeeded(currentSession) + pstnProtocolChecker?.checkForPSTNSupportIfNeeded() } /** @@ -169,7 +167,6 @@ class WebRtcCallManager @Inject constructor( Timber.v("## VOIP headSetButtonTapped") val call = getCurrentCall() ?: return if (call.mxCall.state is CallState.LocalRinging) { - // accept call call.acceptIncomingCall() } if (call.mxCall.state is CallState.Connected) { 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 acc295b242..b4ac226db4 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 @@ -26,8 +26,8 @@ import com.airbnb.mvrx.ViewModelContext import com.jakewharton.rxrelay2.BehaviorRelay import com.jakewharton.rxrelay2.PublishRelay import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel @@ -65,6 +65,7 @@ import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.call.PSTNProtocolChecker import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.LocalEcho @@ -121,7 +122,7 @@ class RoomDetailViewModel @AssistedInject constructor( private val directRoomHelper: DirectRoomHelper, timelineSettingsFactory: TimelineSettingsFactory ) : VectorViewModel(initialState), - Timeline.Listener, ChatEffectManager.Delegate, WebRtcCallManager.PSTNSupportListener { + Timeline.Listener, ChatEffectManager.Delegate, PSTNProtocolChecker.Listener { private val room = session.getRoom(initialState.roomId)!! private val eventId = initialState.eventId @@ -233,65 +234,65 @@ class RoomDetailViewModel @AssistedInject constructor( override fun handle(action: RoomDetailAction) { when (action) { - is RoomDetailAction.UserIsTyping -> handleUserIsTyping(action) - is RoomDetailAction.SaveDraft -> handleSaveDraft(action) - is RoomDetailAction.SendMessage -> handleSendMessage(action) - is RoomDetailAction.SendMedia -> handleSendMedia(action) - is RoomDetailAction.SendSticker -> handleSendSticker(action) - is RoomDetailAction.TimelineEventTurnsVisible -> handleEventVisible(action) - is RoomDetailAction.TimelineEventTurnsInvisible -> handleEventInvisible(action) - is RoomDetailAction.LoadMoreTimelineEvents -> handleLoadMore(action) - is RoomDetailAction.SendReaction -> handleSendReaction(action) - is RoomDetailAction.AcceptInvite -> handleAcceptInvite() - is RoomDetailAction.RejectInvite -> handleRejectInvite() - is RoomDetailAction.RedactAction -> handleRedactEvent(action) - is RoomDetailAction.UndoReaction -> handleUndoReact(action) - is RoomDetailAction.UpdateQuickReactAction -> handleUpdateQuickReaction(action) - is RoomDetailAction.EnterRegularMode -> handleEnterRegularMode(action) - is RoomDetailAction.EnterEditMode -> handleEditAction(action) - is RoomDetailAction.EnterQuoteMode -> handleQuoteAction(action) - is RoomDetailAction.EnterReplyMode -> handleReplyAction(action) - is RoomDetailAction.DownloadOrOpen -> handleOpenOrDownloadFile(action) - is RoomDetailAction.NavigateToEvent -> handleNavigateToEvent(action) - is RoomDetailAction.HandleTombstoneEvent -> handleTombstoneEvent(action) - is RoomDetailAction.ResendMessage -> handleResendEvent(action) - is RoomDetailAction.RemoveFailedEcho -> handleRemove(action) - is RoomDetailAction.ResendAll -> handleResendAll() - is RoomDetailAction.MarkAllAsRead -> handleMarkAllAsRead() - is RoomDetailAction.ReportContent -> handleReportContent(action) - is RoomDetailAction.IgnoreUser -> handleIgnoreUser(action) + is RoomDetailAction.UserIsTyping -> handleUserIsTyping(action) + is RoomDetailAction.SaveDraft -> handleSaveDraft(action) + is RoomDetailAction.SendMessage -> handleSendMessage(action) + is RoomDetailAction.SendMedia -> handleSendMedia(action) + is RoomDetailAction.SendSticker -> handleSendSticker(action) + is RoomDetailAction.TimelineEventTurnsVisible -> handleEventVisible(action) + is RoomDetailAction.TimelineEventTurnsInvisible -> handleEventInvisible(action) + is RoomDetailAction.LoadMoreTimelineEvents -> handleLoadMore(action) + is RoomDetailAction.SendReaction -> handleSendReaction(action) + is RoomDetailAction.AcceptInvite -> handleAcceptInvite() + is RoomDetailAction.RejectInvite -> handleRejectInvite() + is RoomDetailAction.RedactAction -> handleRedactEvent(action) + is RoomDetailAction.UndoReaction -> handleUndoReact(action) + is RoomDetailAction.UpdateQuickReactAction -> handleUpdateQuickReaction(action) + is RoomDetailAction.EnterRegularMode -> handleEnterRegularMode(action) + is RoomDetailAction.EnterEditMode -> handleEditAction(action) + is RoomDetailAction.EnterQuoteMode -> handleQuoteAction(action) + is RoomDetailAction.EnterReplyMode -> handleReplyAction(action) + is RoomDetailAction.DownloadOrOpen -> handleOpenOrDownloadFile(action) + is RoomDetailAction.NavigateToEvent -> handleNavigateToEvent(action) + is RoomDetailAction.HandleTombstoneEvent -> handleTombstoneEvent(action) + is RoomDetailAction.ResendMessage -> handleResendEvent(action) + is RoomDetailAction.RemoveFailedEcho -> handleRemove(action) + is RoomDetailAction.ResendAll -> handleResendAll() + is RoomDetailAction.MarkAllAsRead -> handleMarkAllAsRead() + is RoomDetailAction.ReportContent -> handleReportContent(action) + is RoomDetailAction.IgnoreUser -> handleIgnoreUser(action) is RoomDetailAction.EnterTrackingUnreadMessagesState -> startTrackingUnreadMessages() - is RoomDetailAction.ExitTrackingUnreadMessagesState -> stopTrackingUnreadMessages() - is RoomDetailAction.ReplyToOptions -> handleReplyToOptions(action) - is RoomDetailAction.AcceptVerificationRequest -> handleAcceptVerification(action) - is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action) - is RoomDetailAction.RequestVerification -> handleRequestVerification(action) - is RoomDetailAction.ResumeVerification -> handleResumeRequestVerification(action) - is RoomDetailAction.ReRequestKeys -> handleReRequestKeys(action) - 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() - is RoomDetailAction.ManageIntegrations -> handleManageIntegrations() - is RoomDetailAction.AddJitsiWidget -> handleAddJitsiConference(action) - is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId) - is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action) - is RoomDetailAction.CancelSend -> handleCancel(action) - is RoomDetailAction.OpenOrCreateDm -> handleOpenOrCreateDm(action) - is RoomDetailAction.JumpToReadReceipt -> handleJumpToReadReceipt(action) - RoomDetailAction.QuickActionInvitePeople -> handleInvitePeople() - RoomDetailAction.QuickActionSetAvatar -> handleQuickSetAvatar() - is RoomDetailAction.SetAvatarAction -> handleSetNewAvatar(action) - RoomDetailAction.QuickActionSetTopic -> _viewEvents.post(RoomDetailViewEvents.OpenRoomSettings) - is RoomDetailAction.ShowRoomAvatarFullScreen -> { + is RoomDetailAction.ExitTrackingUnreadMessagesState -> stopTrackingUnreadMessages() + is RoomDetailAction.ReplyToOptions -> handleReplyToOptions(action) + is RoomDetailAction.AcceptVerificationRequest -> handleAcceptVerification(action) + is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action) + is RoomDetailAction.RequestVerification -> handleRequestVerification(action) + is RoomDetailAction.ResumeVerification -> handleResumeRequestVerification(action) + is RoomDetailAction.ReRequestKeys -> handleReRequestKeys(action) + 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() + is RoomDetailAction.ManageIntegrations -> handleManageIntegrations() + is RoomDetailAction.AddJitsiWidget -> handleAddJitsiConference(action) + is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId) + is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action) + is RoomDetailAction.CancelSend -> handleCancel(action) + is RoomDetailAction.OpenOrCreateDm -> handleOpenOrCreateDm(action) + is RoomDetailAction.JumpToReadReceipt -> handleJumpToReadReceipt(action) + RoomDetailAction.QuickActionInvitePeople -> handleInvitePeople() + RoomDetailAction.QuickActionSetAvatar -> handleQuickSetAvatar() + is RoomDetailAction.SetAvatarAction -> handleSetNewAvatar(action) + RoomDetailAction.QuickActionSetTopic -> _viewEvents.post(RoomDetailViewEvents.OpenRoomSettings) + is RoomDetailAction.ShowRoomAvatarFullScreen -> { _viewEvents.post( RoomDetailViewEvents.ShowRoomAvatarFullScreen(action.matrixItem, action.transitionView) ) } - is RoomDetailAction.DoNotShowPreviewUrlFor -> handleDoNotShowPreviewUrlFor(action) + is RoomDetailAction.DoNotShowPreviewUrlFor -> handleDoNotShowPreviewUrlFor(action) }.exhaustive } @@ -620,16 +621,16 @@ class RoomDetailViewModel @AssistedInject constructor( return@withState false } when (itemId) { - R.id.resend_all -> state.asyncRoomSummary()?.hasFailedSending == true + R.id.resend_all -> state.asyncRoomSummary()?.hasFailedSending == true R.id.timeline_setting -> true - R.id.invite -> state.canInvite - R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true + R.id.invite -> state.canInvite + R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true R.id.open_matrix_apps -> true R.id.voice_call, - R.id.video_call -> callManager.getCallsByRoomId(state.roomId).isEmpty() - R.id.hangup_call -> callManager.getCallsByRoomId(state.roomId).isNotEmpty() - R.id.search -> true - else -> false + R.id.video_call -> callManager.getCallsByRoomId(state.roomId).isEmpty() + R.id.hangup_call -> callManager.getCallsByRoomId(state.roomId).isNotEmpty() + R.id.search -> true + else -> false } } @@ -742,7 +743,7 @@ class RoomDetailViewModel @AssistedInject constructor( _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) popDraft() } - is ParsedCommand.SendChatEffect -> { + is ParsedCommand.SendChatEffect -> { sendChatEffect(slashCommandResult) _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) popDraft() @@ -775,7 +776,7 @@ class RoomDetailViewModel @AssistedInject constructor( } }.exhaustive } - is SendMode.EDIT -> { + is SendMode.EDIT -> { // is original event a reply? val inReplyTo = state.sendMode.timelineEvent.getRelationContent()?.inReplyTo?.eventId if (inReplyTo != null) { @@ -800,7 +801,7 @@ class RoomDetailViewModel @AssistedInject constructor( _viewEvents.post(RoomDetailViewEvents.MessageSent) popDraft() } - is SendMode.QUOTE -> { + is SendMode.QUOTE -> { val messageContent: MessageContent? = state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel() ?: state.sendMode.timelineEvent.root.getClearContent().toModel() @@ -823,7 +824,7 @@ class RoomDetailViewModel @AssistedInject constructor( _viewEvents.post(RoomDetailViewEvents.MessageSent) popDraft() } - is SendMode.REPLY -> { + is SendMode.REPLY -> { state.sendMode.timelineEvent.let { room.replyToMessage(it, action.text.toString(), action.autoMarkdown) _viewEvents.post(RoomDetailViewEvents.MessageSent) @@ -1442,7 +1443,7 @@ class RoomDetailViewModel @AssistedInject constructor( } override fun onPSTNSupportUpdated() { - updateShowDialerOptionState() + updateShowDialerOptionState() } private fun updateShowDialerOptionState() {