From eb80a16202028a5a567b7230e1a816c53a8b852d Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Thu, 14 Nov 2024 11:38:29 +0100 Subject: [PATCH] Prevent being stuck in full screen mode without video --- .../notifications/NotificationsManager.kt | 2 +- .../fragment/ActiveConferenceCallFragment.kt | 18 ++++++--- .../model/ConferenceParticipantDeviceModel.kt | 2 +- .../viewmodel/ConferenceViewModel.kt | 39 ++++++++++++++++--- .../ui/call/viewmodel/CurrentCallViewModel.kt | 31 ++++++++++----- .../fragment/ConversationsListFragment.kt | 2 +- .../viewmodel/ContactsListViewModel.kt | 2 +- .../meetings/viewmodel/MeetingViewModel.kt | 2 +- .../java/org/linphone/utils/AudioUtils.kt | 6 +-- .../call_active_conference_fragment.xml | 3 +- ...all_conference_active_speaker_fragment.xml | 6 +-- 11 files changed, 79 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/org/linphone/notifications/NotificationsManager.kt b/app/src/main/java/org/linphone/notifications/NotificationsManager.kt index a10b95924..700efd1fb 100644 --- a/app/src/main/java/org/linphone/notifications/NotificationsManager.kt +++ b/app/src/main/java/org/linphone/notifications/NotificationsManager.kt @@ -1001,7 +1001,7 @@ class NotificationsManager @MainThread constructor(private val context: Context) } Log.i( - "Creating notification for ${if (isIncoming) "incoming" else "outgoing"} ${if (isConference) "conference" else "call"} with video ${if (isVideo) "enabled" else "disabled"} on channel [$channel]" + "Creating notification for [${if (isIncoming) "incoming" else "outgoing"}] [${if (isConference) "conference" else "call"}] with video [${if (isVideo) "enabled" else "disabled"}] on channel [$channel]" ) val builder = NotificationCompat.Builder( diff --git a/app/src/main/java/org/linphone/ui/call/conference/fragment/ActiveConferenceCallFragment.kt b/app/src/main/java/org/linphone/ui/call/conference/fragment/ActiveConferenceCallFragment.kt index 017321f4f..cd715c677 100644 --- a/app/src/main/java/org/linphone/ui/call/conference/fragment/ActiveConferenceCallFragment.kt +++ b/app/src/main/java/org/linphone/ui/call/conference/fragment/ActiveConferenceCallFragment.kt @@ -202,11 +202,15 @@ class ActiveConferenceCallFragment : GenericCallFragment() { } } - callViewModel.fullScreenMode.observe(viewLifecycleOwner) { hide -> - Log.i("$TAG Switching full screen mode to ${if (hide) "ON" else "OFF"}") + callViewModel.conferenceModel.fullScreenMode.observe(viewLifecycleOwner) { hide -> + Log.i("$TAG Switching full screen mode to [${if (hide) "ON" else "OFF"}]") sharedViewModel.toggleFullScreenEvent.value = Event(hide) callStatsBottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN callMediaEncryptionStatsBottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN + + if (hide != callViewModel.fullScreenMode.value) { + callViewModel.fullScreenMode.value = hide + } } callViewModel.conferenceModel.conferenceLayout.observe(viewLifecycleOwner) { layout -> @@ -220,8 +224,10 @@ class ActiveConferenceCallFragment : GenericCallFragment() { Log.i("$TAG We are alone in that conference, using nativePreviewWindowId") core.nativePreviewWindowId = binding.localPreviewVideoSurface - // Don't forget to leave full screen mode, otherwise we won't be able to leave it by touching video surface... - callViewModel.fullScreenMode.postValue(false) + if (callViewModel.conferenceModel.fullScreenMode.value == true && callViewModel.conferenceModel.isMeParticipantSendingVideo.value == false) { + // Don't forget to leave full screen mode, otherwise we won't be able to leave it by touching video surface... + callViewModel.conferenceModel.fullScreenMode.postValue(false) + } } } } @@ -230,9 +236,9 @@ class ActiveConferenceCallFragment : GenericCallFragment() { viewLifecycleOwner ) { it.consume { - if (callViewModel.fullScreenMode.value == false) { + if (callViewModel.conferenceModel.fullScreenMode.value == false) { Log.i("$TAG First participant joined conference, switching to full screen mode") - callViewModel.toggleFullScreen() + callViewModel.conferenceModel.toggleFullScreen() } } } diff --git a/app/src/main/java/org/linphone/ui/call/conference/model/ConferenceParticipantDeviceModel.kt b/app/src/main/java/org/linphone/ui/call/conference/model/ConferenceParticipantDeviceModel.kt index 91e55b118..145c0e3ce 100644 --- a/app/src/main/java/org/linphone/ui/call/conference/model/ConferenceParticipantDeviceModel.kt +++ b/app/src/main/java/org/linphone/ui/call/conference/model/ConferenceParticipantDeviceModel.kt @@ -177,7 +177,7 @@ class ConferenceParticipantDeviceModel @WorkerThread constructor( ) } else { Log.i( - "$TAG Participant [${device.address.asStringUriOnly()}] is ${if (inConference) "inside" else "outside"} the conference with state [${device.state}]" + "$TAG Participant [${device.address.asStringUriOnly()}] is [${if (inConference) "inside" else "outside"}] the conference with state [${device.state}]" ) } diff --git a/app/src/main/java/org/linphone/ui/call/conference/viewmodel/ConferenceViewModel.kt b/app/src/main/java/org/linphone/ui/call/conference/viewmodel/ConferenceViewModel.kt index f3bb9e108..5089c66d1 100644 --- a/app/src/main/java/org/linphone/ui/call/conference/viewmodel/ConferenceViewModel.kt +++ b/app/src/main/java/org/linphone/ui/call/conference/viewmodel/ConferenceViewModel.kt @@ -75,6 +75,8 @@ class ConferenceViewModel @UiThread constructor() : GenericViewModel() { val isConversationAvailable = MutableLiveData() + val fullScreenMode = MutableLiveData() + val firstParticipantOtherThanOurselvesJoinedEvent: MutableLiveData> by lazy { MutableLiveData>() } @@ -123,8 +125,7 @@ class ConferenceViewModel @UiThread constructor() : GenericViewModel() { if (conference.isMe(device.address)) { val direction = device.getStreamCapability(StreamType.Video) val sendingVideo = direction == MediaDirection.SendRecv || direction == MediaDirection.SendOnly - isMeParticipantSendingVideo.postValue(sendingVideo) - Log.i("$TAG We ${if (sendingVideo) "are" else "aren't"} sending video") + localVideoStreamToggled(sendingVideo) } } @@ -255,6 +256,8 @@ class ConferenceViewModel @UiThread constructor() : GenericViewModel() { init { isPaused.value = false isConversationAvailable.value = false + isMeParticipantSendingVideo.value = false + fullScreenMode.value = false } @WorkerThread @@ -283,7 +286,7 @@ class ConferenceViewModel @UiThread constructor() : GenericViewModel() { isPaused.postValue(!isIn) } Log.i( - "$TAG We ${if (isIn) "are" else "aren't"} in the conference right now, current state is [$state]" + "$TAG We [${if (isIn) "are" else "aren't"}] in the conference right now, current state is [$state]" ) val screenSharing = conference.screenSharingParticipant != null @@ -317,6 +320,33 @@ class ConferenceViewModel @UiThread constructor() : GenericViewModel() { } } + @UiThread + fun toggleFullScreen() { + if (fullScreenMode.value == true) { + // Always allow to switch off full screen mode + fullScreenMode.value = false + return + } + + if (conferenceLayout.value == AUDIO_ONLY_LAYOUT) { + // Do not allow turning full screen on for audio only conference + return + } + + if (isMeParticipantSendingVideo.value == false && participants.value.orEmpty().size == 1) { + // Do not allow turning full screen on if we're alone and not sending our video + return + } + + fullScreenMode.value = true + } + + @WorkerThread + fun localVideoStreamToggled(enabled: Boolean) { + isMeParticipantSendingVideo.postValue(enabled) + Log.i("$TAG We [${if (enabled) "are" else "aren't"}] sending video") + } + @UiThread fun goToConversation() { coreContext.postOnCoreThread { core -> @@ -559,8 +589,7 @@ class ConferenceViewModel @UiThread constructor() : GenericViewModel() { val direction = device.getStreamCapability(StreamType.Video) val sendingVideo = direction == MediaDirection.SendRecv || direction == MediaDirection.SendOnly - isMeParticipantSendingVideo.postValue(sendingVideo) - Log.i("$TAG We ${if (sendingVideo) "are" else "aren't"} sending video right now") + localVideoStreamToggled(sendingVideo) } if (!activeSpeakerParticipantDeviceFound && devicesList.isNotEmpty()) { 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 31142ff90..ce37436bb 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 @@ -788,6 +788,9 @@ class CurrentCallViewModel @UiThread constructor() : GenericViewModel() { params?.videoDirection = MediaDirection.SendRecv } } + + val sendingVideo = params?.videoDirection == MediaDirection.SendRecv || params?.videoDirection == MediaDirection.SendOnly + conferenceModel.localVideoStreamToggled(sendingVideo) } else if (params != null) { params.isVideoEnabled = true params.videoDirection = when (currentCall.currentParams.videoDirection) { @@ -857,8 +860,17 @@ class CurrentCallViewModel @UiThread constructor() : GenericViewModel() { @UiThread fun toggleFullScreen() { - if (fullScreenMode.value == false && isVideoEnabled.value == false) return - fullScreenMode.value = fullScreenMode.value != true + if (fullScreenMode.value == true) { + // Always allow to switch off full screen mode + fullScreenMode.value = false + return + } + + if (isVideoEnabled.value == false) { + // Do not allow turning full screen on for audio only calls + return + } + fullScreenMode.value = true } @UiThread @@ -1214,19 +1226,18 @@ class CurrentCallViewModel @UiThread constructor() : GenericViewModel() { isReceivingVideo.postValue(isReceiving) } - if (((isReceiving) && !wasReceiving)) { - if (fullScreenMode.value != true) { - val inConference = conferenceModel.isCurrentCallInConference.value == true - if (!inConference) { + if (currentCall.conference == null) { // Let conference view model handle full screen while in conference + if (isReceiving && !wasReceiving) { // Do not change full screen mode base on our video being sent when it wasn't + if (fullScreenMode.value != true) { Log.i( - "$TAG Video is being received (it wasn't before), switching to full-screen mode" + "$TAG Video is being received or sent (and it wasn't before), switching to full-screen mode" ) fullScreenMode.postValue(true) } + } else if (!isSending && !isReceiving && fullScreenMode.value == true) { + Log.w("$TAG Video is no longer sent nor received, leaving full screen mode") + fullScreenMode.postValue(false) } - } else if (!isSending && !isReceiving && fullScreenMode.value == true) { - Log.w("$TAG Video is no longer enabled, leaving full screen mode") - fullScreenMode.postValue(false) } updateProximitySensor() diff --git a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsListFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsListFragment.kt index ab26aaaaa..9cb4eaf2e 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsListFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsListFragment.kt @@ -242,7 +242,7 @@ class ConversationsListFragment : AbstractMainFragment() { } Log.i( - "$TAG Navigating to ${if (isMedia) "media" else "file"} viewer fragment with path [$path]" + "$TAG Navigating to [${if (isMedia) "media" else "file"}] viewer fragment with path [$path]" ) if (isMedia) { val intent = Intent(requireActivity(), MediaViewerActivity::class.java) diff --git a/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactsListViewModel.kt b/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactsListViewModel.kt index 38a1de817..4f54d1074 100644 --- a/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactsListViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactsListViewModel.kt @@ -219,7 +219,7 @@ class ContactsListViewModel @UiThread constructor() : AbstractMainViewModel() { contactModel.friend.edit() val starred = !contactModel.friend.starred Log.i( - "$TAG Friend [${contactModel.name.value}] will be ${if (starred) "added to" else "removed from"} favourites" + "$TAG Friend [${contactModel.name.value}] will be [${if (starred) "added to" else "removed from"}] favourites" ) contactModel.friend.starred = starred contactModel.friend.done() diff --git a/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/MeetingViewModel.kt b/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/MeetingViewModel.kt index 2b3f3b18c..2d1fad61a 100644 --- a/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/MeetingViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/MeetingViewModel.kt @@ -266,7 +266,7 @@ class MeetingViewModel @UiThread constructor() : GenericViewModel() { val participant = info.address val isOrganizer = organizer?.weakEqual(participant) == true Log.d( - "$TAG Conference [${conferenceInfo.subject}] ${if (isOrganizer) "organizer" else "participant"} [${participant.asStringUriOnly()}] is a [${info.role}]" + "$TAG Conference [${conferenceInfo.subject}] [${if (isOrganizer) "organizer" else "participant"}] [${participant.asStringUriOnly()}] is a [${info.role}]" ) if (isOrganizer) { organizerFound = true diff --git a/app/src/main/java/org/linphone/utils/AudioUtils.kt b/app/src/main/java/org/linphone/utils/AudioUtils.kt index aee8d5608..d3057923d 100644 --- a/app/src/main/java/org/linphone/utils/AudioUtils.kt +++ b/app/src/main/java/org/linphone/utils/AudioUtils.kt @@ -117,7 +117,7 @@ class AudioUtils { val extendedAudioDevices = coreContext.core.extendedAudioDevices Log.i( - "$TAG Looking for an ${if (output) "output" else "input"} audio device with capability [$capability], driver name [$preferredDriver] and type [$types] in extended audio devices list (size ${extendedAudioDevices.size})" + "$TAG Looking for an [${if (output) "output" else "input"}] audio device with capability [$capability], driver name [$preferredDriver] and type [$types] in extended audio devices list (size ${extendedAudioDevices.size})" ) val foundAudioDevice = extendedAudioDevices.find { it.driverName == preferredDriver && types.contains(it.type) && it.hasCapability( @@ -143,7 +143,7 @@ class AudioUtils { } if (call != null) { Log.i( - "$TAG Found [${audioDevice.type}] ${if (output) "playback" else "recorder"} audio device [${audioDevice.deviceName} (${audioDevice.driverName})], routing call audio to it" + "$TAG Found [${audioDevice.type}] [${if (output) "playback" else "recorder"}] audio device [${audioDevice.deviceName} (${audioDevice.driverName})], routing call audio to it" ) if (output) { call.outputAudioDevice = audioDevice @@ -152,7 +152,7 @@ class AudioUtils { } } else { Log.i( - "$TAG Found [${audioDevice.type}] ${if (output) "playback" else "recorder"} audio device [${audioDevice.deviceName} (${audioDevice.driverName})], changing core default audio device" + "$TAG Found [${audioDevice.type}] [${if (output) "playback" else "recorder"}] audio device [${audioDevice.deviceName} (${audioDevice.driverName})], changing core default audio device" ) if (output) { coreContext.core.outputAudioDevice = audioDevice diff --git a/app/src/main/res/layout/call_active_conference_fragment.xml b/app/src/main/res/layout/call_active_conference_fragment.xml index cde62f8c1..6ddf65ca0 100644 --- a/app/src/main/res/layout/call_active_conference_fragment.xml +++ b/app/src/main/res/layout/call_active_conference_fragment.xml @@ -44,6 +44,7 @@ @@ -235,7 +236,7 @@ + app:layout_constraintBottom_toBottomOf="parent">