Switch from audio only to active speaker using toggle video button (instead of disabling it)

This commit is contained in:
Sylvain Berfini 2022-11-16 15:03:33 +01:00
parent cf3b68cc1b
commit 791e27f479
4 changed files with 54 additions and 23 deletions

View file

@ -81,6 +81,15 @@ class ConferenceCallFragment : GenericFragment<VoipConferenceCallFragmentBinding
binding.statsViewModel = statsViewModel
conferenceViewModel.reloadConferenceFragmentEvent.observe(
viewLifecycleOwner
) {
it.consume {
Log.i("[Conference Call] Reloading fragment after toggling video ON while in AUDIO_ONLY layout")
refreshConferenceFragment()
}
}
conferenceViewModel.conferenceDisplayMode.observe(
viewLifecycleOwner
) { displayMode ->
@ -101,6 +110,16 @@ class ConferenceCallFragment : GenericFragment<VoipConferenceCallFragmentBinding
coreContext.core.nativeVideoWindowId = null
}
}
when (displayMode) {
ConferenceDisplayMode.AUDIO_ONLY -> {
controlsViewModel.fullScreenMode.value = false
}
else -> {
val conference = conferenceViewModel.conference.value
if (conference != null) switchToFullScreenIfPossible(conference)
}
}
}
conferenceViewModel.conferenceParticipantDevices.observe(
@ -111,7 +130,7 @@ class ConferenceCallFragment : GenericFragment<VoipConferenceCallFragmentBinding
it.size > conferenceViewModel.maxParticipantsForMosaicLayout
) {
Log.w("[Conference Call] More than ${conferenceViewModel.maxParticipantsForMosaicLayout} participants (${it.size}), forcing active speaker layout")
conferenceViewModel.changeLayout(ConferenceDisplayMode.ACTIVE_SPEAKER)
conferenceViewModel.changeLayout(ConferenceDisplayMode.ACTIVE_SPEAKER, false)
refreshConferenceFragment()
// Can't use SnackBar whilst changing fragment
Toast.makeText(requireContext(), R.string.conference_too_many_participants_for_mosaic_layout, Toast.LENGTH_LONG).show()
@ -149,20 +168,6 @@ class ConferenceCallFragment : GenericFragment<VoipConferenceCallFragmentBinding
}
}
conferenceViewModel.conferenceDisplayMode.observe(
viewLifecycleOwner
) { layout ->
when (layout) {
ConferenceDisplayMode.AUDIO_ONLY -> {
controlsViewModel.fullScreenMode.value = false
}
else -> {
val conference = conferenceViewModel.conference.value
if (conference != null) switchToFullScreenIfPossible(conference)
}
}
}
conferenceViewModel.firstToJoinEvent.observe(
viewLifecycleOwner
) {
@ -272,14 +277,14 @@ class ConferenceCallFragment : GenericFragment<VoipConferenceCallFragmentBinding
if (conference.currentParams.isVideoEnabled) {
when {
conference.me.devices.isEmpty() -> {
Log.w("[Conference Call] Conference has video enabled but either our device hasn't joined yet")
Log.i("[Conference Call] Conference has video enabled but our device hasn't joined yet")
}
conference.me.devices.find { it.isInConference && it.getStreamAvailability(StreamType.Video) } != null -> {
Log.i("[Conference Call] Conference has video enabled & our device has video enabled, enabling full screen mode")
controlsViewModel.fullScreenMode.value = true
}
else -> {
Log.w("[Conference Call] Conference has video enabled but our device video is disabled")
Log.i("[Conference Call] Conference has video enabled but our device video is disabled")
}
}
}

View file

@ -78,6 +78,12 @@ class ConferenceViewModel : ViewModel() {
MutableLiveData<Event<Boolean>>()
}
private var waitForNextStreamsRunningToUpdateLayout = false
val reloadConferenceFragmentEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
private val conferenceListener = object : ConferenceListenerStub() {
override fun onParticipantAdded(conference: Conference, participant: Participant) {
Log.i("[Conference] Participant added: ${participant.address.asStringUriOnly()}")
@ -218,6 +224,18 @@ class ConferenceViewModel : ViewModel() {
}
}
}
override fun onCallStateChanged(
core: Core,
call: Call,
state: Call.State?,
message: String
) {
if (state == Call.State.StreamsRunning && waitForNextStreamsRunningToUpdateLayout) {
waitForNextStreamsRunningToUpdateLayout = false
reloadConferenceFragmentEvent.value = Event(true)
}
}
}
init {
@ -340,7 +358,14 @@ class ConferenceViewModel : ViewModel() {
}
}
fun changeLayout(layout: ConferenceDisplayMode) {
fun switchLayoutFromAudioOnlyToActiveSpeaker() {
if (conferenceDisplayMode.value == ConferenceDisplayMode.AUDIO_ONLY) {
changeLayout(ConferenceDisplayMode.ACTIVE_SPEAKER, true)
waitForNextStreamsRunningToUpdateLayout = true
}
}
fun changeLayout(layout: ConferenceDisplayMode, forceSendingVideo: Boolean) {
Log.i("[Conference] Trying to change conference layout to $layout")
val conference = conference.value
if (conference != null) {
@ -353,6 +378,7 @@ class ConferenceViewModel : ViewModel() {
}
params.isVideoEnabled = layout != ConferenceDisplayMode.AUDIO_ONLY
if (forceSendingVideo) params.videoDirection = MediaDirection.SendRecv
params.conferenceVideoLayout = when (layout) {
ConferenceDisplayMode.GRID -> ConferenceLayout.Grid
else -> ConferenceLayout.ActiveSpeaker

View file

@ -96,8 +96,8 @@
android:layout_marginStart="5dp"
android:background="@drawable/button_background_reverse"
android:contentDescription="@{controlsViewModel.isVideoEnabled &amp;&amp; controlsViewModel.isSendingVideo ? @string/content_description_disable_video : @string/content_description_enable_video}"
android:enabled="@{controlsViewModel.isVideoAvailable &amp;&amp; !controlsViewModel.isVideoUpdateInProgress &amp;&amp; conferenceViewModel.conferenceDisplayMode != ConferenceDisplayMode.AUDIO_ONLY}"
android:onClick="@{() -> controlsViewModel.toggleVideo()}"
android:enabled="@{controlsViewModel.isVideoAvailable &amp;&amp; !controlsViewModel.isVideoUpdateInProgress}"
android:onClick="@{() -> conferenceViewModel.conferenceDisplayMode != ConferenceDisplayMode.AUDIO_ONLY ? controlsViewModel.toggleVideo() : conferenceViewModel.switchLayoutFromAudioOnlyToActiveSpeaker()}"
android:padding="5dp"
android:selected="@{controlsViewModel.isVideoEnabled &amp;&amp; controlsViewModel.isSendingVideo}"
android:src="@drawable/icon_toggle_camera"

View file

@ -79,7 +79,7 @@
android:drawableEnd="@drawable/icon_conference_layout_grid"
android:drawableTint="?attr/voipDrawableColor"
android:enabled="@{conferenceViewModel.conferenceParticipantDevices.size() > conferenceViewModel.maxParticipantsForMosaicLayout ? false : true}"
android:onClickListener="@{() -> conferenceViewModel.changeLayout(ConferenceDisplayMode.GRID)}"
android:onClickListener="@{() -> conferenceViewModel.changeLayout(ConferenceDisplayMode.GRID, false)}"
android:text="@string/conference_display_mode_mosaic" />
<View
@ -97,7 +97,7 @@
android:checked="@{conferenceViewModel.conferenceDisplayMode == ConferenceDisplayMode.ACTIVE_SPEAKER}"
android:drawableEnd="@drawable/icon_conference_layout_active_speaker"
android:drawableTint="?attr/voipDrawableColor"
android:onClickListener="@{() -> conferenceViewModel.changeLayout(ConferenceDisplayMode.ACTIVE_SPEAKER)}"
android:onClickListener="@{() -> conferenceViewModel.changeLayout(ConferenceDisplayMode.ACTIVE_SPEAKER, false)}"
android:text="@string/conference_display_mode_active_speaker" />
<View
@ -115,7 +115,7 @@
android:checked="@{conferenceViewModel.conferenceDisplayMode == ConferenceDisplayMode.AUDIO_ONLY}"
android:drawableEnd="@drawable/icon_conference_layout_audio_only"
android:drawableTint="?attr/voipDrawableColor"
android:onClickListener="@{() -> conferenceViewModel.changeLayout(ConferenceDisplayMode.AUDIO_ONLY)}"
android:onClickListener="@{() -> conferenceViewModel.changeLayout(ConferenceDisplayMode.AUDIO_ONLY, false)}"
android:text="@string/conference_display_mode_audio_only" />
<View