Rework how audio only layout will be handled

This commit is contained in:
Sylvain Berfini 2022-03-31 15:47:15 +02:00
parent 7b933ad76b
commit 291ae367a6
19 changed files with 448 additions and 263 deletions

View file

@ -23,6 +23,7 @@ import android.Manifest
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.activities.voip.ConferenceDisplayMode
import org.linphone.core.*
import org.linphone.core.tools.Log
import org.linphone.utils.AudioRouteUtils
@ -44,9 +45,7 @@ class ConferenceWaitingRoomViewModel : ViewModel() {
val layoutMenuSelected = MutableLiveData<Boolean>()
val isActiveSpeakerLayoutSelected = MutableLiveData<Boolean>()
val isAudioOnlyLayoutSelected = MutableLiveData<Boolean>()
val selectedLayout = MutableLiveData<ConferenceDisplayMode>()
val isVideoAvailable = MutableLiveData<Boolean>()
@ -101,20 +100,17 @@ class ConferenceWaitingRoomViewModel : ViewModel() {
Log.i("[Conference Waiting Room] Microphone will be ${if (callParams.isMicEnabled) "enabled" else "muted"}")
updateMicState()
layoutMenuSelected.value = false
isActiveSpeakerLayoutSelected.value = false
isAudioOnlyLayoutSelected.value = false
updateLayout()
isVideoAvailable.value = isAudioOnlyLayoutSelected.value == false && (core.isVideoCaptureEnabled || core.isVideoPreviewEnabled)
callParams.isVideoEnabled = isVideoAvailable.value == true
callParams.isVideoEnabled = isVideoAvailableInCore()
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"}")
updateVideoState()
layoutMenuSelected.value = false
updateLayout()
if (AudioRouteUtils.isBluetoothAudioRouteAvailable()) {
setBluetoothAudioRoute()
} else if (isVideoAvailable.value == true && isVideoEnabled.value == true) {
} else if (isVideoAvailableInCore() && isVideoEnabled.value == true) {
setSpeakerAudioRoute()
} else {
setEarpieceAudioRoute()
@ -209,22 +205,31 @@ class ConferenceWaitingRoomViewModel : ViewModel() {
fun setMosaicLayout() {
Log.i("[Conference Waiting Room] Set default layout to Mosaic")
coreContext.core.defaultConferenceLayout = ConferenceLayout.Grid
callParams.conferenceVideoLayout = ConferenceLayout.Grid
callParams.isVideoEnabled = isVideoAvailableInCore()
updateLayout()
updateVideoState()
layoutMenuSelected.value = false
}
fun setActiveSpeakerLayout() {
Log.i("[Conference Waiting Room] Set default layout to ActiveSpeaker")
coreContext.core.defaultConferenceLayout = ConferenceLayout.ActiveSpeaker
callParams.conferenceVideoLayout = ConferenceLayout.ActiveSpeaker
callParams.isVideoEnabled = isVideoAvailableInCore()
updateLayout()
updateVideoState()
layoutMenuSelected.value = false
}
fun setAudioOnlyLayout() {
Log.i("[Conference Waiting Room] Set default layout to AudioOnly")
coreContext.core.defaultConferenceLayout = ConferenceLayout.Legacy // TODO: FIXME: Replace Legacy by AudioOnly
Log.i("[Conference Waiting Room] Set default layout to AudioOnly, disabling video in call")
callParams.isVideoEnabled = false
updateLayout()
updateVideoState()
layoutMenuSelected.value = false
}
@ -233,7 +238,7 @@ class ConferenceWaitingRoomViewModel : ViewModel() {
askPermissionEvent.value = Event(Manifest.permission.CAMERA)
return
}
callParams.isVideoEnabled = isVideoAvailable.value == true
callParams.isVideoEnabled = isVideoAvailableInCore()
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"}")
updateVideoState()
@ -241,7 +246,7 @@ class ConferenceWaitingRoomViewModel : ViewModel() {
fun enableVideo() {
Log.i("[Conference Waiting Room] Video will be enabled")
callParams.isVideoEnabled = isVideoAvailable.value == true
callParams.isVideoEnabled = isVideoAvailableInCore()
callParams.videoDirection = MediaDirection.SendRecv
updateVideoState()
}
@ -290,20 +295,25 @@ class ConferenceWaitingRoomViewModel : ViewModel() {
}
private fun updateLayout() {
val core = coreContext.core
val layout = core.defaultConferenceLayout
isActiveSpeakerLayoutSelected.value = layout == ConferenceLayout.ActiveSpeaker
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()
if (!callParams.isVideoEnabled) {
selectedLayout.value = ConferenceDisplayMode.AUDIO_ONLY
} else {
selectedLayout.value = when (callParams.conferenceVideoLayout) {
ConferenceLayout.Grid -> ConferenceDisplayMode.GRID
else -> ConferenceDisplayMode.ACTIVE_SPEAKER
}
}
}
private fun updateVideoState() {
isVideoAvailable.value = callParams.isVideoEnabled
isVideoEnabled.value = callParams.isVideoEnabled && callParams.videoDirection == MediaDirection.SendRecv
isSwitchCameraAvailable.value = callParams.isVideoEnabled && coreContext.showSwitchCameraButton()
coreContext.core.isVideoPreviewEnabled = callParams.isVideoEnabled
}
private fun isVideoAvailableInCore(): Boolean {
val core = coreContext.core
return core.isVideoCaptureEnabled || core.isVideoPreviewEnabled
}
}

View file

@ -48,9 +48,6 @@ class ConferencesSettingsViewModel : GenericSettingsViewModel() {
labels.add(prefs.getString(R.string.conference_display_mode_mosaic))
layoutValues.add(ConferenceLayout.Grid.toInt())
labels.add(prefs.getString(R.string.conference_display_mode_audio_only))
layoutValues.add(ConferenceLayout.Legacy.toInt()) // TODO: FIXME: Use AudioOnly
layoutLabels.value = labels
layoutIndex.value = layoutValues.indexOf(core.defaultConferenceLayout.toInt())
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2010-2022 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.voip
enum class ConferenceDisplayMode {
GRID,
ACTIVE_SPEAKER,
AUDIO_ONLY
}

View file

@ -113,7 +113,6 @@ open class CallData(val call: Call) : GenericContactData(call.remoteAddress) {
displayableAddress.value = clone.asStringUriOnly()
update()
// initChatRoom()
val conferenceInfo = coreContext.core.findConferenceInformationFromUri(call.remoteAddress)
if (conferenceInfo != null) {
@ -167,6 +166,10 @@ open class CallData(val call: Call) : GenericContactData(call.remoteAddress) {
contextMenuClickListener?.onShowContextMenu(anchor, this)
}
fun isActiveAndNotInConference(): Boolean {
return isPaused.value == false && isRemotelyPaused.value == false && isInRemoteConference.value == false
}
private fun isCallPaused(): Boolean {
return when (call.state) {
Call.State.Paused, Call.State.Pausing -> true

View file

@ -39,6 +39,7 @@ import org.linphone.activities.main.MainActivity
import org.linphone.activities.navigateToCallsList
import org.linphone.activities.navigateToConferenceLayout
import org.linphone.activities.navigateToConferenceParticipants
import org.linphone.activities.voip.ConferenceDisplayMode
import org.linphone.activities.voip.viewmodels.CallsViewModel
import org.linphone.activities.voip.viewmodels.ConferenceViewModel
import org.linphone.activities.voip.viewmodels.ControlsViewModel
@ -79,20 +80,11 @@ class ConferenceCallFragment : GenericFragment<VoipConferenceCallFragmentBinding
binding.statsViewModel = statsViewModel
conferenceViewModel.conferenceMosaicDisplayMode.observe(
conferenceViewModel.conferenceDisplayMode.observe(
viewLifecycleOwner
) {
if (it) {
startTimer(R.id.active_conference_timer)
}
}
conferenceViewModel.conferenceActiveSpeakerDisplayMode.observe(
viewLifecycleOwner
) {
if (it) {
startTimer(R.id.active_conference_timer)
) { displayMode ->
startTimer(R.id.active_conference_timer)
if (displayMode == ConferenceDisplayMode.ACTIVE_SPEAKER) {
if (conferenceViewModel.conferenceExists.value == true) {
Log.i("[Conference Call] Local participant is in conference and current layout is active speaker, updating Core's native window id")
val layout =
@ -107,14 +99,6 @@ class ConferenceCallFragment : GenericFragment<VoipConferenceCallFragmentBinding
}
}
conferenceViewModel.conferenceAudioOnlyDisplayMode.observe(
viewLifecycleOwner
) {
if (it) {
startTimer(R.id.active_conference_timer)
}
}
conferenceViewModel.conferenceParticipantDevices.observe(
viewLifecycleOwner
) {
@ -138,6 +122,20 @@ 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)
}
}
}
controlsViewModel.goToConferenceParticipantsListEvent.observe(
viewLifecycleOwner
) {

View file

@ -26,6 +26,7 @@ import androidx.navigation.navGraphViewModels
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.activities.GenericFragment
import org.linphone.activities.voip.ConferenceDisplayMode
import org.linphone.activities.voip.viewmodels.ConferenceViewModel
import org.linphone.databinding.VoipConferenceLayoutFragmentBinding
@ -53,6 +54,16 @@ class ConferenceLayoutFragment : GenericFragment<VoipConferenceLayoutFragmentBin
}
}
conferenceViewModel.conferenceDisplayMode.observe(
viewLifecycleOwner
) {
binding.localPreviewVideoSurface.visibility = if (it == ConferenceDisplayMode.AUDIO_ONLY) {
View.GONE
} else {
View.VISIBLE
}
}
binding.setDismissDialogClickListener {
val dialog = binding.root.findViewById<LinearLayout>(R.id.too_many_participants_dialog)
dialog?.visibility = View.GONE

View file

@ -73,6 +73,7 @@ class CallsViewModel : ViewModel() {
}
override fun onLastCallEnded(core: Core) {
Log.i("[Calls] Last call ended")
currentCallData.value?.destroy()
noMoreCallEvent.value = Event(true)
}

View file

@ -24,6 +24,7 @@ import androidx.lifecycle.ViewModel
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.activities.voip.ConferenceDisplayMode
import org.linphone.activities.voip.data.ConferenceParticipantData
import org.linphone.activities.voip.data.ConferenceParticipantDeviceData
import org.linphone.core.*
@ -43,9 +44,7 @@ class ConferenceViewModel : ViewModel() {
val conferenceCreationPending = MutableLiveData<Boolean>()
val conferenceParticipants = MutableLiveData<List<ConferenceParticipantData>>()
val conferenceParticipantDevices = MutableLiveData<List<ConferenceParticipantDeviceData>>()
val conferenceMosaicDisplayMode = MutableLiveData<Boolean>()
val conferenceActiveSpeakerDisplayMode = MutableLiveData<Boolean>()
val conferenceAudioOnlyDisplayMode = MutableLiveData<Boolean>()
val conferenceDisplayMode = MutableLiveData<ConferenceDisplayMode>()
val isRecording = MutableLiveData<Boolean>()
val isRemotelyRecorded = MutableLiveData<Boolean>()
@ -64,10 +63,9 @@ class ConferenceViewModel : ViewModel() {
updateParticipantsList(conference)
val count = conferenceParticipants.value.orEmpty().size
if (count > maxParticipantsForMosaicLayout && conferenceMosaicDisplayMode.value == true) {
if (count > maxParticipantsForMosaicLayout && conferenceDisplayMode.value == ConferenceDisplayMode.GRID) {
Log.w("[Conference] More than $maxParticipantsForMosaicLayout participants ($count), forcing active speaker layout")
conferenceMosaicDisplayMode.value = false
conferenceActiveSpeakerDisplayMode.value = true
conferenceDisplayMode.value = ConferenceDisplayMode.ACTIVE_SPEAKER
}
}
@ -181,9 +179,6 @@ class ConferenceViewModel : ViewModel() {
conferenceParticipants.value = arrayListOf()
conferenceParticipantDevices.value = arrayListOf()
conferenceMosaicDisplayMode.value = false
conferenceActiveSpeakerDisplayMode.value = false
conferenceAudioOnlyDisplayMode.value = false
subject.value = AppUtils.getString(R.string.conference_default_title)
@ -290,27 +285,57 @@ class ConferenceViewModel : ViewModel() {
}
}
fun changeLayout(layout: ConferenceLayout) {
fun changeLayout(layout: ConferenceDisplayMode) {
Log.i("[Conference] Trying to change conference layout to $layout")
val conference = conference.value
if (conference != null) {
conference.layout = layout
updateConferenceLayout(conference)
val call = conference.call
if (call != null) {
val params = call.core.createCallParams(call)
if (params == null) {
Log.e("[Conference] Failed to create call params from conference call!")
return
}
params.isVideoEnabled = layout != ConferenceDisplayMode.AUDIO_ONLY
params.conferenceVideoLayout = when (layout) {
ConferenceDisplayMode.GRID -> ConferenceLayout.Grid
else -> ConferenceLayout.ActiveSpeaker
}
call.update(params)
conferenceDisplayMode.value = layout
val list = sortDevicesDataList(conferenceParticipantDevices.value.orEmpty())
conferenceParticipantDevices.value = list
} else {
Log.e("[Conference] Failed to get call from conference!")
}
} else {
Log.e("[Conference] Conference is null in ConferenceViewModel")
}
}
private fun updateConferenceLayout(conference: Conference) {
val layout = conference.layout
conferenceMosaicDisplayMode.value = layout == ConferenceLayout.Grid
conferenceActiveSpeakerDisplayMode.value = layout == ConferenceLayout.ActiveSpeaker
conferenceAudioOnlyDisplayMode.value = layout == ConferenceLayout.Legacy // TODO: FIXME: Use AudioOnly layout
val call = conference.call
if (call == null) {
Log.e("[Conference] Conference call is null!")
return
}
val params = call.params
conferenceDisplayMode.value = if (!params.isVideoEnabled) {
ConferenceDisplayMode.AUDIO_ONLY
} else {
when (params.conferenceVideoLayout) {
ConferenceLayout.Grid -> ConferenceDisplayMode.GRID
else -> ConferenceDisplayMode.ACTIVE_SPEAKER
}
}
val list = sortDevicesDataList(conferenceParticipantDevices.value.orEmpty())
conferenceParticipantDevices.value = list
Log.i("[Conference] Conference current layout is: $layout")
Log.i("[Conference] Conference current layout is: ${conferenceDisplayMode.value}")
}
private fun terminateConference(conference: Conference) {
@ -422,7 +447,7 @@ class ConferenceViewModel : ViewModel() {
}
if (meDeviceData != null) {
val index = sortedList.indexOf(meDeviceData)
val expectedIndex = if (conferenceActiveSpeakerDisplayMode.value == true) {
val expectedIndex = if (conferenceDisplayMode.value == ConferenceDisplayMode.ACTIVE_SPEAKER) {
0
} else {
sortedList.size - 1

View file

@ -353,9 +353,6 @@ class CoreContext(val context: Context, coreConfig: Config) {
core.isVibrationOnIncomingCallEnabled = true
core.config.setBool("app", "incoming_call_vibration", false)
}
if (core.defaultConferenceLayout == ConferenceLayout.Legacy) {
core.defaultConferenceLayout = ConferenceLayout.ActiveSpeaker
}
initUserCertificates()

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="6.7dp" />
<solid android:color="?attr/voipParticipantBackgroundColor"/>
</shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<stroke android:width="2dp" android:color="?attr/colorPrimary"/>
<corners android:radius="6.7dp"/>
</shape>

View file

@ -3,7 +3,11 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.view.View" />
<import type="org.linphone.activities.voip.ConferenceDisplayMode" />
<variable
name="viewModel"
type="org.linphone.activities.main.conference.viewmodels.ConferenceWaitingRoomViewModel" />
@ -21,20 +25,19 @@
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_margin="20dp"
android:maxLines="1"
android:ellipsize="end"
android:maxLines="1"
android:text="@{viewModel.subject, default=`Conference subject`}" />
<TextView
style="@style/conference_waiting_room_no_video_font"
android:visibility="@{viewModel.isVideoEnabled ? View.GONE : View.VISIBLE}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="@string/conference_waiting_room_video_disabled"/>
android:text="@string/conference_waiting_room_video_disabled"
android:visibility="@{viewModel.isVideoEnabled ? View.GONE : View.VISIBLE}" />
<androidx.constraintlayout.widget.ConstraintLayout
android:visibility="@{viewModel.isVideoEnabled ? View.VISIBLE : View.GONE, default=gone}"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/conference_controls"
@ -42,7 +45,8 @@
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:layout_marginBottom="20dp"
android:background="@drawable/shape_remote_video_background">
android:background="@drawable/shape_remote_video_background"
android:visibility="@{viewModel.isVideoEnabled ? View.VISIBLE : View.GONE, default=gone}">
<org.linphone.activities.voip.views.RoundCornersTextureView
android:id="@+id/local_preview_video_surface"
@ -73,131 +77,131 @@
android:id="@+id/conference_controls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_above="@id/buttons"
android:layout_marginBottom="20dp">
android:layout_marginBottom="20dp"
android:orientation="horizontal">
<TextView
android:onClick="@{() -> viewModel.cancel()}"
style="@style/big_orange_button_font"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingLeft="20dp"
android:paddingTop="8dp"
android:paddingRight="20dp"
android:paddingBottom="8dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="10dp"
android:gravity="center"
android:background="@drawable/shape_rect_gray_button"
android:text="@string/conference_waiting_room_cancel_call"
style="@style/big_orange_button_font"/>
<TextView
android:onClick="@{() -> viewModel.start()}"
android:enabled="@{!viewModel.joinInProgress}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/shape_rect_gray_button"
android:gravity="center"
android:onClick="@{() -> viewModel.cancel()}"
android:paddingLeft="20dp"
android:paddingTop="8dp"
android:paddingRight="20dp"
android:paddingBottom="8dp"
android:text="@string/conference_waiting_room_cancel_call" />
<TextView
style="@style/big_orange_button_bold_font"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="20dp"
android:gravity="center"
android:layout_weight="1"
android:background="@drawable/shape_rect_orange_button"
android:text="@string/conference_waiting_room_start_call"
style="@style/big_orange_button_bold_font"/>
android:enabled="@{!viewModel.joinInProgress}"
android:gravity="center"
android:onClick="@{() -> viewModel.start()}"
android:paddingLeft="20dp"
android:paddingTop="8dp"
android:paddingRight="20dp"
android:paddingBottom="8dp"
android:text="@string/conference_waiting_room_start_call" />
</LinearLayout>
<LinearLayout
android:visibility="@{viewModel.audioRoutesSelected ? View.VISIBLE : View.GONE, default=gone}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/buttons"
android:background="@drawable/shape_audio_routes_background"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:orientation="vertical">
android:background="@drawable/shape_audio_routes_background"
android:orientation="vertical"
android:visibility="@{viewModel.audioRoutesSelected ? View.VISIBLE : View.GONE, default=gone}">
<ImageView
android:selected="@{viewModel.isBluetoothHeadsetSelected}"
android:onClick="@{() -> viewModel.setBluetoothAudioRoute()}"
android:contentDescription="@string/content_description_use_bluetooth_headset"
android:layout_width="@dimen/voip_call_button_size"
android:layout_height="@dimen/voip_call_button_size"
android:background="@drawable/button_toggle_background_reverse"
android:layout_margin="5dp"
android:background="@drawable/button_toggle_background_reverse"
android:contentDescription="@string/content_description_use_bluetooth_headset"
android:onClick="@{() -> viewModel.setBluetoothAudioRoute()}"
android:selected="@{viewModel.isBluetoothHeadsetSelected}"
android:src="@drawable/icon_bluetooth" />
<ImageView
android:selected="@{!viewModel.isSpeakerSelected &amp;&amp; !viewModel.isBluetoothHeadsetSelected}"
android:onClick="@{() -> viewModel.setEarpieceAudioRoute()}"
android:contentDescription="@string/content_description_use_earpiece"
android:layout_width="@dimen/voip_call_button_size"
android:layout_height="@dimen/voip_call_button_size"
android:background="@drawable/button_toggle_background_reverse"
android:layout_margin="5dp"
android:background="@drawable/button_toggle_background_reverse"
android:contentDescription="@string/content_description_use_earpiece"
android:onClick="@{() -> viewModel.setEarpieceAudioRoute()}"
android:selected="@{!viewModel.isSpeakerSelected &amp;&amp; !viewModel.isBluetoothHeadsetSelected}"
android:src="@drawable/icon_earpiece" />
<ImageView
android:selected="@{viewModel.isSpeakerSelected}"
android:onClick="@{() -> viewModel.setSpeakerAudioRoute()}"
android:contentDescription="@string/content_description_use_speaker"
android:layout_width="@dimen/voip_call_button_size"
android:layout_height="@dimen/voip_call_button_size"
android:background="@drawable/button_toggle_background_reverse"
android:layout_margin="5dp"
android:background="@drawable/button_toggle_background_reverse"
android:contentDescription="@string/content_description_use_speaker"
android:onClick="@{() -> viewModel.setSpeakerAudioRoute()}"
android:selected="@{viewModel.isSpeakerSelected}"
android:src="@drawable/icon_speaker" />
</LinearLayout>
<LinearLayout
android:visibility="@{viewModel.layoutMenuSelected ? View.VISIBLE : View.GONE, default=gone}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/buttons"
android:background="@drawable/shape_audio_routes_background"
android:layout_alignParentEnd="true"
android:layout_marginBottom="10dp"
android:layout_marginEnd="15dp"
android:orientation="vertical">
android:layout_marginBottom="10dp"
android:background="@drawable/shape_audio_routes_background"
android:orientation="vertical"
android:visibility="@{viewModel.layoutMenuSelected ? View.VISIBLE : View.GONE, default=gone}">
<ImageView
android:selected="@{!viewModel.isActiveSpeakerLayoutSelected &amp;&amp; !viewModel.isAudioOnlyLayoutSelected}"
android:onClick="@{() -> viewModel.setMosaicLayout()}"
android:contentDescription="@string/conference_display_mode_mosaic"
android:layout_width="@dimen/voip_call_button_size"
android:layout_height="@dimen/voip_call_button_size"
android:background="@drawable/button_toggle_background_reverse"
android:layout_margin="5dp"
android:background="@drawable/button_toggle_background_reverse"
android:contentDescription="@string/conference_display_mode_mosaic"
android:onClick="@{() -> viewModel.setMosaicLayout()}"
android:padding="5dp"
android:selected="@{viewModel.selectedLayout == ConferenceDisplayMode.GRID}"
android:src="@drawable/icon_conference_layout_grid"
app:tint="@color/white_color" />
<ImageView
android:selected="@{viewModel.isActiveSpeakerLayoutSelected}"
android:onClick="@{() -> viewModel.setActiveSpeakerLayout()}"
android:contentDescription="@string/conference_display_mode_active_speaker"
android:layout_width="@dimen/voip_call_button_size"
android:layout_height="@dimen/voip_call_button_size"
android:background="@drawable/button_toggle_background_reverse"
android:layout_margin="5dp"
android:background="@drawable/button_toggle_background_reverse"
android:contentDescription="@string/conference_display_mode_active_speaker"
android:onClick="@{() -> viewModel.setActiveSpeakerLayout()}"
android:padding="5dp"
android:selected="@{viewModel.selectedLayout == ConferenceDisplayMode.ACTIVE_SPEAKER}"
android:src="@drawable/icon_conference_layout_active_speaker"
app:tint="@color/white_color"/>
app:tint="@color/white_color" />
<ImageView
android:selected="@{viewModel.isAudioOnlyLayoutSelected}"
android:onClick="@{() -> viewModel.setAudioOnlyLayout()}"
android:contentDescription="@string/conference_display_mode_audio_only"
android:layout_width="@dimen/voip_call_button_size"
android:layout_height="@dimen/voip_call_button_size"
android:background="@drawable/button_toggle_background_reverse"
android:layout_margin="5dp"
android:background="@drawable/button_toggle_background_reverse"
android:contentDescription="@string/conference_display_mode_audio_only"
android:onClick="@{() -> viewModel.setAudioOnlyLayout()}"
android:padding="5dp"
android:selected="@{viewModel.selectedLayout == ConferenceDisplayMode.AUDIO_ONLY}"
android:src="@drawable/icon_conference_layout_audio_only"
app:tint="@color/white_color" />
@ -211,14 +215,14 @@
android:layout_marginBottom="10dp">
<ImageView
android:contentDescription="@{viewModel.isMicrophoneMuted ? @string/content_description_disable_mic_mute : @string/content_description_enable_mic_mute}"
android:onClick="@{() -> viewModel.toggleMuteMicrophone()}"
android:selected="@{viewModel.isMicrophoneMuted}"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="5dp"
android:background="@drawable/button_background_reverse"
android:contentDescription="@{viewModel.isMicrophoneMuted ? @string/content_description_disable_mic_mute : @string/content_description_enable_mic_mute}"
android:onClick="@{() -> viewModel.toggleMuteMicrophone()}"
android:padding="5dp"
android:selected="@{viewModel.isMicrophoneMuted}"
android:src="@drawable/icon_toggle_mic"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="W,1:1"
@ -245,46 +249,46 @@
android:visibility="@{viewModel.audioRoutesEnabled ? View.GONE : View.VISIBLE}" />
<ImageView
android:onClick="@{() -> viewModel.toggleAudioRoutesMenu()}"
android:selected="@{viewModel.audioRoutesSelected}"
android:visibility="@{viewModel.audioRoutesEnabled ? View.VISIBLE : View.GONE, default=gone}"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@drawable/button_toggle_background_reverse"
android:src="@drawable/icon_audio_routes"
android:contentDescription="@string/content_description_toggle_audio_menu"
android:onClick="@{() -> viewModel.toggleAudioRoutesMenu()}"
android:padding="5dp"
android:contentDescription="@string/content_description_toggle_audio_menu"/>
android:selected="@{viewModel.audioRoutesSelected}"
android:src="@drawable/icon_audio_routes"
android:visibility="@{viewModel.audioRoutesEnabled ? View.VISIBLE : View.GONE, default=gone}" />
</RelativeLayout>
<ImageView
android:contentDescription="@{viewModel.isVideoEnabled ? @string/content_description_disable_video : @string/content_description_enable_video}"
android:enabled="@{viewModel.isVideoAvailable}"
android:onClick="@{() -> viewModel.toggleVideo()}"
android:selected="@{viewModel.isVideoEnabled}"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="5dp"
android:background="@drawable/button_background_reverse"
android:contentDescription="@{viewModel.isVideoEnabled ? @string/content_description_disable_video : @string/content_description_enable_video}"
android:enabled="@{viewModel.isVideoAvailable}"
android:onClick="@{() -> viewModel.toggleVideo()}"
android:padding="5dp"
android:selected="@{viewModel.isVideoEnabled}"
android:src="@drawable/icon_toggle_camera"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="W,1:1"
app:layout_constraintStart_toEndOf="@id/speaker" />
<ImageView
android:onClick="@{() -> viewModel.toggleLayoutMenu()}"
android:selected="@{viewModel.layoutMenuSelected}"
android:layout_height="match_parent"
android:layout_width="0dp"
android:background="@drawable/button_toggle_background_reverse"
android:src="@{viewModel.isActiveSpeakerLayoutSelected ? @drawable/icon_conference_layout_active_speaker : viewModel.isAudioOnlyLayoutSelected ? @drawable/icon_conference_layout_audio_only : @drawable/icon_conference_layout_grid, default=@drawable/icon_conference_layout_grid}"
android:padding="10dp"
android:layout_height="match_parent"
android:layout_marginEnd="20dp"
android:background="@drawable/button_toggle_background_reverse"
android:contentDescription="@string/content_description_toggle_layout_menu"
app:layout_constraintDimensionRatio="W,1:1"
android:onClick="@{() -> viewModel.toggleLayoutMenu()}"
android:padding="10dp"
android:selected="@{viewModel.layoutMenuSelected}"
android:src="@{viewModel.selectedLayout == ConferenceDisplayMode.ACTIVE_SPEAKER ? @drawable/icon_conference_layout_active_speaker : viewModel.selectedLayout == ConferenceDisplayMode.AUDIO_ONLY ? @drawable/icon_conference_layout_audio_only : @drawable/icon_conference_layout_grid, default=@drawable/icon_conference_layout_grid}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
app:layout_constraintDimensionRatio="W,1:1"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -3,13 +3,22 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.view.View" />
<import type="org.linphone.activities.voip.ConferenceDisplayMode" />
<variable
name="controlsViewModel"
type="org.linphone.activities.voip.viewmodels.ControlsViewModel" />
<variable
name="callsViewModel"
type="org.linphone.activities.voip.viewmodels.CallsViewModel" />
<variable
name="conferenceViewModel"
type="org.linphone.activities.voip.viewmodels.ConferenceViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
@ -86,7 +95,7 @@
android:layout_marginStart="5dp"
android:background="@drawable/button_background_reverse"
android:contentDescription="@{controlsViewModel.isVideoEnabled &amp;&amp; controlsViewModel.isVideoSendReceive ? @string/content_description_disable_video : @string/content_description_enable_video}"
android:enabled="@{controlsViewModel.isVideoAvailable &amp;&amp; !controlsViewModel.isVideoUpdateInProgress}"
android:enabled="@{controlsViewModel.isVideoAvailable &amp;&amp; !controlsViewModel.isVideoUpdateInProgress &amp;&amp; conferenceViewModel.conferenceDisplayMode != ConferenceDisplayMode.AUDIO_ONLY}}"
android:onClick="@{() -> controlsViewModel.toggleVideo()}"
android:padding="5dp"
android:selected="@{controlsViewModel.isVideoEnabled &amp;&amp; controlsViewModel.isVideoSendReceive}"
@ -128,7 +137,7 @@
android:translationY="@{controlsViewModel.bouncyCounterTranslateY}"
android:visibility="@{callsViewModel.chatAndCallsCount == 0 ? View.GONE : View.VISIBLE}"
app:layout_constraintEnd_toEndOf="@id/more"
app:layout_constraintTop_toTopOf="@id/more"/>
app:layout_constraintTop_toTopOf="@id/more" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -4,13 +4,19 @@
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="android.view.View" />
<import type="org.linphone.activities.voip.ConferenceDisplayMode" />
<variable
name="controlsViewModel"
type="org.linphone.activities.voip.viewmodels.ControlsViewModel" />
<variable
name="callsViewModel"
type="org.linphone.activities.voip.viewmodels.CallsViewModel" />
<variable
name="conferenceViewModel"
type="org.linphone.activities.voip.viewmodels.ConferenceViewModel" />
@ -37,7 +43,7 @@
app:layout_constraintEnd_toStartOf="@id/call_stats"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/chat"/>
app:layout_constraintTop_toTopOf="@id/chat" />
<TextView
android:id="@+id/call_stats"
@ -91,7 +97,7 @@
app:layout_constraintBottom_toBottomOf="@id/chat"
app:layout_constraintEnd_toEndOf="@id/chat"
app:layout_constraintStart_toStartOf="@id/chat"
app:layout_constraintTop_toTopOf="@id/chat"/>
app:layout_constraintTop_toTopOf="@id/chat" />
<TextView
android:id="@+id/transfer_call"
@ -107,7 +113,7 @@
app:layout_constraintEnd_toStartOf="@id/conference_participants"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/numpad"/>
app:layout_constraintTop_toBottomOf="@id/numpad" />
<TextView
android:id="@+id/conference_participants"
@ -123,7 +129,7 @@
app:layout_constraintEnd_toStartOf="@id/add_call"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toEndOf="@id/transfer_call"
app:layout_constraintTop_toTopOf="@id/calls"/>
app:layout_constraintTop_toTopOf="@id/calls" />
<TextView
android:id="@+id/add_call"
@ -138,14 +144,14 @@
app:layout_constraintBottom_toBottomOf="@id/calls"
app:layout_constraintEnd_toStartOf="@id/conference_layout"
app:layout_constraintStart_toEndOf="@id/conference_participants"
app:layout_constraintTop_toTopOf="@id/calls"/>
app:layout_constraintTop_toTopOf="@id/calls" />
<TextView
android:id="@+id/conference_layout"
style="@style/call_options_font"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:drawableTop="@{conferenceViewModel.conferenceActiveSpeakerDisplayMode ? @drawable/icon_conference_layout_active_speaker : conferenceViewModel.conferenceAudioOnlyDisplayMode ? @drawable/icon_conference_layout_audio_only : @drawable/icon_conference_layout_grid, default=@drawable/icon_conference_layout_grid}"
android:drawableTop="@{conferenceViewModel.conferenceDisplayMode == ConferenceDisplayMode.ACTIVE_SPEAKER ? @drawable/icon_conference_layout_active_speaker : conferenceViewModel.conferenceDisplayMode == ConferenceDisplayMode.AUDIO_ONLY ? @drawable/icon_conference_layout_audio_only : @drawable/icon_conference_layout_grid, default=@drawable/icon_conference_layout_grid}"
android:enabled="@{conferenceViewModel.isVideoConference}"
android:gravity="center"
android:onClick="@{() -> controlsViewModel.goToConferenceLayout()}"

View file

@ -26,52 +26,40 @@
android:layout_margin="@{controlsViewModel.fullScreenMode || controlsViewModel.pipMode ? @dimen/voip_remote_margin_full_screen : @dimen/voip_remote_margin, default=@dimen/voip_remote_margin}"
android:visibility="@{inflatedVisibility}">
<include
android:id="@+id/header"
layout="@layout/voip_conference_header"
android:visibility="@{controlsViewModel.fullScreenMode || controlsViewModel.pipMode ? View.GONE : View.VISIBLE}"
app:conferenceViewModel="@{conferenceViewModel}" />
<include
android:id="@+id/remote_recording"
layout="@layout/voip_remote_recording"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/header"
android:layout_margin="10dp"
android:visibility="@{conferenceViewModel.isRemotelyRecorded ? View.VISIBLE : View.GONE, default=gone}" />
<androidx.constraintlayout.widget.ConstraintLayout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/miniatures"
android:layout_below="@id/remote_recording"
android:layout_marginTop="10dp"
android:background="@drawable/shape_remote_background">
android:gravity="center"
android:orientation="vertical">
<include
layout="@layout/voip_contact_avatar"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="10dp"
app:data="@{conferenceViewModel.speakingParticipant}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="200dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
android:id="@+id/header"
layout="@layout/voip_conference_header"
android:visibility="@{controlsViewModel.fullScreenMode || controlsViewModel.pipMode ? View.GONE : View.VISIBLE}"
app:conferenceViewModel="@{conferenceViewModel}" />
<TextView
style="@style/call_remote_name_font"
android:layout_width="wrap_content"
<include
android:id="@+id/remote_recording"
layout="@layout/voip_remote_recording"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginBottom="10dp"
android:text="@{conferenceViewModel.speakingParticipant.contact.fullName ?? conferenceViewModel.speakingParticipant.displayName}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
android:visibility="@{conferenceViewModel.isRemotelyRecorded ? View.VISIBLE : View.GONE, default=gone}" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:entries="@{conferenceViewModel.conferenceParticipantDevices}"
app:layout="@{@layout/voip_conference_participant_remote_audio_only}" />
</androidx.core.widget.NestedScrollView>
</LinearLayout>
</RelativeLayout>

View file

@ -4,17 +4,25 @@
xmlns:bind="http://schemas.android.com/tools">
<data>
<import type="android.view.View" />
<import type="org.linphone.activities.voip.ConferenceDisplayMode" />
<import type="com.google.android.flexbox.FlexDirection" />
<variable
name="controlsViewModel"
type="org.linphone.activities.voip.viewmodels.ControlsViewModel" />
<variable
name="callsViewModel"
type="org.linphone.activities.voip.viewmodels.CallsViewModel" />
<variable
name="conferenceViewModel"
type="org.linphone.activities.voip.viewmodels.ConferenceViewModel" />
<variable
name="statsViewModel"
type="org.linphone.activities.voip.viewmodels.StatisticsListViewModel" />
@ -33,129 +41,130 @@
<ViewStub
android:id="@+id/stubbed_conference_active_speaker_layout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:inflatedId="@+id/conference_active_speaker_layout"
android:layout="@layout/voip_conference_active_speaker"
android:visibility="@{conferenceViewModel.conferenceActiveSpeakerDisplayMode ? View.VISIBLE : View.GONE, default=gone}"
app:inflatedVisibility="@{conferenceViewModel.conferenceActiveSpeakerDisplayMode ? View.VISIBLE : View.GONE}"
android:layout_width="match_parent"
android:visibility="@{conferenceViewModel.conferenceDisplayMode == ConferenceDisplayMode.ACTIVE_SPEAKER &amp;&amp; conferenceViewModel.conferenceExists &amp;&amp; !callsViewModel.currentCallData.isActiveAndNotInConference ? View.VISIBLE : View.GONE, default=gone}"
app:conferenceViewModel="@{conferenceViewModel}"
app:controlsViewModel="@{controlsViewModel}"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/primary_buttons" />
app:inflatedVisibility="@{conferenceViewModel.conferenceDisplayMode == ConferenceDisplayMode.ACTIVE_SPEAKER &amp;&amp; conferenceViewModel.conferenceExists &amp;&amp; !callsViewModel.currentCallData.isActiveAndNotInConference ? View.VISIBLE : View.GONE}"
app:layout_constraintBottom_toTopOf="@id/primary_buttons"
app:layout_constraintTop_toTopOf="parent" />
<ViewStub
android:id="@+id/stubbed_conference_grid_layout"
android:layout="@layout/voip_conference_grid"
android:visibility="@{conferenceViewModel.conferenceMosaicDisplayMode ? View.VISIBLE : View.GONE, default=gone}"
app:inflatedVisibility="@{conferenceViewModel.conferenceMosaicDisplayMode ? View.VISIBLE : View.GONE}"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout="@layout/voip_conference_grid"
android:visibility="@{conferenceViewModel.conferenceDisplayMode == ConferenceDisplayMode.GRID &amp;&amp; conferenceViewModel.conferenceExists &amp;&amp; !callsViewModel.currentCallData.isActiveAndNotInConference ? View.VISIBLE : View.GONE, default=gone}"
app:conferenceViewModel="@{conferenceViewModel}"
app:controlsViewModel="@{controlsViewModel}"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/primary_buttons" />
app:inflatedVisibility="@{conferenceViewModel.conferenceDisplayMode == ConferenceDisplayMode.GRID &amp;&amp; conferenceViewModel.conferenceExists &amp;&amp; !callsViewModel.currentCallData.isActiveAndNotInConference ? View.VISIBLE : View.GONE}"
app:layout_constraintBottom_toTopOf="@id/primary_buttons"
app:layout_constraintTop_toTopOf="parent" />
<ViewStub
android:id="@+id/stubbed_conference_audio_only_layout"
android:layout="@layout/voip_conference_audio_only"
android:visibility="@{conferenceViewModel.conferenceAudioOnlyDisplayMode ? View.VISIBLE : View.GONE, default=gone}"
app:inflatedVisibility="@{conferenceViewModel.conferenceAudioOnlyDisplayMode ? View.VISIBLE : View.GONE}"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout="@layout/voip_conference_audio_only"
android:visibility="@{conferenceViewModel.conferenceDisplayMode == ConferenceDisplayMode.AUDIO_ONLY &amp;&amp; conferenceViewModel.conferenceExists &amp;&amp; !callsViewModel.currentCallData.isActiveAndNotInConference ? View.VISIBLE : View.GONE, default=gone}"
app:conferenceViewModel="@{conferenceViewModel}"
app:controlsViewModel="@{controlsViewModel}"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/primary_buttons" />
app:inflatedVisibility="@{conferenceViewModel.conferenceDisplayMode == ConferenceDisplayMode.AUDIO_ONLY &amp;&amp; conferenceViewModel.conferenceExists &amp;&amp; !callsViewModel.currentCallData.isActiveAndNotInConference ? View.VISIBLE : View.GONE}"
app:layout_constraintBottom_toTopOf="@id/primary_buttons"
app:layout_constraintTop_toTopOf="parent" />
<ViewStub
android:id="@+id/stubbed_audio_routes"
android:layout="@layout/voip_buttons_audio_routes"
android:visibility="@{controlsViewModel.audioRoutesEnabled ? View.VISIBLE : View.GONE}"
app:inflatedVisibility="@{controlsViewModel.audioRoutesEnabled ? View.VISIBLE : View.GONE}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginStart="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginBottom="10dp"
android:layout="@layout/voip_buttons_audio_routes"
android:visibility="@{controlsViewModel.audioRoutesEnabled ? View.VISIBLE : View.GONE}"
app:controlsViewModel="@{controlsViewModel}"
app:inflatedVisibility="@{controlsViewModel.audioRoutesEnabled ? View.VISIBLE : View.GONE}"
app:layout_constraintBottom_toTopOf="@id/primary_buttons"
app:controlsViewModel="@{controlsViewModel}"/>
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<include
android:id="@+id/primary_buttons"
android:visibility="@{controlsViewModel.fullScreenMode || controlsViewModel.pipMode ? View.GONE : View.VISIBLE}"
layout="@layout/voip_buttons"
android:layout_width="0dp"
android:layout_height="@dimen/voip_buttons_fragment_size"
android:layout_marginBottom="10dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintWidth_max="@dimen/voip_buttons_max_width"
android:layout_marginBottom="10dp"
android:visibility="@{controlsViewModel.fullScreenMode || controlsViewModel.pipMode ? View.GONE : View.VISIBLE}"
app:callsViewModel="@{callsViewModel}"
app:conferenceViewModel="@{conferenceViewModel}"
app:controlsViewModel="@{controlsViewModel}"
app:callsViewModel="@{callsViewModel}" />
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintWidth_max="@dimen/voip_buttons_max_width" />
<ViewStub
android:id="@+id/stubbed_paused_conference"
android:layout="@layout/voip_conference_paused"
android:visibility="@{conferenceViewModel.isConferenceLocallyPaused ? View.VISIBLE : View.GONE, default=gone}"
app:inflatedVisibility="@{conferenceViewModel.isConferenceLocallyPaused ? View.VISIBLE : View.GONE}"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="5dp"
app:layout_constraintTop_toTopOf="parent"
android:layout="@layout/voip_conference_paused"
android:visibility="@{conferenceViewModel.isConferenceLocallyPaused ? View.VISIBLE : View.GONE, default=gone}"
app:conferenceViewModel="@{conferenceViewModel}"
app:inflatedVisibility="@{conferenceViewModel.isConferenceLocallyPaused ? View.VISIBLE : View.GONE}"
app:layout_constraintBottom_toTopOf="@id/primary_buttons"
app:conferenceViewModel="@{conferenceViewModel}" />
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:onClick="@{() -> controlsViewModel.hideExtraButtons(false)}"
android:visibility="@{controlsViewModel.showExtras ? View.VISIBLE : View.GONE, default=gone}"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/voip_translucent_popup_background" />
android:background="@color/voip_translucent_popup_background"
android:onClick="@{() -> controlsViewModel.hideExtraButtons(false)}"
android:visibility="@{controlsViewModel.showExtras ? View.VISIBLE : View.GONE, default=gone}" />
<include
layout="@layout/voip_buttons_extra"
android:translationY="@{controlsViewModel.extraButtonsMenuTranslateY, default=@dimen/voip_call_extra_buttons_translate_y}"
android:layout_width="0dp"
android:layout_height="@dimen/voip_call_extra_buttons_height"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintWidth_max="@dimen/voip_extra_menu_max_width"
app:controlsViewModel="@{controlsViewModel}"
android:translationY="@{controlsViewModel.extraButtonsMenuTranslateY, default=@dimen/voip_call_extra_buttons_translate_y}"
app:callsViewModel="@{callsViewModel}"
app:conferenceViewModel="@{conferenceViewModel}"/>
app:conferenceViewModel="@{conferenceViewModel}"
app:controlsViewModel="@{controlsViewModel}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintWidth_max="@dimen/voip_extra_menu_max_width" />
<ViewStub
android:id="@+id/stubbed_call_stats"
android:layout="@layout/voip_call_stats"
android:visibility="@{controlsViewModel.callStatsVisible ? View.VISIBLE : View.GONE, default=gone}"
app:inflatedVisibility="@{controlsViewModel.callStatsVisible ? View.VISIBLE : View.GONE}"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:statsViewModel="@{statsViewModel}"
app:controlsViewModel="@{controlsViewModel}"/>
android:layout="@layout/voip_call_stats"
android:visibility="@{controlsViewModel.callStatsVisible ? View.VISIBLE : View.GONE, default=gone}"
app:controlsViewModel="@{controlsViewModel}"
app:inflatedVisibility="@{controlsViewModel.callStatsVisible ? View.VISIBLE : View.GONE}"
app:statsViewModel="@{statsViewModel}" />
<ViewStub
android:id="@+id/stubbed_numpad"
android:layout="@layout/voip_numpad"
android:visibility="@{controlsViewModel.numpadVisible ? View.VISIBLE : View.GONE, default=gone}"
app:inflatedVisibility="@{controlsViewModel.numpadVisible ? View.VISIBLE : View.GONE}"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:controlsViewModel="@{controlsViewModel}"/>
android:layout="@layout/voip_numpad"
android:visibility="@{controlsViewModel.numpadVisible ? View.VISIBLE : View.GONE, default=gone}"
app:controlsViewModel="@{controlsViewModel}"
app:inflatedVisibility="@{controlsViewModel.numpadVisible ? View.VISIBLE : View.GONE}" />
</androidx.constraintlayout.widget.ConstraintLayout>
<include
layout="@layout/voip_conference_creation_pending_wait_layout"
bind:visibility="@{conferenceViewModel.conferenceCreationPending}"/>
bind:visibility="@{conferenceViewModel.conferenceCreationPending}" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -6,7 +6,7 @@
<import type="android.view.View" />
<import type="org.linphone.core.ConferenceLayout" />
<import type="org.linphone.activities.voip.ConferenceDisplayMode" />
<variable
name="cancelClickListener"
@ -71,11 +71,11 @@
android:layout_marginTop="10dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="10dp"
android:checked="@{conferenceViewModel.conferenceMosaicDisplayMode}"
android:checked="@{conferenceViewModel.conferenceDisplayMode == ConferenceDisplayMode.GRID}"
android:drawableEnd="@drawable/icon_conference_layout_grid"
android:drawableTint="?attr/voipDrawableColor"
android:enabled="@{conferenceViewModel.conferenceParticipantDevices.size() > conferenceViewModel.maxParticipantsForMosaicLayout ? false : true}"
android:onClickListener="@{() -> conferenceViewModel.changeLayout(ConferenceLayout.Grid)}"
android:onClickListener="@{() -> conferenceViewModel.changeLayout(ConferenceDisplayMode.GRID)}"
android:text="@string/conference_display_mode_mosaic" />
<View
@ -90,11 +90,11 @@
android:layout_marginTop="10dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="10dp"
android:checked="@{conferenceViewModel.conferenceActiveSpeakerDisplayMode}"
android:checked="@{conferenceViewModel.conferenceDisplayMode == ConferenceDisplayMode.ACTIVE_SPEAKER}"
android:drawableEnd="@drawable/icon_conference_layout_active_speaker"
android:drawableTint="?attr/voipDrawableColor"
android:enabled="@{conferenceViewModel.conferenceParticipantDevices.size() > conferenceViewModel.maxParticipantsForMosaicLayout ? false : true}"
android:onClickListener="@{() -> conferenceViewModel.changeLayout(ConferenceLayout.ActiveSpeaker)}"
android:onClickListener="@{() -> conferenceViewModel.changeLayout(ConferenceDisplayMode.ACTIVE_SPEAKER)}"
android:text="@string/conference_display_mode_active_speaker" />
<View
@ -102,7 +102,6 @@
android:layout_height="1dp"
android:background="?attr/dividerColor" />
<!-- TODO: FIXME: Use AudioOnly layout -->
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -110,10 +109,10 @@
android:layout_marginTop="10dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="10dp"
android:checked="@{conferenceViewModel.conferenceAudioOnlyDisplayMode}"
android:checked="@{conferenceViewModel.conferenceDisplayMode == ConferenceDisplayMode.AUDIO_ONLY}"
android:drawableEnd="@drawable/icon_conference_layout_audio_only"
android:drawableTint="?attr/voipDrawableColor"
android:onClickListener="@{() -> conferenceViewModel.changeLayout(ConferenceLayout.Legacy)}"
android:onClickListener="@{() -> conferenceViewModel.changeLayout(ConferenceDisplayMode.AUDIO_ONLY)}"
android:text="@string/conference_display_mode_audio_only" />
<View

View file

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.view.View" />
<variable
name="data"
type="org.linphone.activities.voip.data.ConferenceParticipantDeviceData" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="@dimen/voip_conference_audio_only_participant_cell_height">
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="5dp"
android:background="@drawable/shape_audio_only_remote_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<include
android:id="@+id/participant_avatar"
layout="@layout/voip_contact_avatar"
android:layout_width="@dimen/voip_conference_audio_only_participant_avatar_size"
android:layout_height="@dimen/voip_conference_audio_only_participant_avatar_size"
android:layout_marginStart="10dp"
app:data="@{data}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHeight_max="200dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/participant_name"
style="@style/call_remote_name_font"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="@{data.contact.fullName ?? data.displayName, default=`Merry Brandybuck`}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/participant_avatar"
app:layout_constraintTop_toTopOf="parent" />
<TextView
style="@style/call_remote_name_font"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@string/conference_participant_paused"
android:visibility="@{data.isInConference ? View.GONE : View.VISIBLE, default=gone}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/participant_name"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="5dp"
android:adjustViewBounds="true"
android:contentDescription="@null"
android:src="@drawable/shape_conference_audio_only_border"
android:visibility="@{data.activeSpeaker ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="@dimen/voip_conference_participant_mic_muted_icon_size_grid"
android:layout_height="@dimen/voip_conference_participant_mic_muted_icon_size_grid"
android:layout_marginEnd="10dp"
android:background="@drawable/shape_button_disabled_background"
android:contentDescription="@string/content_description_conference_participant_mic_muted"
android:padding="5dp"
android:src="@drawable/icon_mic_muted"
android:visibility="@{data.micMuted ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -61,4 +61,6 @@
<dimen name="voip_dialog_button_max_width">137dp</dimen>
<dimen name="voip_contact_avatar_max_height">200dp</dimen>
<dimen name="voip_numpad_button_max_size">60dp</dimen>
<dimen name="voip_conference_audio_only_participant_cell_height">60dp</dimen>
<dimen name="voip_conference_audio_only_participant_avatar_size">40dp</dimen>
</resources>