Prevent being stuck in full screen mode without video

This commit is contained in:
Sylvain Berfini 2024-11-14 11:38:29 +01:00
parent a7fb2ccfec
commit eb80a16202
11 changed files with 79 additions and 34 deletions

View file

@ -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(

View file

@ -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()
}
}
}

View file

@ -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}]"
)
}

View file

@ -75,6 +75,8 @@ class ConferenceViewModel @UiThread constructor() : GenericViewModel() {
val isConversationAvailable = MutableLiveData<Boolean>()
val fullScreenMode = MutableLiveData<Boolean>()
val firstParticipantOtherThanOurselvesJoinedEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
@ -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()) {

View file

@ -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()

View file

@ -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)

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -44,6 +44,7 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraint_layout"
android:onClick="@{() -> viewModel.conferenceModel.toggleFullScreen()}"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/gray_900">
@ -235,7 +236,7 @@
<androidx.fragment.app.FragmentContainerView
android:id="@+id/conference_layout_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:onClick="@{() -> viewModel.toggleFullScreen()}"
android:onClick="@{() -> viewModel.conferenceModel.toggleFullScreen()}"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="@{viewModel.fullScreenMode || viewModel.pipMode ? @dimen/zero : @dimen/call_main_actions_menu_margin, default=@dimen/call_main_actions_menu_margin}"

View file

@ -145,11 +145,9 @@
<HorizontalScrollView
android:id="@+id/active_speaker_miniatures_horizontal_layout"
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
app:layout_constraintBottom_toBottomOf="parent">
<LinearLayout
android:layout_width="wrap_content"