Merge pull request #5203 from vector-im/feature/dla/fix_consult_transfer

Call transfer with consult fails to make outgoing consultation call.
This commit is contained in:
Benoit Marty 2022-02-14 19:54:09 +01:00 committed by GitHub
commit 6c4f389342
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 101 additions and 77 deletions

1
changelog.d/5201.bugfix Normal file
View File

@ -0,0 +1 @@
Fix for call transfer with consult failing to make outgoing consultation call.

View File

@ -54,6 +54,7 @@ import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.databinding.ActivityCallBinding
import im.vector.app.features.call.dialpad.CallDialPadBottomSheet
import im.vector.app.features.call.dialpad.DialPadFragment
import im.vector.app.features.call.transfer.CallTransferActivity
import im.vector.app.features.call.utils.EglUtils
import im.vector.app.features.call.webrtc.WebRtcCall
import im.vector.app.features.call.webrtc.WebRtcCallManager
@ -165,6 +166,7 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
?.let {
callViewModel.handle(VectorCallViewActions.SwitchCall(it))
}
this.intent = intent
}
override fun getMenuRes() = R.menu.vector_call
@ -522,15 +524,22 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
val callId = withState(callViewModel) { it.callId }
navigator.openCallTransfer(this, callTransferActivityResultLauncher, callId)
}
is VectorCallViewEvents.FailToTransfer -> showSnackbar(getString(R.string.call_transfer_failure))
null -> {
}
}
}
private val callTransferActivityResultLauncher = registerStartForActivityResult { activityResult ->
if (activityResult.resultCode == Activity.RESULT_CANCELED) {
when (activityResult.resultCode) {
Activity.RESULT_CANCELED -> {
callViewModel.handle(VectorCallViewActions.CallTransferSelectionCancelled)
}
Activity.RESULT_OK -> {
CallTransferActivity.getCallTransferResult(activityResult.data)
?.let { callViewModel.handle(VectorCallViewActions.CallTransferSelectionResult(it)) }
}
}
}
private fun onErrorTimoutConnect(turn: TurnServerResponse?) {

View File

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

View File

@ -29,6 +29,7 @@ sealed class VectorCallViewEvents : VectorViewEvents {
) : VectorCallViewEvents()
object ShowDialPad : VectorCallViewEvents()
object ShowCallTransferScreen : VectorCallViewEvents()
object FailToTransfer : VectorCallViewEvents()
// data class CallAnswered(val content: CallAnswerContent) : VectorCallViewEvents()
// data class CallHangup(val content: CallHangupContent) : 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.platform.VectorViewModel
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.WebRtcCallManager
import im.vector.app.features.call.webrtc.getOpponentAsMatrixItem
import im.vector.app.features.createdirect.DirectRoomHelper
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
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.call.CallState
import org.matrix.android.sdk.api.session.call.MxCall
@ -47,7 +51,9 @@ class VectorCallViewModel @AssistedInject constructor(
@Assisted initialState: VectorCallViewState,
val session: Session,
val callManager: WebRtcCallManager,
val proximityManager: CallProximityManager
val proximityManager: CallProximityManager,
private val dialPadLookup: DialPadLookup,
private val directRoomHelper: DirectRoomHelper,
) : VectorViewModel<VectorCallViewState, VectorCallViewActions, VectorCallViewEvents>(initialState) {
private var call: WebRtcCall? = null
@ -327,6 +333,9 @@ class VectorCallViewModel @AssistedInject constructor(
VectorCallViewActions.CallTransferSelectionCancelled -> {
call?.updateRemoteOnHold(false)
}
is VectorCallViewActions.CallTransferSelectionResult -> {
handleCallTransferSelectionResult(action.callTransferResult)
}
VectorCallViewActions.TransferCall -> {
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
interface Factory : MavericksAssistedViewModelFactory<VectorCallViewModel, VectorCallViewState> {
override fun create(initialState: VectorCallViewState): VectorCallViewModel

View File

@ -16,7 +16,6 @@
package im.vector.app.features.call.transfer
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
@ -27,6 +26,7 @@ import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityCallTransferBinding
import kotlinx.parcelize.Parcelize
@ -57,9 +57,7 @@ class CallTransferActivity : VectorBaseActivity<ActivityCallTransferBinding>() {
callTransferViewModel.observeViewEvents {
when (it) {
is CallTransferViewEvents.Complete -> handleComplete()
CallTransferViewEvents.Loading -> showWaitingView()
is CallTransferViewEvents.FailToTransfer -> showSnackbar(getString(R.string.call_transfer_failure))
}
}.exhaustive
}
sectionsPagerAdapter = CallTransferPagerAdapter(this)
@ -82,29 +80,41 @@ class CallTransferActivity : VectorBaseActivity<ActivityCallTransferBinding>() {
when (views.callTransferTabLayout.selectedTabPosition) {
CallTransferPagerAdapter.USER_LIST_INDEX -> {
val selectedUser = sectionsPagerAdapter.userListFragment?.getCurrentState()?.getSelectedMatrixId()?.firstOrNull() ?: return@debouncedClicks
val action = CallTransferAction.ConnectWithUserId(views.callTransferConsultCheckBox.isChecked, selectedUser)
callTransferViewModel.handle(action)
val result = CallTransferResult.ConnectWithUserId(views.callTransferConsultCheckBox.isChecked, selectedUser)
handleComplete(result)
}
CallTransferPagerAdapter.DIAL_PAD_INDEX -> {
val phoneNumber = sectionsPagerAdapter.dialPadFragment?.getRawInput() ?: return@debouncedClicks
val action = CallTransferAction.ConnectWithPhoneNumber(views.callTransferConsultCheckBox.isChecked, phoneNumber)
callTransferViewModel.handle(action)
val result = CallTransferResult.ConnectWithPhoneNumber(views.callTransferConsultCheckBox.isChecked, phoneNumber)
handleComplete(result)
}
}
}
}
private fun handleComplete() {
setResult(Activity.RESULT_OK)
private fun handleComplete(callTransferResult: CallTransferResult? = null) {
if (callTransferResult != null) {
val intent = Intent().apply {
putExtra(EXTRA_TRANSFER_RESULT, callTransferResult)
}
setResult(RESULT_OK, intent)
} else {
setResult(RESULT_OK)
}
finish()
}
companion object {
private const val EXTRA_TRANSFER_RESULT = "EXTRA_TRANSFER_RESULT"
fun newIntent(context: Context, callId: String): Intent {
return Intent(context, CallTransferActivity::class.java).also {
it.putExtra(Mavericks.KEY_ARG, CallTransferArgs(callId))
}
}
fun getCallTransferResult(intent: Intent?): CallTransferResult? {
return intent?.extras?.getParcelable(EXTRA_TRANSFER_RESULT)
}
}
}

View File

@ -16,9 +16,10 @@
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()
data class ConnectWithPhoneNumber(val consultFirst: Boolean, val phoneNumber: String) : CallTransferAction()
sealed class CallTransferResult : Parcelable {
@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 {
object Complete : CallTransferViewEvents()
object Loading : CallTransferViewEvents()
object FailToTransfer : CallTransferViewEvents()
}

View File

@ -22,22 +22,16 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
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.features.call.dialpad.DialPadLookup
import im.vector.app.features.call.webrtc.WebRtcCall
import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.createdirect.DirectRoomHelper
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.call.CallState
import org.matrix.android.sdk.api.session.call.MxCall
class CallTransferViewModel @AssistedInject constructor(@Assisted initialState: CallTransferViewState,
private val dialPadLookup: DialPadLookup,
private val directRoomHelper: DirectRoomHelper,
private val callManager: WebRtcCallManager) :
VectorViewModel<CallTransferViewState, CallTransferAction, CallTransferViewEvents>(initialState) {
VectorViewModel<CallTransferViewState, EmptyAction, CallTransferViewEvents>(initialState) {
@AssistedFactory
interface Factory : MavericksAssistedViewModelFactory<CallTransferViewModel, CallTransferViewState> {
@ -68,53 +62,5 @@ class CallTransferViewModel @AssistedInject constructor(@Assisted initialState:
call?.removeListener(callListener)
}
override fun handle(action: CallTransferAction) {
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)
}
}
}
override fun handle(action: EmptyAction) { }
}