Conference: disable video when using audio only layout + sort participants device depending on layout
This commit is contained in:
parent
26f06436f4
commit
ab0cc34b24
4 changed files with 61 additions and 51 deletions
|
@ -106,7 +106,7 @@ class ConferenceWaitingRoomViewModel : ViewModel() {
|
||||||
isAudioOnlyLayoutSelected.value = false
|
isAudioOnlyLayoutSelected.value = false
|
||||||
updateLayout()
|
updateLayout()
|
||||||
|
|
||||||
isVideoAvailable.value = core.isVideoCaptureEnabled || core.isVideoPreviewEnabled
|
isVideoAvailable.value = isAudioOnlyLayoutSelected.value == false && (core.isVideoCaptureEnabled || core.isVideoPreviewEnabled)
|
||||||
callParams.isVideoEnabled = isVideoAvailable.value == true
|
callParams.isVideoEnabled = isVideoAvailable.value == true
|
||||||
callParams.videoDirection = if (core.videoActivationPolicy.automaticallyInitiate) MediaDirection.SendRecv else MediaDirection.RecvOnly
|
callParams.videoDirection = if (core.videoActivationPolicy.automaticallyInitiate) MediaDirection.SendRecv else MediaDirection.RecvOnly
|
||||||
Log.i("[Conference Waiting Room] Video will be ${if (callParams.isVideoEnabled) "enabled" else "disabled"}")
|
Log.i("[Conference Waiting Room] Video will be ${if (callParams.isVideoEnabled) "enabled" else "disabled"}")
|
||||||
|
@ -233,6 +233,7 @@ class ConferenceWaitingRoomViewModel : ViewModel() {
|
||||||
askPermissionEvent.value = Event(Manifest.permission.CAMERA)
|
askPermissionEvent.value = Event(Manifest.permission.CAMERA)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
callParams.isVideoEnabled = isVideoAvailable.value == true
|
||||||
callParams.videoDirection = if (callParams.videoDirection == MediaDirection.SendRecv) MediaDirection.RecvOnly else MediaDirection.SendRecv
|
callParams.videoDirection = if (callParams.videoDirection == MediaDirection.SendRecv) MediaDirection.RecvOnly else MediaDirection.SendRecv
|
||||||
Log.i("[Conference Waiting Room] Video will be ${if (callParams.isVideoEnabled) "enabled" else "disabled"}")
|
Log.i("[Conference Waiting Room] Video will be ${if (callParams.isVideoEnabled) "enabled" else "disabled"}")
|
||||||
updateVideoState()
|
updateVideoState()
|
||||||
|
@ -240,6 +241,7 @@ class ConferenceWaitingRoomViewModel : ViewModel() {
|
||||||
|
|
||||||
fun enableVideo() {
|
fun enableVideo() {
|
||||||
Log.i("[Conference Waiting Room] Video will be enabled")
|
Log.i("[Conference Waiting Room] Video will be enabled")
|
||||||
|
callParams.isVideoEnabled = isVideoAvailable.value == true
|
||||||
callParams.videoDirection = MediaDirection.SendRecv
|
callParams.videoDirection = MediaDirection.SendRecv
|
||||||
updateVideoState()
|
updateVideoState()
|
||||||
}
|
}
|
||||||
|
@ -288,13 +290,19 @@ class ConferenceWaitingRoomViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateLayout() {
|
private fun updateLayout() {
|
||||||
val layout = coreContext.core.defaultConferenceLayout
|
val core = coreContext.core
|
||||||
|
val layout = core.defaultConferenceLayout
|
||||||
isActiveSpeakerLayoutSelected.value = layout == ConferenceLayout.ActiveSpeaker
|
isActiveSpeakerLayoutSelected.value = layout == ConferenceLayout.ActiveSpeaker
|
||||||
isAudioOnlyLayoutSelected.value = layout == ConferenceLayout.Legacy // TODO: FIXME: Replace Legacy by AudioOnly
|
isAudioOnlyLayoutSelected.value = layout == ConferenceLayout.Legacy // TODO: FIXME: Replace Legacy by AudioOnly
|
||||||
|
|
||||||
|
isVideoAvailable.value = isAudioOnlyLayoutSelected.value == false && (core.isVideoCaptureEnabled || core.isVideoPreviewEnabled)
|
||||||
|
callParams.isVideoEnabled = isVideoAvailable.value == true && isAudioOnlyLayoutSelected.value == false
|
||||||
|
if (isAudioOnlyLayoutSelected.value == true) callParams.videoDirection = MediaDirection.RecvOnly
|
||||||
|
updateVideoState()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateVideoState() {
|
private fun updateVideoState() {
|
||||||
isVideoEnabled.value = callParams.videoDirection == MediaDirection.SendRecv
|
isVideoEnabled.value = callParams.isVideoEnabled && callParams.videoDirection == MediaDirection.SendRecv
|
||||||
isSwitchCameraAvailable.value = callParams.isVideoEnabled && coreContext.showSwitchCameraButton()
|
isSwitchCameraAvailable.value = callParams.isVideoEnabled && coreContext.showSwitchCameraButton()
|
||||||
coreContext.core.isVideoPreviewEnabled = callParams.isVideoEnabled
|
coreContext.core.isVideoPreviewEnabled = callParams.isVideoEnabled
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,6 @@ import androidx.navigation.navGraphViewModels
|
||||||
import org.linphone.R
|
import org.linphone.R
|
||||||
import org.linphone.activities.GenericFragment
|
import org.linphone.activities.GenericFragment
|
||||||
import org.linphone.activities.voip.viewmodels.ConferenceViewModel
|
import org.linphone.activities.voip.viewmodels.ConferenceViewModel
|
||||||
import org.linphone.core.ConferenceLayout
|
|
||||||
import org.linphone.core.tools.Log
|
|
||||||
import org.linphone.databinding.VoipConferenceLayoutFragmentBinding
|
import org.linphone.databinding.VoipConferenceLayoutFragmentBinding
|
||||||
|
|
||||||
class ConferenceLayoutFragment : GenericFragment<VoipConferenceLayoutFragmentBinding>() {
|
class ConferenceLayoutFragment : GenericFragment<VoipConferenceLayoutFragmentBinding>() {
|
||||||
|
@ -46,48 +44,6 @@ class ConferenceLayoutFragment : GenericFragment<VoipConferenceLayoutFragmentBin
|
||||||
goBack()
|
goBack()
|
||||||
}
|
}
|
||||||
|
|
||||||
conferenceViewModel.conferenceMosaicDisplayMode.observe(
|
|
||||||
viewLifecycleOwner
|
|
||||||
) {
|
|
||||||
if (it) {
|
|
||||||
Log.i("[Conference] Trying to change conference layout to Grid")
|
|
||||||
val conference = conferenceViewModel.conference.value
|
|
||||||
if (conference != null) {
|
|
||||||
conference.layout = ConferenceLayout.Grid
|
|
||||||
} else {
|
|
||||||
Log.e("[Conference] Conference is null in ConferenceViewModel")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
conferenceViewModel.conferenceActiveSpeakerDisplayMode.observe(
|
|
||||||
viewLifecycleOwner
|
|
||||||
) {
|
|
||||||
if (it) {
|
|
||||||
Log.i("[Conference] Trying to change conference layout to ActiveSpeaker")
|
|
||||||
val conference = conferenceViewModel.conference.value
|
|
||||||
if (conference != null) {
|
|
||||||
conference.layout = ConferenceLayout.ActiveSpeaker
|
|
||||||
} else {
|
|
||||||
Log.e("[Conference] Conference is null in ConferenceViewModel")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
conferenceViewModel.conferenceAudioOnlyDisplayMode.observe(
|
|
||||||
viewLifecycleOwner
|
|
||||||
) {
|
|
||||||
if (it) {
|
|
||||||
Log.i("[Conference] Trying to change conference layout to AudioOnly")
|
|
||||||
val conference = conferenceViewModel.conference.value
|
|
||||||
if (conference != null) {
|
|
||||||
conference.layout = ConferenceLayout.Legacy // TODO: FIXME: Use AudioOnly
|
|
||||||
} else {
|
|
||||||
Log.e("[Conference] Conference is null in ConferenceViewModel")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
conferenceViewModel.conferenceParticipantDevices.observe(
|
conferenceViewModel.conferenceParticipantDevices.observe(
|
||||||
viewLifecycleOwner
|
viewLifecycleOwner
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -290,11 +290,26 @@ class ConferenceViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun changeLayout(layout: ConferenceLayout) {
|
||||||
|
Log.i("[Conference] Trying to change conference layout to $layout")
|
||||||
|
val conference = conference.value
|
||||||
|
if (conference != null) {
|
||||||
|
conference.layout = layout
|
||||||
|
updateConferenceLayout(conference)
|
||||||
|
} else {
|
||||||
|
Log.e("[Conference] Conference is null in ConferenceViewModel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateConferenceLayout(conference: Conference) {
|
private fun updateConferenceLayout(conference: Conference) {
|
||||||
val layout = conference.layout
|
val layout = conference.layout
|
||||||
conferenceMosaicDisplayMode.value = layout == ConferenceLayout.Grid
|
conferenceMosaicDisplayMode.value = layout == ConferenceLayout.Grid
|
||||||
conferenceActiveSpeakerDisplayMode.value = layout == ConferenceLayout.ActiveSpeaker
|
conferenceActiveSpeakerDisplayMode.value = layout == ConferenceLayout.ActiveSpeaker
|
||||||
conferenceAudioOnlyDisplayMode.value = layout == ConferenceLayout.Legacy // TODO: FIXME: Use AudioOnly layout
|
conferenceAudioOnlyDisplayMode.value = layout == ConferenceLayout.Legacy // TODO: FIXME: Use AudioOnly layout
|
||||||
|
|
||||||
|
val list = sortDevicesDataList(conferenceParticipantDevices.value.orEmpty())
|
||||||
|
conferenceParticipantDevices.value = list
|
||||||
|
|
||||||
Log.i("[Conference] Conference current layout is: $layout")
|
Log.i("[Conference] Conference current layout is: $layout")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,11 +387,13 @@ class ConferenceViewModel : ViewModel() {
|
||||||
val deviceData = ConferenceParticipantDeviceData(device, false)
|
val deviceData = ConferenceParticipantDeviceData(device, false)
|
||||||
devices.add(deviceData)
|
devices.add(deviceData)
|
||||||
|
|
||||||
|
val sortedDevices = sortDevicesDataList(devices)
|
||||||
|
|
||||||
if (speakingParticipant.value == null) {
|
if (speakingParticipant.value == null) {
|
||||||
speakingParticipant.value = deviceData
|
speakingParticipant.value = deviceData
|
||||||
}
|
}
|
||||||
|
|
||||||
conferenceParticipantDevices.value = devices
|
conferenceParticipantDevices.value = sortedDevices
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeParticipantDevice(device: ParticipantDevice) {
|
private fun removeParticipantDevice(device: ParticipantDevice) {
|
||||||
|
@ -395,4 +412,28 @@ class ConferenceViewModel : ViewModel() {
|
||||||
|
|
||||||
conferenceParticipantDevices.value = devices
|
conferenceParticipantDevices.value = devices
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun sortDevicesDataList(devices: List<ConferenceParticipantDeviceData>): ArrayList<ConferenceParticipantDeviceData> {
|
||||||
|
val sortedList = arrayListOf<ConferenceParticipantDeviceData>()
|
||||||
|
sortedList.addAll(devices)
|
||||||
|
|
||||||
|
val meDeviceData = sortedList.find {
|
||||||
|
it.isMe
|
||||||
|
}
|
||||||
|
if (meDeviceData != null) {
|
||||||
|
val index = sortedList.indexOf(meDeviceData)
|
||||||
|
val expectedIndex = if (conferenceActiveSpeakerDisplayMode.value == true) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
sortedList.size - 1
|
||||||
|
}
|
||||||
|
if (index != expectedIndex) {
|
||||||
|
Log.i("[Conference] Me device data is at index $index, moving it to index $expectedIndex")
|
||||||
|
sortedList.removeAt(index)
|
||||||
|
sortedList.add(expectedIndex, meDeviceData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sortedList
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
<data>
|
<data>
|
||||||
<import type="android.view.View" />
|
<import type="android.view.View" />
|
||||||
|
<import type="org.linphone.core.ConferenceLayout" />
|
||||||
<variable
|
<variable
|
||||||
name="cancelClickListener"
|
name="cancelClickListener"
|
||||||
type="android.view.View.OnClickListener"/>
|
type="android.view.View.OnClickListener"/>
|
||||||
|
@ -59,6 +60,7 @@
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<RadioButton
|
<RadioButton
|
||||||
|
android:onClickListener="@{() -> conferenceViewModel.changeLayout(ConferenceLayout.Grid)}"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="20dp"
|
android:layout_marginStart="20dp"
|
||||||
|
@ -67,7 +69,7 @@
|
||||||
android:layout_marginBottom="10dp"
|
android:layout_marginBottom="10dp"
|
||||||
android:text="@string/conference_display_mode_mosaic"
|
android:text="@string/conference_display_mode_mosaic"
|
||||||
android:enabled="@{conferenceViewModel.conferenceParticipantDevices.size() > conferenceViewModel.maxParticipantsForMosaicLayout ? false : true}"
|
android:enabled="@{conferenceViewModel.conferenceParticipantDevices.size() > conferenceViewModel.maxParticipantsForMosaicLayout ? false : true}"
|
||||||
android:checked="@={conferenceViewModel.conferenceMosaicDisplayMode}"
|
android:checked="@{conferenceViewModel.conferenceMosaicDisplayMode}"
|
||||||
android:drawableEnd="@drawable/icon_conference_layout_grid"
|
android:drawableEnd="@drawable/icon_conference_layout_grid"
|
||||||
android:drawableTint="?attr/voipDrawableColor"/>
|
android:drawableTint="?attr/voipDrawableColor"/>
|
||||||
|
|
||||||
|
@ -77,6 +79,7 @@
|
||||||
android:background="?attr/dividerColor" />
|
android:background="?attr/dividerColor" />
|
||||||
|
|
||||||
<RadioButton
|
<RadioButton
|
||||||
|
android:onClickListener="@{() -> conferenceViewModel.changeLayout(ConferenceLayout.ActiveSpeaker)}"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="20dp"
|
android:layout_marginStart="20dp"
|
||||||
|
@ -85,7 +88,7 @@
|
||||||
android:layout_marginBottom="10dp"
|
android:layout_marginBottom="10dp"
|
||||||
android:text="@string/conference_display_mode_active_speaker"
|
android:text="@string/conference_display_mode_active_speaker"
|
||||||
android:enabled="@{conferenceViewModel.conferenceParticipantDevices.size() > conferenceViewModel.maxParticipantsForMosaicLayout ? false : true}"
|
android:enabled="@{conferenceViewModel.conferenceParticipantDevices.size() > conferenceViewModel.maxParticipantsForMosaicLayout ? false : true}"
|
||||||
android:checked="@={conferenceViewModel.conferenceActiveSpeakerDisplayMode}"
|
android:checked="@{conferenceViewModel.conferenceActiveSpeakerDisplayMode}"
|
||||||
android:drawableEnd="@drawable/icon_conference_layout_active_speaker"
|
android:drawableEnd="@drawable/icon_conference_layout_active_speaker"
|
||||||
android:drawableTint="?attr/voipDrawableColor"/>
|
android:drawableTint="?attr/voipDrawableColor"/>
|
||||||
|
|
||||||
|
@ -94,7 +97,9 @@
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
android:background="?attr/dividerColor" />
|
android:background="?attr/dividerColor" />
|
||||||
|
|
||||||
|
<!-- TODO: FIXME: Use AudioOnly layout -->
|
||||||
<RadioButton
|
<RadioButton
|
||||||
|
android:onClickListener="@{() -> conferenceViewModel.changeLayout(ConferenceLayout.Legacy)}"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="20dp"
|
android:layout_marginStart="20dp"
|
||||||
|
@ -102,7 +107,7 @@
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:layout_marginBottom="10dp"
|
android:layout_marginBottom="10dp"
|
||||||
android:text="@string/conference_display_mode_audio_only"
|
android:text="@string/conference_display_mode_audio_only"
|
||||||
android:checked="@={conferenceViewModel.conferenceAudioOnlyDisplayMode}"
|
android:checked="@{conferenceViewModel.conferenceAudioOnlyDisplayMode}"
|
||||||
android:drawableEnd="@drawable/icon_conference_layout_audio_only"
|
android:drawableEnd="@drawable/icon_conference_layout_audio_only"
|
||||||
android:drawableTint="?attr/voipDrawableColor"/>
|
android:drawableTint="?attr/voipDrawableColor"/>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue