From 9c1b9b29390e65a5e854c5f8db6294315d838646 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Fri, 23 Feb 2024 11:56:30 +0100 Subject: [PATCH] Using new APIs to be able to make asymetrical video calls --- app/src/main/assets/linphonerc_default | 3 ++ app/src/main/assets/linphonerc_factory | 2 -- .../java/org/linphone/core/CoreContext.kt | 36 ++++++++++++++++++- .../ui/call/fragment/ActiveCallFragment.kt | 18 ++++++++-- .../ui/call/fragment/NewCallFragment.kt | 2 +- .../ui/call/viewmodel/CurrentCallViewModel.kt | 28 +++++++++++++-- .../java/org/linphone/ui/main/MainActivity.kt | 2 +- .../ui/main/chat/model/ConversationModel.kt | 2 +- .../ui/main/chat/model/MessageModel.kt | 2 +- .../viewmodel/ConversationInfoViewModel.kt | 6 ++-- .../chat/viewmodel/ConversationViewModel.kt | 6 ++-- .../contacts/viewmodel/ContactViewModel.kt | 20 ++++------- .../history/fragment/HistoryListFragment.kt | 2 +- .../history/fragment/StartCallFragment.kt | 2 +- .../viewmodel/ContactHistoryViewModel.kt | 8 ++--- .../history/viewmodel/StartCallViewModel.kt | 4 +-- .../viewmodel/AddressSelectionViewModel.kt | 2 +- .../main/res/layout/call_actions_generic.xml | 2 +- .../main/res/layout/call_active_fragment.xml | 6 ++-- 19 files changed, 104 insertions(+), 49 deletions(-) diff --git a/app/src/main/assets/linphonerc_default b/app/src/main/assets/linphonerc_default index 04268cf53..d217cc38d 100644 --- a/app/src/main/assets/linphonerc_default +++ b/app/src/main/assets/linphonerc_default @@ -19,6 +19,9 @@ upload_bw=0 [video] size=vga +automatically_accept=1 +automatically_initiate=0 +automatically_accept_direction=2 #receive only [app] tunnel=disabled diff --git a/app/src/main/assets/linphonerc_factory b/app/src/main/assets/linphonerc_factory index fc2954596..c1269d081 100644 --- a/app/src/main/assets/linphonerc_factory +++ b/app/src/main/assets/linphonerc_factory @@ -36,8 +36,6 @@ android_disable_audio_focus_requests=1 displaytype=MSAndroidTextureDisplay auto_resize_preview_to_keep_ratio=1 max_conference_size=vga -automatically_accept=1 -automatically_initiate=0 [misc] enable_basic_to_client_group_chat_room_migration=0 diff --git a/app/src/main/java/org/linphone/core/CoreContext.kt b/app/src/main/java/org/linphone/core/CoreContext.kt index 67b2827d5..6b8b1b064 100644 --- a/app/src/main/java/org/linphone/core/CoreContext.kt +++ b/app/src/main/java/org/linphone/core/CoreContext.kt @@ -249,6 +249,16 @@ class CoreContext @UiThread constructor(val context: Context) : HandlerThread("C Log.i("$TAG Configuring Core") core.videoCodecPriorityPolicy = CodecPriorityPolicy.Auto + val oldVersion = corePreferences.linphoneConfigurationVersion + val newVersion = "6.0.0" + if (oldVersion == "5.2") { + Log.i("$TAG Migrating configuration from [$oldVersion] to [$newVersion]") + val policy = core.videoActivationPolicy.clone() + policy.automaticallyAccept = true + policy.automaticallyAcceptDirection = MediaDirection.RecvOnly + core.videoActivationPolicy = policy + } + updateFriendListsSubscriptionDependingOnDefaultAccount() computeUserAgent() @@ -262,7 +272,7 @@ class CoreContext @UiThread constructor(val context: Context) : HandlerThread("C val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager audioManager.registerAudioDeviceCallback(audioDeviceCallback, coreThread) - corePreferences.linphoneConfigurationVersion = "6.0" + corePreferences.linphoneConfigurationVersion = newVersion Log.i("$TAG Report Core created and started") } @@ -354,6 +364,30 @@ class CoreContext @UiThread constructor(val context: Context) : HandlerThread("C return found != null } + @WorkerThread + fun startAudioCall( + address: Address, + forceZRTP: Boolean = false, + localAddress: Address? = null + ) { + val params = core.createCallParams(null) + params?.isVideoEnabled = true + params?.videoDirection = MediaDirection.Inactive + startCall(address, params, forceZRTP, localAddress) + } + + @WorkerThread + fun startVideoCall( + address: Address, + forceZRTP: Boolean = false, + localAddress: Address? = null + ) { + val params = core.createCallParams(null) + params?.isVideoEnabled = true + params?.videoDirection = MediaDirection.SendRecv + startCall(address, params, forceZRTP, localAddress) + } + @WorkerThread fun startCall( address: Address, diff --git a/app/src/main/java/org/linphone/ui/call/fragment/ActiveCallFragment.kt b/app/src/main/java/org/linphone/ui/call/fragment/ActiveCallFragment.kt index c2a72c9aa..048dbe310 100644 --- a/app/src/main/java/org/linphone/ui/call/fragment/ActiveCallFragment.kt +++ b/app/src/main/java/org/linphone/ui/call/fragment/ActiveCallFragment.kt @@ -335,6 +335,22 @@ class ActiveCallFragment : GenericCallFragment() { } } + callViewModel.isSendingVideo.observe(viewLifecycleOwner) { sending -> + coreContext.core.nativePreviewWindowId = if (sending) { + binding.localPreviewVideoSurface + } else { + null + } + } + + callViewModel.isReceivingVideo.observe(viewLifecycleOwner) { receiving -> + coreContext.core.nativeVideoWindowId = if (receiving) { + binding.remoteVideoSurface + } else { + null + } + } + callViewModel.chatRoomCreationErrorEvent.observe(viewLifecycleOwner) { it.consume { error -> (requireActivity() as CallActivity).showRedToast( @@ -368,8 +384,6 @@ class ActiveCallFragment : GenericCallFragment() { super.onResume() coreContext.postOnCoreThread { core -> - core.nativeVideoWindowId = binding.remoteVideoSurface - coreContext.core.nativePreviewWindowId = binding.localPreviewVideoSurface binding.localPreviewVideoSurface.setOnTouchListener(previewTouchListener) // Need to be done manually diff --git a/app/src/main/java/org/linphone/ui/call/fragment/NewCallFragment.kt b/app/src/main/java/org/linphone/ui/call/fragment/NewCallFragment.kt index 13b344315..549379c63 100644 --- a/app/src/main/java/org/linphone/ui/call/fragment/NewCallFragment.kt +++ b/app/src/main/java/org/linphone/ui/call/fragment/NewCallFragment.kt @@ -39,7 +39,7 @@ class NewCallFragment : AbstractNewTransferCallFragment() { @WorkerThread override fun action(address: Address) { Log.i("$TAG Calling [${address.asStringUriOnly()}]") - coreContext.startCall(address) + coreContext.startAudioCall(address) coreContext.postOnMainThread { findNavController().popBackStack() diff --git a/app/src/main/java/org/linphone/ui/call/viewmodel/CurrentCallViewModel.kt b/app/src/main/java/org/linphone/ui/call/viewmodel/CurrentCallViewModel.kt index 7baf5b2af..d5c975ec8 100644 --- a/app/src/main/java/org/linphone/ui/call/viewmodel/CurrentCallViewModel.kt +++ b/app/src/main/java/org/linphone/ui/call/viewmodel/CurrentCallViewModel.kt @@ -76,6 +76,10 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { val isVideoEnabled = MutableLiveData() + val isSendingVideo = MutableLiveData() + + val isReceivingVideo = MutableLiveData() + val showSwitchCamera = MutableLiveData() val isOutgoing = MutableLiveData() @@ -230,6 +234,7 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { Log.i("$TAG Call [${call.remoteAddress.asStringUriOnly()}] state changed [$state]") if (LinphoneUtils.isCallOutgoing(call.state)) { isVideoEnabled.postValue(call.params.isVideoEnabled) + updateVideoDirection(call.currentParams.videoDirection) } else if (LinphoneUtils.isCallEnding(call.state)) { // If current call is being terminated but there is at least one other call, switch val core = call.core @@ -270,6 +275,7 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { } } isVideoEnabled.postValue(videoEnabled) + updateVideoDirection(call.currentParams.videoDirection) // Toggle full screen OFF when remote disables video if (!videoEnabled && fullScreenMode.value == true) { @@ -606,10 +612,15 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { params?.videoDirection = MediaDirection.SendRecv } } - } else { - params?.isVideoEnabled = params?.isVideoEnabled == false + } else if (params != null) { + params.isVideoEnabled = true + params.videoDirection = when (currentCall.currentParams.videoDirection) { + MediaDirection.RecvOnly -> MediaDirection.SendRecv + MediaDirection.SendRecv, MediaDirection.SendOnly -> MediaDirection.RecvOnly + else -> MediaDirection.SendOnly + } Log.i( - "$TAG Updating call with video enabled set to ${params?.isVideoEnabled}" + "$TAG Updating call with video enabled and media direction set to ${params.videoDirection}" ) } currentCall.update(params) @@ -967,6 +978,7 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { } else { isVideoEnabled.postValue(call.currentParams.isVideoEnabled) } + updateVideoDirection(call.currentParams.videoDirection) if (ActivityCompat.checkSelfPermission( coreContext.context, @@ -1056,6 +1068,16 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { } } + @WorkerThread + private fun updateVideoDirection(direction: MediaDirection) { + isSendingVideo.postValue( + direction == MediaDirection.SendRecv || direction == MediaDirection.SendOnly + ) + isReceivingVideo.postValue( + direction == MediaDirection.SendRecv || direction == MediaDirection.RecvOnly + ) + } + @AnyThread private fun updateCallQualityIcon() { viewModelScope.launch { diff --git a/app/src/main/java/org/linphone/ui/main/MainActivity.kt b/app/src/main/java/org/linphone/ui/main/MainActivity.kt index 21d6a161b..1b74cb401 100644 --- a/app/src/main/java/org/linphone/ui/main/MainActivity.kt +++ b/app/src/main/java/org/linphone/ui/main/MainActivity.kt @@ -622,7 +622,7 @@ class MainActivity : GenericActivity() { ) Log.i("$TAG Interpreted SIP URI is [${address?.asStringUriOnly()}]") if (address != null) { - coreContext.startCall(address) + coreContext.startAudioCall(address) } } } diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/ConversationModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/ConversationModel.kt index 80d1befe2..fb3d4e5da 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/model/ConversationModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/model/ConversationModel.kt @@ -217,7 +217,7 @@ class ConversationModel @WorkerThread constructor( coreContext.postOnCoreThread { val address = chatRoom.participants.firstOrNull()?.address ?: chatRoom.peerAddress Log.i("$TAG Calling [${address.asStringUriOnly()}]") - coreContext.startCall(address) + coreContext.startAudioCall(address) } } diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/MessageModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/MessageModel.kt index af7d090d8..689f6143c 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/model/MessageModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/model/MessageModel.kt @@ -557,7 +557,7 @@ class MessageModel @WorkerThread constructor( Log.i("$TAG Clicked on SIP URI: $text") val address = coreContext.core.interpretUrl(text, false) if (address != null) { - coreContext.startCall(address) + coreContext.startAudioCall(address) } else { Log.w("$TAG Failed to parse [$text] as SIP URI") } diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationInfoViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationInfoViewModel.kt index 029e6b85e..7d587f00c 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationInfoViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationInfoViewModel.kt @@ -207,7 +207,7 @@ class ConversationInfoViewModel @UiThread constructor() : AbstractConversationVi Log.i( "$TAG Conference info created, address is ${conferenceAddress.asStringUriOnly()}" ) - coreContext.startCall(conferenceAddress) + coreContext.startVideoCall(conferenceAddress) } else { Log.e("$TAG Conference info URI is null!") // TODO: notify error to user @@ -284,9 +284,7 @@ class ConversationInfoViewModel @UiThread constructor() : AbstractConversationVi val address = firstParticipant?.address if (address != null) { Log.i("$TAG Audio calling SIP address [${address.asStringUriOnly()}]") - val params = core.createCallParams(null) - params?.isVideoEnabled = false - coreContext.startCall(address, params) + coreContext.startAudioCall(address) } else { Log.e("$TAG Failed to find participant to call!") } diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt index e62d70d4d..c57e8a169 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt @@ -283,7 +283,7 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo Log.i( "$TAG Conference info created, address is ${conferenceAddress.asStringUriOnly()}" ) - coreContext.startCall(conferenceAddress) + coreContext.startVideoCall(conferenceAddress) } else { Log.e("$TAG Conference info URI is null!") // TODO: notify error to user @@ -394,9 +394,7 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo val address = firstParticipant?.address if (address != null) { Log.i("$TAG Audio calling SIP address [${address.asStringUriOnly()}]") - val params = core.createCallParams(null) - params?.isVideoEnabled = false - coreContext.startCall(address, params) + coreContext.startAudioCall(address) } } } diff --git a/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactViewModel.kt b/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactViewModel.kt index f151ca804..c5d8ec5d7 100644 --- a/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactViewModel.kt @@ -140,15 +140,11 @@ class ContactViewModel @UiThread constructor() : ViewModel() { when (expectedAction) { START_AUDIO_CALL -> { Log.i("$TAG Audio calling SIP address [${address.asStringUriOnly()}]") - val params = core.createCallParams(null) - params?.isVideoEnabled = false - coreContext.startCall(address, params) + coreContext.startAudioCall(address) } START_VIDEO_CALL -> { Log.i("$TAG Video calling SIP address [${address.asStringUriOnly()}]") - val params = core.createCallParams(null) - params?.isVideoEnabled = true - coreContext.startCall(address, params) + coreContext.startVideoCall(address) } START_CONVERSATION -> { Log.i( @@ -405,7 +401,7 @@ class ContactViewModel @UiThread constructor() : ViewModel() { "$TAG Only 1 SIP address found for contact [${friend.name}], starting audio call directly" ) val address = friend.addresses.first() - coreContext.startCall(address) + coreContext.startAudioCall(address) } else if (addressesCount == 0 && numbersCount == 1 && enablePhoneNumbers) { val number = friend.phoneNumbers.first() val address = core.interpretUrl(number, LinphoneUtils.applyInternationalPrefix()) @@ -413,7 +409,7 @@ class ContactViewModel @UiThread constructor() : ViewModel() { Log.i( "$TAG Only 1 phone number found for contact [${friend.name}], starting audio call directly" ) - coreContext.startCall(address) + coreContext.startAudioCall(address) } else { Log.e("$TAG Failed to interpret phone number [$number] as SIP address") } @@ -442,9 +438,7 @@ class ContactViewModel @UiThread constructor() : ViewModel() { "$TAG Only 1 SIP address found for contact [${friend.name}], starting video call directly" ) val address = friend.addresses.first() - val params = core.createCallParams(null) - params?.isVideoEnabled = true - coreContext.startCall(address, params) + coreContext.startVideoCall(address) } else if (addressesCount == 0 && numbersCount == 1 && enablePhoneNumbers) { val number = friend.phoneNumbers.first() val address = core.interpretUrl(number, LinphoneUtils.applyInternationalPrefix()) @@ -452,9 +446,7 @@ class ContactViewModel @UiThread constructor() : ViewModel() { Log.i( "$TAG Only 1 phone number found for contact [${friend.name}], starting video call directly" ) - val params = core.createCallParams(null) - params?.isVideoEnabled = true - coreContext.startCall(address, params) + coreContext.startVideoCall(address) } else { Log.e("$TAG Failed to interpret phone number [$number] as SIP address") } diff --git a/app/src/main/java/org/linphone/ui/main/history/fragment/HistoryListFragment.kt b/app/src/main/java/org/linphone/ui/main/history/fragment/HistoryListFragment.kt index 43e80fded..ea64a2460 100644 --- a/app/src/main/java/org/linphone/ui/main/history/fragment/HistoryListFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/history/fragment/HistoryListFragment.kt @@ -185,7 +185,7 @@ class HistoryListFragment : AbstractTopBarFragment() { ) } else { Log.i("$TAG Starting call to [${model.address.asStringUriOnly()}]") - coreContext.startCall(model.address) + coreContext.startAudioCall(model.address) } } } diff --git a/app/src/main/java/org/linphone/ui/main/history/fragment/StartCallFragment.kt b/app/src/main/java/org/linphone/ui/main/history/fragment/StartCallFragment.kt index bdbc32893..b353b542e 100644 --- a/app/src/main/java/org/linphone/ui/main/history/fragment/StartCallFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/history/fragment/StartCallFragment.kt @@ -150,7 +150,7 @@ class StartCallFragment : GenericAddressPickerFragment() { @WorkerThread override fun onSingleAddressSelected(address: Address, friend: Friend) { - coreContext.startCall(address) + coreContext.startAudioCall(address) } override fun onPause() { diff --git a/app/src/main/java/org/linphone/ui/main/history/viewmodel/ContactHistoryViewModel.kt b/app/src/main/java/org/linphone/ui/main/history/viewmodel/ContactHistoryViewModel.kt index 5d7e8c051..4ae819b5a 100644 --- a/app/src/main/java/org/linphone/ui/main/history/viewmodel/ContactHistoryViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/history/viewmodel/ContactHistoryViewModel.kt @@ -140,18 +140,14 @@ class ContactHistoryViewModel @UiThread constructor() : ViewModel() { @UiThread fun startAudioCall() { coreContext.postOnCoreThread { core -> - val params = core.createCallParams(null) - params?.isVideoEnabled = false - coreContext.startCall(address, params) + coreContext.startAudioCall(address) } } @UiThread fun startVideoCall() { coreContext.postOnCoreThread { core -> - val params = core.createCallParams(null) - params?.isVideoEnabled = true - coreContext.startCall(address, params) + coreContext.startVideoCall(address) } } diff --git a/app/src/main/java/org/linphone/ui/main/history/viewmodel/StartCallViewModel.kt b/app/src/main/java/org/linphone/ui/main/history/viewmodel/StartCallViewModel.kt index f24073b25..1ed1ce1a2 100644 --- a/app/src/main/java/org/linphone/ui/main/history/viewmodel/StartCallViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/history/viewmodel/StartCallViewModel.kt @@ -83,7 +83,7 @@ class StartCallViewModel @UiThread constructor() : AddressSelectionViewModel() { Log.i( "$TAG Conference info created, address is ${conferenceAddress.asStringUriOnly()}" ) - coreContext.startCall(conferenceAddress) + coreContext.startVideoCall(conferenceAddress) } else { Log.e("$TAG Conference info URI is null!") // TODO: notify error to user @@ -122,7 +122,7 @@ class StartCallViewModel @UiThread constructor() : AddressSelectionViewModel() { ) if (address != null) { Log.i("$TAG Calling [${address.asStringUriOnly()}]") - coreContext.startCall(address) + coreContext.startAudioCall(address) } else { Log.e("$TAG Failed to parse [$suggestion] as SIP address") } diff --git a/app/src/main/java/org/linphone/ui/main/viewmodel/AddressSelectionViewModel.kt b/app/src/main/java/org/linphone/ui/main/viewmodel/AddressSelectionViewModel.kt index 381b51ff4..a3ee23ac5 100644 --- a/app/src/main/java/org/linphone/ui/main/viewmodel/AddressSelectionViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/viewmodel/AddressSelectionViewModel.kt @@ -257,7 +257,7 @@ abstract class AddressSelectionViewModel @UiThread constructor() : DefaultAccoun } val model = ContactOrSuggestionModel(address) { - coreContext.startCall(address) + coreContext.startAudioCall(address) } suggestionsList.add(model) diff --git a/app/src/main/res/layout/call_actions_generic.xml b/app/src/main/res/layout/call_actions_generic.xml index 8106a3661..6796856dd 100644 --- a/app/src/main/res/layout/call_actions_generic.xml +++ b/app/src/main/res/layout/call_actions_generic.xml @@ -52,7 +52,7 @@ android:padding="@dimen/call_button_icon_padding" android:enabled="@{!viewModel.isPaused && !viewModel.isPausedByRemote}" android:visibility="@{viewModel.hideVideo ? View.GONE : View.VISIBLE}" - android:src="@{viewModel.isVideoEnabled ? @drawable/video_camera : @drawable/video_camera_slash, default=@drawable/video_camera}" + android:src="@{viewModel.isSendingVideo ? @drawable/video_camera : @drawable/video_camera_slash, default=@drawable/video_camera}" android:background="@drawable/in_call_button_background_red" app:tint="@color/in_call_button_tint_color" app:layout_constraintHorizontal_bias="1" diff --git a/app/src/main/res/layout/call_active_fragment.xml b/app/src/main/res/layout/call_active_fragment.xml index 1e731c345..86ccba1a8 100644 --- a/app/src/main/res/layout/call_active_fragment.xml +++ b/app/src/main/res/layout/call_active_fragment.xml @@ -105,7 +105,7 @@ android:layout_marginBottom="@{viewModel.fullScreenMode || viewModel.pipMode || viewModel.halfOpenedFolded ? @dimen/zero : @dimen/call_main_actions_menu_margin, default=@dimen/call_main_actions_menu_margin}" android:layout_marginTop="@{viewModel.fullScreenMode || viewModel.pipMode || viewModel.halfOpenedFolded ? @dimen/zero : @dimen/call_remote_video_top_margin, default=@dimen/call_remote_video_top_margin}" android:onClick="@{() -> viewModel.toggleFullScreen()}" - android:visibility="@{viewModel.isVideoEnabled && !(viewModel.isPaused || viewModel.isPausedByRemote) ? View.VISIBLE : View.GONE}" + android:visibility="@{viewModel.isReceivingVideo && !(viewModel.isPaused || viewModel.isPausedByRemote) ? View.VISIBLE : View.GONE}" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="@id/hinge_bottom" app:layout_constraintEnd_toEndOf="parent" @@ -192,7 +192,7 @@ android:layout_marginStart="5dp" android:layout_marginEnd="5dp" android:src="@drawable/camera_rotate" - android:visibility="@{!viewModel.fullScreenMode && !viewModel.pipMode && viewModel.isVideoEnabled && viewModel.showSwitchCamera ? View.VISIBLE : View.GONE}" + android:visibility="@{!viewModel.fullScreenMode && !viewModel.pipMode && viewModel.isSendingVideo && viewModel.showSwitchCamera ? View.VISIBLE : View.GONE}" app:tint="@color/white" app:layout_constraintTop_toTopOf="@id/back" app:layout_constraintBottom_toBottomOf="@id/back" @@ -262,7 +262,7 @@ android:layout_height="wrap_content" android:layout_marginEnd="10dp" android:layout_marginBottom="20dp" - android:visibility="@{viewModel.isVideoEnabled && !(viewModel.isPaused || viewModel.isPausedByRemote) ? View.VISIBLE : View.GONE}" + android:visibility="@{viewModel.isSendingVideo && !(viewModel.isPaused || viewModel.isPausedByRemote) ? View.VISIBLE : View.GONE}" app:alignTopRight="true" app:displayMode="black_bars" app:layout_constraintBottom_toBottomOf="@id/remote_video_surface"