Move call transfer actions back to callViewModel via result from callTransferActivity

This commit is contained in:
David Langley 2022-02-10 19:07:32 +00:00
parent 11986ec9ef
commit cb37bc56a6
8 changed files with 97 additions and 69 deletions

View File

@ -54,6 +54,9 @@ import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.databinding.ActivityCallBinding import im.vector.app.databinding.ActivityCallBinding
import im.vector.app.features.call.dialpad.CallDialPadBottomSheet import im.vector.app.features.call.dialpad.CallDialPadBottomSheet
import im.vector.app.features.call.dialpad.DialPadFragment import im.vector.app.features.call.dialpad.DialPadFragment
import im.vector.app.features.call.transfer.CallTransferActivity
import im.vector.app.features.call.transfer.CallTransferResult
import im.vector.app.features.call.transfer.CallTransferViewEvents
import im.vector.app.features.call.utils.EglUtils import im.vector.app.features.call.utils.EglUtils
import im.vector.app.features.call.webrtc.WebRtcCall import im.vector.app.features.call.webrtc.WebRtcCall
import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.call.webrtc.WebRtcCallManager
@ -523,14 +526,23 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
val callId = withState(callViewModel) { it.callId } val callId = withState(callViewModel) { it.callId }
navigator.openCallTransfer(this, callTransferActivityResultLauncher, callId) navigator.openCallTransfer(this, callTransferActivityResultLauncher, callId)
} }
is VectorCallViewEvents.FailToTransfer -> showSnackbar(getString(R.string.call_transfer_failure))
null -> { null -> {
} }
} }
} }
private val callTransferActivityResultLauncher = registerStartForActivityResult { activityResult -> private val callTransferActivityResultLauncher = registerStartForActivityResult { activityResult ->
if (activityResult.resultCode == Activity.RESULT_CANCELED) {
callViewModel.handle(VectorCallViewActions.CallTransferSelectionCancelled) when(activityResult.resultCode) {
Activity.RESULT_CANCELED -> {
callViewModel.handle(VectorCallViewActions.CallTransferSelectionCancelled)
}
Activity.RESULT_OK -> {
activityResult.data?.extras?.getParcelable<CallTransferResult>(CallTransferActivity.EXTRA_TRANSFER_RESULT)?.also {
callViewModel.handle(VectorCallViewActions.CallTransferSelectionResult(it))
}
}
} }
} }

View File

@ -18,6 +18,7 @@ package im.vector.app.features.call
import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.features.call.audio.CallAudioManager import im.vector.app.features.call.audio.CallAudioManager
import im.vector.app.features.call.transfer.CallTransferResult
sealed class VectorCallViewActions : VectorViewModelAction { sealed class VectorCallViewActions : VectorViewModelAction {
object EndCall : VectorCallViewActions() object EndCall : VectorCallViewActions()
@ -37,5 +38,6 @@ sealed class VectorCallViewActions : VectorViewModelAction {
object ToggleHDSD : VectorCallViewActions() object ToggleHDSD : VectorCallViewActions()
object InitiateCallTransfer : VectorCallViewActions() object InitiateCallTransfer : VectorCallViewActions()
object CallTransferSelectionCancelled : VectorCallViewActions() object CallTransferSelectionCancelled : VectorCallViewActions()
data class CallTransferSelectionResult(val callTransferResult: CallTransferResult) : VectorCallViewActions()
object TransferCall : VectorCallViewActions() object TransferCall : VectorCallViewActions()
} }

View File

@ -29,6 +29,7 @@ sealed class VectorCallViewEvents : VectorViewEvents {
) : VectorCallViewEvents() ) : VectorCallViewEvents()
object ShowDialPad : VectorCallViewEvents() object ShowDialPad : VectorCallViewEvents()
object ShowCallTransferScreen : VectorCallViewEvents() object ShowCallTransferScreen : VectorCallViewEvents()
object FailToTransfer : VectorCallViewEvents()
// data class CallAnswered(val content: CallAnswerContent) : VectorCallViewEvents() // data class CallAnswered(val content: CallAnswerContent) : VectorCallViewEvents()
// data class CallHangup(val content: CallHangupContent) : VectorCallViewEvents() // data class CallHangup(val content: CallHangupContent) : VectorCallViewEvents()
// object CallAccepted : VectorCallViewEvents() // object CallAccepted : VectorCallViewEvents()

View File

@ -29,13 +29,17 @@ import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.call.audio.CallAudioManager import im.vector.app.features.call.audio.CallAudioManager
import im.vector.app.features.call.dialpad.DialPadLookup
import im.vector.app.features.call.transfer.CallTransferResult
import im.vector.app.features.call.webrtc.WebRtcCall import im.vector.app.features.call.webrtc.WebRtcCall
import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.call.webrtc.getOpponentAsMatrixItem import im.vector.app.features.call.webrtc.getOpponentAsMatrixItem
import im.vector.app.features.createdirect.DirectRoomHelper
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.MatrixPatterns
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.Session 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.CallState
import org.matrix.android.sdk.api.session.call.MxCall import org.matrix.android.sdk.api.session.call.MxCall
@ -47,7 +51,9 @@ class VectorCallViewModel @AssistedInject constructor(
@Assisted initialState: VectorCallViewState, @Assisted initialState: VectorCallViewState,
val session: Session, val session: Session,
val callManager: WebRtcCallManager, val callManager: WebRtcCallManager,
val proximityManager: CallProximityManager val proximityManager: CallProximityManager,
private val dialPadLookup: DialPadLookup,
private val directRoomHelper: DirectRoomHelper,
) : VectorViewModel<VectorCallViewState, VectorCallViewActions, VectorCallViewEvents>(initialState) { ) : VectorViewModel<VectorCallViewState, VectorCallViewActions, VectorCallViewEvents>(initialState) {
private var call: WebRtcCall? = null private var call: WebRtcCall? = null
@ -327,6 +333,9 @@ class VectorCallViewModel @AssistedInject constructor(
VectorCallViewActions.CallTransferSelectionCancelled -> { VectorCallViewActions.CallTransferSelectionCancelled -> {
call?.updateRemoteOnHold(false) call?.updateRemoteOnHold(false)
} }
is VectorCallViewActions.CallTransferSelectionResult -> {
handleCallTransferSelectionResult(action.callTransferResult)
}
VectorCallViewActions.TransferCall -> { VectorCallViewActions.TransferCall -> {
handleCallTransfer() handleCallTransfer()
} }
@ -345,6 +354,53 @@ class VectorCallViewModel @AssistedInject constructor(
} }
} }
private fun handleCallTransferSelectionResult(result: CallTransferResult) {
when (result) {
is CallTransferResult.ConnectWithUserId -> connectWithUserId(result)
is CallTransferResult.ConnectWithPhoneNumber -> connectWithPhoneNumber(result)
}.exhaustive
}
private fun connectWithUserId(result: CallTransferResult.ConnectWithUserId) {
viewModelScope.launch {
try {
if (result.consultFirst) {
val dmRoomId = directRoomHelper.ensureDMExists(result.selectedUserId)
callManager.startOutgoingCall(
nativeRoomId = dmRoomId,
otherUserId = result.selectedUserId,
isVideoCall = call?.mxCall?.isVideoCall.orFalse(),
transferee = call
)
} else {
call?.transferToUser(result.selectedUserId, null)
}
} catch (failure: Throwable) {
_viewEvents.post(VectorCallViewEvents.FailToTransfer)
}
}
}
private fun connectWithPhoneNumber(action: CallTransferResult.ConnectWithPhoneNumber) {
viewModelScope.launch {
try {
val result = dialPadLookup.lookupPhoneNumber(action.phoneNumber)
if (action.consultFirst) {
callManager.startOutgoingCall(
nativeRoomId = result.roomId,
otherUserId = result.userId,
isVideoCall = call?.mxCall?.isVideoCall.orFalse(),
transferee = call
)
} else {
call?.transferToUser(result.userId, result.roomId)
}
} catch (failure: Throwable) {
_viewEvents.post(VectorCallViewEvents.FailToTransfer)
}
}
}
@AssistedFactory @AssistedFactory
interface Factory : MavericksAssistedViewModelFactory<VectorCallViewModel, VectorCallViewState> { interface Factory : MavericksAssistedViewModelFactory<VectorCallViewModel, VectorCallViewState> {
override fun create(initialState: VectorCallViewState): VectorCallViewModel override fun create(initialState: VectorCallViewState): VectorCallViewModel

View File

@ -57,8 +57,6 @@ class CallTransferActivity : VectorBaseActivity<ActivityCallTransferBinding>() {
callTransferViewModel.observeViewEvents { callTransferViewModel.observeViewEvents {
when (it) { when (it) {
is CallTransferViewEvents.Complete -> handleComplete() is CallTransferViewEvents.Complete -> handleComplete()
CallTransferViewEvents.Loading -> showWaitingView()
is CallTransferViewEvents.FailToTransfer -> showSnackbar(getString(R.string.call_transfer_failure))
} }
} }
@ -82,25 +80,32 @@ class CallTransferActivity : VectorBaseActivity<ActivityCallTransferBinding>() {
when (views.callTransferTabLayout.selectedTabPosition) { when (views.callTransferTabLayout.selectedTabPosition) {
CallTransferPagerAdapter.USER_LIST_INDEX -> { CallTransferPagerAdapter.USER_LIST_INDEX -> {
val selectedUser = sectionsPagerAdapter.userListFragment?.getCurrentState()?.getSelectedMatrixId()?.firstOrNull() ?: return@debouncedClicks val selectedUser = sectionsPagerAdapter.userListFragment?.getCurrentState()?.getSelectedMatrixId()?.firstOrNull() ?: return@debouncedClicks
val action = CallTransferAction.ConnectWithUserId(views.callTransferConsultCheckBox.isChecked, selectedUser) val result = CallTransferResult.ConnectWithUserId(views.callTransferConsultCheckBox.isChecked, selectedUser)
callTransferViewModel.handle(action) handleComplete(result)
} }
CallTransferPagerAdapter.DIAL_PAD_INDEX -> { CallTransferPagerAdapter.DIAL_PAD_INDEX -> {
val phoneNumber = sectionsPagerAdapter.dialPadFragment?.getRawInput() ?: return@debouncedClicks val phoneNumber = sectionsPagerAdapter.dialPadFragment?.getRawInput() ?: return@debouncedClicks
val action = CallTransferAction.ConnectWithPhoneNumber(views.callTransferConsultCheckBox.isChecked, phoneNumber) val result = CallTransferResult.ConnectWithPhoneNumber(views.callTransferConsultCheckBox.isChecked, phoneNumber)
callTransferViewModel.handle(action) handleComplete(result)
} }
} }
} }
} }
private fun handleComplete() { private fun handleComplete(callTransferResult: CallTransferResult? = null) {
setResult(Activity.RESULT_OK) if (callTransferResult != null) {
val intent = Intent().apply {
putExtra(EXTRA_TRANSFER_RESULT, callTransferResult)
}
setResult(RESULT_OK, intent)
} else {
setResult(RESULT_OK)
}
finish() finish()
} }
companion object { companion object {
const val EXTRA_TRANSFER_RESULT = "EXTRA_TRANSFER_RESULT"
fun newIntent(context: Context, callId: String): Intent { fun newIntent(context: Context, callId: String): Intent {
return Intent(context, CallTransferActivity::class.java).also { return Intent(context, CallTransferActivity::class.java).also {
it.putExtra(Mavericks.KEY_ARG, CallTransferArgs(callId)) it.putExtra(Mavericks.KEY_ARG, CallTransferArgs(callId))

View File

@ -16,9 +16,11 @@
package im.vector.app.features.call.transfer package im.vector.app.features.call.transfer
import im.vector.app.core.platform.VectorViewModelAction import android.os.Parcelable
import kotlinx.parcelize.Parcelize
sealed class CallTransferAction : VectorViewModelAction {
data class ConnectWithUserId(val consultFirst: Boolean, val selectedUserId: String) : CallTransferAction() sealed class CallTransferResult : Parcelable{
data class ConnectWithPhoneNumber(val consultFirst: Boolean, val phoneNumber: String) : CallTransferAction() @Parcelize data class ConnectWithUserId(val consultFirst: Boolean, val selectedUserId: String) : CallTransferResult()
@Parcelize data class ConnectWithPhoneNumber(val consultFirst: Boolean, val phoneNumber: String) : CallTransferResult()
} }

View File

@ -20,6 +20,4 @@ import im.vector.app.core.platform.VectorViewEvents
sealed class CallTransferViewEvents : VectorViewEvents { sealed class CallTransferViewEvents : VectorViewEvents {
object Complete : CallTransferViewEvents() object Complete : CallTransferViewEvents()
object Loading : CallTransferViewEvents()
object FailToTransfer : CallTransferViewEvents()
} }

View File

@ -23,6 +23,7 @@ import dagger.assisted.AssistedInject
import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.call.dialpad.DialPadLookup import im.vector.app.features.call.dialpad.DialPadLookup
import im.vector.app.features.call.webrtc.WebRtcCall import im.vector.app.features.call.webrtc.WebRtcCall
@ -34,10 +35,8 @@ 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.MxCall
class CallTransferViewModel @AssistedInject constructor(@Assisted initialState: CallTransferViewState, class CallTransferViewModel @AssistedInject constructor(@Assisted initialState: CallTransferViewState,
private val dialPadLookup: DialPadLookup,
private val directRoomHelper: DirectRoomHelper,
private val callManager: WebRtcCallManager) : private val callManager: WebRtcCallManager) :
VectorViewModel<CallTransferViewState, CallTransferAction, CallTransferViewEvents>(initialState) { VectorViewModel<CallTransferViewState, EmptyAction, CallTransferViewEvents>(initialState) {
@AssistedFactory @AssistedFactory
interface Factory : MavericksAssistedViewModelFactory<CallTransferViewModel, CallTransferViewState> { interface Factory : MavericksAssistedViewModelFactory<CallTransferViewModel, CallTransferViewState> {
@ -68,53 +67,6 @@ class CallTransferViewModel @AssistedInject constructor(@Assisted initialState:
call?.removeListener(callListener) call?.removeListener(callListener)
} }
override fun handle(action: CallTransferAction) { override fun handle(action: EmptyAction) { }
when (action) {
is CallTransferAction.ConnectWithUserId -> connectWithUserId(action)
is CallTransferAction.ConnectWithPhoneNumber -> connectWithPhoneNumber(action)
}.exhaustive
}
private fun connectWithUserId(action: CallTransferAction.ConnectWithUserId) {
viewModelScope.launch {
try {
if (action.consultFirst) {
val dmRoomId = directRoomHelper.ensureDMExists(action.selectedUserId)
callManager.startOutgoingCall(
nativeRoomId = dmRoomId,
otherUserId = action.selectedUserId,
isVideoCall = call?.mxCall?.isVideoCall.orFalse(),
transferee = call
)
} else {
call?.transferToUser(action.selectedUserId, null)
}
_viewEvents.post(CallTransferViewEvents.Complete)
} catch (failure: Throwable) {
_viewEvents.post(CallTransferViewEvents.FailToTransfer)
}
}
}
private fun connectWithPhoneNumber(action: CallTransferAction.ConnectWithPhoneNumber) {
viewModelScope.launch {
try {
_viewEvents.post(CallTransferViewEvents.Loading)
val result = dialPadLookup.lookupPhoneNumber(action.phoneNumber)
if (action.consultFirst) {
callManager.startOutgoingCall(
nativeRoomId = result.roomId,
otherUserId = result.userId,
isVideoCall = call?.mxCall?.isVideoCall.orFalse(),
transferee = call
)
} else {
call?.transferToUser(result.userId, result.roomId)
}
_viewEvents.post(CallTransferViewEvents.Complete)
} catch (failure: Throwable) {
_viewEvents.post(CallTransferViewEvents.FailToTransfer)
}
}
}
} }