diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index f9f8a49314..0f27c48c00 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -93,7 +93,6 @@ class VectorCallActivity : VectorBaseActivity(), CallContro } private val callViewModel: VectorCallViewModel by viewModel() - private lateinit var callArgs: CallArgs @Inject lateinit var callManager: WebRtcCallManager @Inject lateinit var viewModelFactory: VectorCallViewModel.Factory @@ -123,13 +122,6 @@ class VectorCallActivity : VectorBaseActivity(), CallContro window.navigationBarColor = Color.BLACK super.onCreate(savedInstanceState) - if (intent.hasExtra(MvRx.KEY_ARG)) { - callArgs = intent.getParcelableExtra(MvRx.KEY_ARG)!! - } else { - Timber.tag(loggerTag.value).e("missing callArgs for VectorCall Activity") - finish() - } - Timber.tag(loggerTag.value).v("EXTRA_MODE is ${intent.getStringExtra(EXTRA_MODE)}") if (intent.getStringExtra(EXTRA_MODE) == INCOMING_RINGING) { turnScreenOnAndKeyguardOff() @@ -152,17 +144,28 @@ class VectorCallActivity : VectorBaseActivity(), CallContro } .disposeOnDestroy() - if (callArgs.isVideoCall) { - if (checkPermissions(PERMISSIONS_FOR_VIDEO_IP_CALL, this, permissionCameraLauncher, R.string.permissions_rationale_msg_camera_and_audio)) { - start() - } - } else { - if (checkPermissions(PERMISSIONS_FOR_AUDIO_IP_CALL, this, permissionCameraLauncher, R.string.permissions_rationale_msg_record_audio)) { - start() + callViewModel.selectSubscribe(this, VectorCallViewState::callId, VectorCallViewState::isVideoCall) { _, isVideoCall -> + if (isVideoCall) { + if (checkPermissions(PERMISSIONS_FOR_VIDEO_IP_CALL, this, permissionCameraLauncher, R.string.permissions_rationale_msg_camera_and_audio)) { + setupRenderersIfNeeded() + } + } else { + if (checkPermissions(PERMISSIONS_FOR_AUDIO_IP_CALL, this, permissionCameraLauncher, R.string.permissions_rationale_msg_record_audio)) { + setupRenderersIfNeeded() + } } } } + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + intent?.takeIf { it.hasExtra(MvRx.KEY_ARG) } + ?.let { intent.getParcelableExtra(MvRx.KEY_ARG) } + ?.let { + callViewModel.handle(VectorCallViewActions.SwitchCall(it)) + } + } + override fun getMenuRes() = R.menu.vector_call override fun onUserLeaveHint() { @@ -211,13 +214,19 @@ class VectorCallActivity : VectorBaseActivity(), CallContro } override fun onDestroy() { - callManager.getCallById(callArgs.callId)?.detachRenderers(listOf(views.pipRenderer, views.fullscreenRenderer)) + detachRenderersIfNeeded() + turnScreenOffAndKeyguardOn() + super.onDestroy() + } + + private fun detachRenderersIfNeeded() { + val callId = withState(callViewModel) { it.callId } + callManager.getCallById(callId)?.detachRenderers(listOf(views.pipRenderer, views.fullscreenRenderer)) if (surfaceRenderersAreInitialized) { views.pipRenderer.release() views.fullscreenRenderer.release() + surfaceRenderersAreInitialized = false } - turnScreenOffAndKeyguardOn() - super.onDestroy() } private fun renderState(state: VectorCallViewState) { @@ -245,21 +254,21 @@ class VectorCallActivity : VectorBaseActivity(), CallContro is CallState.Idle, is CallState.CreateOffer, is CallState.LocalRinging, - is CallState.Dialing -> { + is CallState.Dialing -> { views.fullscreenRenderer.isVisible = false views.pipRendererWrapper.isVisible = false views.callInfoGroup.isVisible = true views.callToolbar.setSubtitle(R.string.call_ringing) configureCallInfo(state) } - is CallState.Answering -> { + is CallState.Answering -> { views.fullscreenRenderer.isVisible = false views.pipRendererWrapper.isVisible = false views.callInfoGroup.isVisible = true views.callToolbar.setSubtitle(R.string.call_connecting) configureCallInfo(state) } - is CallState.Connected -> { + is CallState.Connected -> { views.callToolbar.subtitle = state.formattedDuration if (callState.iceConnectionState == MxPeerConnectionState.CONNECTED) { if (state.isLocalOnHold || state.isRemoteOnHold) { @@ -291,7 +300,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro configureCallInfo(state) } else { configureCallInfo(state) - if (callArgs.isVideoCall) { + if (state.isVideoCall) { views.fullscreenRenderer.isVisible = true views.pipRendererWrapper.isVisible = true views.callInfoGroup.isVisible = false @@ -311,10 +320,10 @@ class VectorCallActivity : VectorBaseActivity(), CallContro views.callToolbar.setSubtitle(R.string.call_connecting) } } - is CallState.Ended -> { + is CallState.Ended -> { finish() } - null -> { + null -> { } } } @@ -331,12 +340,12 @@ class VectorCallActivity : VectorBaseActivity(), CallContro is CallState.CreateOffer, is CallState.LocalRinging, is CallState.Dialing, - is CallState.Answering -> { + is CallState.Answering -> { views.fullscreenRenderer.isVisible = false views.callInfoGroup.isVisible = false // showLoading() } - is CallState.Connected -> { + is CallState.Connected -> { if (callState.iceConnectionState == MxPeerConnectionState.CONNECTED) { if (state.isLocalOnHold || state.isRemoteOnHold) { views.smallIsHeldIcon.isVisible = true @@ -403,10 +412,13 @@ class VectorCallActivity : VectorBaseActivity(), CallContro views.otherKnownCallLayout.setOnClickListener { withState(callViewModel) { val otherCall = callManager.getCallById(it.otherKnownCallInfo?.callId ?: "") ?: return@withState - startActivity(newIntent(this, otherCall, null)) - finish() + val callArgs = CallArgs(otherCall.nativeRoomId, otherCall.callId, otherCall.mxCall.opponentUserId, !otherCall.mxCall.isOutgoing, otherCall.mxCall.isVideoCall) + callViewModel.handle(VectorCallViewActions.SwitchCall(callArgs)) } } + views.pipRendererWrapper.setOnClickListener { + callViewModel.handle(VectorCallViewActions.ToggleCamera) + } pipDraggrableView = views.pipRendererWrapper.setupDraggable() .setStickyMode(DraggableView.Mode.STICKY_XY) .build() @@ -418,14 +430,15 @@ class VectorCallActivity : VectorBaseActivity(), CallContro private val permissionCameraLauncher = registerForPermissionsResult { allGranted, _ -> if (allGranted) { - start() + setupRenderersIfNeeded() } else { // TODO display something finish() } } - private fun start() { + private fun setupRenderersIfNeeded() { + detachRenderersIfNeeded() rootEglBase = EglUtils.rootEglBase ?: return Unit.also { Timber.tag(loggerTag.value).v("rootEglBase is null") finish() @@ -443,11 +456,10 @@ class VectorCallActivity : VectorBaseActivity(), CallContro views.fullscreenRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL) views.fullscreenRenderer.setEnableHardwareScaler(true /* enabled */) - callManager.getCallById(callArgs.callId)?.attachViewRenderers(views.pipRenderer, views.fullscreenRenderer, - intent.getStringExtra(EXTRA_MODE)?.takeIf { isFirstCreation() }) - - views.pipRendererWrapper.setOnClickListener { - callViewModel.handle(VectorCallViewActions.ToggleCamera) + val callId = withState(callViewModel) { it.callId } + callManager.getCallById(callId)?.also { webRtcCall -> + webRtcCall.attachViewRenderers(views.pipRenderer, views.fullscreenRenderer, intent.getStringExtra(EXTRA_MODE)) + intent.removeExtra(EXTRA_MODE) } surfaceRenderersAreInitialized = true } @@ -467,7 +479,8 @@ class VectorCallActivity : VectorBaseActivity(), CallContro }.show(supportFragmentManager, FRAGMENT_DIAL_PAD_TAG) } is VectorCallViewEvents.ShowCallTransferScreen -> { - navigator.openCallTransfer(this, callArgs.callId) + val callId = withState(callViewModel) { it.callId } + navigator.openCallTransfer(this, callId) } null -> { } @@ -486,39 +499,6 @@ class VectorCallActivity : VectorBaseActivity(), CallContro .show() } - companion object { - private const val EXTRA_MODE = "EXTRA_MODE" - private const val FRAGMENT_DIAL_PAD_TAG = "FRAGMENT_DIAL_PAD_TAG" - - const val OUTGOING_CREATED = "OUTGOING_CREATED" - const val INCOMING_RINGING = "INCOMING_RINGING" - const val INCOMING_ACCEPT = "INCOMING_ACCEPT" - - fun newIntent(context: Context, call: WebRtcCall, mode: String?): Intent { - return Intent(context, VectorCallActivity::class.java).apply { - // what could be the best flags? - flags = Intent.FLAG_ACTIVITY_NEW_TASK - putExtra(MvRx.KEY_ARG, CallArgs(call.nativeRoomId, call.callId, call.mxCall.opponentUserId, !call.mxCall.isOutgoing, call.mxCall.isVideoCall)) - putExtra(EXTRA_MODE, mode) - } - } - - fun newIntent(context: Context, - callId: String, - signalingRoomId: String, - otherUserId: String, - isIncomingCall: Boolean, - isVideoCall: Boolean, - mode: String?): Intent { - return Intent(context, VectorCallActivity::class.java).apply { - // what could be the best flags? - flags = FLAG_ACTIVITY_CLEAR_TOP - putExtra(MvRx.KEY_ARG, CallArgs(signalingRoomId, callId, otherUserId, isIncomingCall, isVideoCall)) - putExtra(EXTRA_MODE, mode) - } - } - } - override fun didTapAudioSettings() { CallSoundDeviceChooserBottomSheet().show(supportFragmentManager, "SoundDeviceChooser") } @@ -544,7 +524,8 @@ class VectorCallActivity : VectorBaseActivity(), CallContro } private fun returnToChat() { - val args = RoomDetailArgs(callArgs.signalingRoomId) + val roomId = withState(callViewModel) { it.roomId } + val args = RoomDetailArgs(roomId) val intent = RoomDetailActivity.newIntent(this, args).apply { flags = FLAG_ACTIVITY_CLEAR_TOP } @@ -592,4 +573,37 @@ class VectorCallActivity : VectorBaseActivity(), CallContro ) } } + + companion object { + private const val EXTRA_MODE = "EXTRA_MODE" + private const val FRAGMENT_DIAL_PAD_TAG = "FRAGMENT_DIAL_PAD_TAG" + + const val OUTGOING_CREATED = "OUTGOING_CREATED" + const val INCOMING_RINGING = "INCOMING_RINGING" + const val INCOMING_ACCEPT = "INCOMING_ACCEPT" + + fun newIntent(context: Context, call: WebRtcCall, mode: String?): Intent { + return Intent(context, VectorCallActivity::class.java).apply { + // what could be the best flags? + flags = Intent.FLAG_ACTIVITY_NEW_TASK + putExtra(MvRx.KEY_ARG, CallArgs(call.nativeRoomId, call.callId, call.mxCall.opponentUserId, !call.mxCall.isOutgoing, call.mxCall.isVideoCall)) + putExtra(EXTRA_MODE, mode) + } + } + + fun newIntent(context: Context, + callId: String, + signalingRoomId: String, + otherUserId: String, + isIncomingCall: Boolean, + isVideoCall: Boolean, + mode: String?): Intent { + return Intent(context, VectorCallActivity::class.java).apply { + // what could be the best flags? + flags = Intent.FLAG_ACTIVITY_NEW_TASK + putExtra(MvRx.KEY_ARG, CallArgs(signalingRoomId, callId, otherUserId, isIncomingCall, isVideoCall)) + putExtra(EXTRA_MODE, mode) + } + } + } } diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewActions.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewActions.kt index a332153aaa..1834c05e41 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewActions.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewActions.kt @@ -29,6 +29,8 @@ sealed class VectorCallViewActions : VectorViewModelAction { data class ChangeAudioDevice(val device: CallAudioManager.Device) : VectorCallViewActions() object OpenDialPad: VectorCallViewActions() data class SendDtmfDigit(val digit: String) : VectorCallViewActions() + data class SwitchCall(val callArgs: CallArgs) : VectorCallViewActions() + object SwitchSoundDevice : VectorCallViewActions() object HeadSetButtonPressed : VectorCallViewActions() object ToggleCamera : VectorCallViewActions() 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 8b96583406..cfe66c187e 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 @@ -174,7 +174,12 @@ class VectorCallViewModel @AssistedInject constructor( } init { - val webRtcCall = callManager.getCallById(initialState.callId) + setupCallWithCurrentState() + } + + private fun setupCallWithCurrentState() = withState { state -> + call?.removeListener(callListener) + val webRtcCall = callManager.getCallById(state.callId) if (webRtcCall == null) { setState { copy(callState = Fail(IllegalArgumentException("No call"))) @@ -229,6 +234,7 @@ class VectorCallViewModel @AssistedInject constructor( override fun onCleared() { callManager.removeCurrentCallListener(currentCallListener) call?.removeListener(callListener) + call = null proximityManager.stop() super.onCleared() } @@ -306,9 +312,13 @@ class VectorCallViewModel @AssistedInject constructor( VectorCallViewEvents.ShowCallTransferScreen ) } - VectorCallViewActions.TransferCall -> { + VectorCallViewActions.TransferCall -> { handleCallTransfer() } + is VectorCallViewActions.SwitchCall -> { + setState { VectorCallViewState(action.callArgs) } + setupCallWithCurrentState() + } }.exhaustive } 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 1f0f7969de..db4b313f86 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 @@ -348,7 +348,7 @@ class WebRtcCall( fun attachViewRenderers(localViewRenderer: SurfaceViewRenderer?, remoteViewRenderer: SurfaceViewRenderer, mode: String?) { sessionScope?.launch(dispatcher) { - Timber.tag(loggerTag.value).v("attachViewRenderers localRendeder $localViewRenderer / $remoteViewRenderer") + Timber.tag(loggerTag.value).v("attachViewRenderers localRenderer $localViewRenderer / $remoteViewRenderer") localSurfaceRenderers.addIfNeeded(localViewRenderer) remoteSurfaceRenderers.addIfNeeded(remoteViewRenderer) when (mode) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt index 895f1a66ff..476cda5a7e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt @@ -358,7 +358,7 @@ class NoticeEventFormatter @Inject constructor( } EventType.CALL_REJECT -> if (event.isSentByCurrentUser()) { - sp.getString(R.string.call_tile_you_declined) + sp.getString(R.string.call_tile_you_declined,"") } else { sp.getString(R.string.call_tile_other_declined, senderName) }