Specific layout for when conference layout is active speaker & alone or 2

This commit is contained in:
Sylvain Berfini 2022-08-23 17:08:11 +02:00
parent 740a935525
commit 1761843c1b
10 changed files with 548 additions and 43 deletions

View file

@ -23,6 +23,7 @@ Group changes to describe their impact on the project, as follows:
- Android 13 support, using new post notifications & media permissions - Android 13 support, using new post notifications & media permissions
- Call recordings can be exported - Call recordings can be exported
- Setting to prevent international prefix from account to be applied to call & chat - Setting to prevent international prefix from account to be applied to call & chat
- Themed app icon is now supported for Android 13+
### Changed ### Changed
- In-call views have been re-designed - In-call views have been re-designed

View file

@ -21,9 +21,11 @@ package org.linphone.activities.voip.fragments
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.os.SystemClock import android.os.SystemClock
import android.view.View import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.Chronometer import android.widget.Chronometer
import android.widget.Toast import android.widget.Toast
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
@ -31,6 +33,8 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding import androidx.databinding.ViewDataBinding
import androidx.navigation.navGraphViewModels import androidx.navigation.navGraphViewModels
import androidx.transition.AutoTransition
import androidx.transition.TransitionManager
import androidx.window.layout.FoldingFeature import androidx.window.layout.FoldingFeature
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.coreContext
@ -38,9 +42,6 @@ import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R import org.linphone.R
import org.linphone.activities.* import org.linphone.activities.*
import org.linphone.activities.main.MainActivity 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.ConferenceDisplayMode
import org.linphone.activities.voip.viewmodels.CallsViewModel import org.linphone.activities.voip.viewmodels.CallsViewModel
import org.linphone.activities.voip.viewmodels.ConferenceViewModel import org.linphone.activities.voip.viewmodels.ConferenceViewModel
@ -90,9 +91,11 @@ class ConferenceCallFragment : GenericFragment<VoipConferenceCallFragmentBinding
if (displayMode == ConferenceDisplayMode.ACTIVE_SPEAKER) { if (displayMode == ConferenceDisplayMode.ACTIVE_SPEAKER) {
if (conferenceViewModel.conferenceExists.value == true) { 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") Log.i("[Conference Call] Local participant is in conference and current layout is active speaker, updating Core's native window id")
val window = val window = binding.root.findViewById<RoundCornersTextureView>(R.id.conference_active_speaker_remote_video)
binding.root.findViewById<RoundCornersTextureView>(R.id.conference_active_speaker_remote_video)
coreContext.core.nativeVideoWindowId = window coreContext.core.nativeVideoWindowId = window
val preview = binding.root.findViewById<RoundCornersTextureView>(R.id.local_preview_video_surface)
conferenceViewModel.meParticipant.value?.setTextureView(preview)
} else { } else {
Log.i("[Conference Call] Either not in conference or current layout isn't active speaker, updating Core's native window id") Log.i("[Conference Call] Either not in conference or current layout isn't active speaker, updating Core's native window id")
coreContext.core.nativeVideoWindowId = null coreContext.core.nativeVideoWindowId = null
@ -115,6 +118,22 @@ class ConferenceCallFragment : GenericFragment<VoipConferenceCallFragmentBinding
} }
} }
conferenceViewModel.secondParticipantJoinedEvent.observe(
viewLifecycleOwner
) {
it.consume {
switchToActiveSpeakerLayoutForTwoParticipants()
}
}
conferenceViewModel.moreThanTwoParticipantsJoinedEvent.observe(
viewLifecycleOwner
) {
it.consume {
switchToActiveSpeakerLayoutForMoreThanTwoParticipants()
}
}
conferenceViewModel.conference.observe( conferenceViewModel.conference.observe(
viewLifecycleOwner viewLifecycleOwner
) { conference -> ) { conference ->
@ -163,6 +182,8 @@ class ConferenceCallFragment : GenericFragment<VoipConferenceCallFragmentBinding
.make(binding.coordinator, R.string.conference_last_user, Snackbar.LENGTH_LONG) .make(binding.coordinator, R.string.conference_last_user, Snackbar.LENGTH_LONG)
.setAnchorView(binding.primaryButtons.hangup) .setAnchorView(binding.primaryButtons.hangup)
.show() .show()
switchToActiveSpeakerLayoutWhenAlone()
} }
} }
@ -278,9 +299,21 @@ class ConferenceCallFragment : GenericFragment<VoipConferenceCallFragmentBinding
controlsViewModel.hideExtraButtons(true) controlsViewModel.hideExtraButtons(true)
} }
override fun onResume() {
super.onResume()
if (conferenceViewModel.conferenceCreationPending.value == false) {
when (conferenceViewModel.conferenceParticipantDevices.value.orEmpty().size) {
1 -> switchToActiveSpeakerLayoutWhenAlone()
2 -> switchToActiveSpeakerLayoutForTwoParticipants()
else -> switchToActiveSpeakerLayoutForMoreThanTwoParticipants()
}
}
}
private fun switchToFullScreenIfPossible(conference: Conference) { private fun switchToFullScreenIfPossible(conference: Conference) {
if (corePreferences.enableFullScreenWhenJoiningVideoConference) { if (corePreferences.enableFullScreenWhenJoiningVideoConference) {
if (conference.currentParams.isVideoEnabled) { if (conference.currentParams.isVideoEnabled && conferenceViewModel.conferenceCreationPending.value == false) {
when { when {
conference.me.devices.isEmpty() -> { conference.me.devices.isEmpty() -> {
Log.w("[Conference Call] Conference has video enabled but either our device hasn't joined yet") Log.w("[Conference Call] Conference has video enabled but either our device hasn't joined yet")
@ -305,10 +338,6 @@ class ConferenceCallFragment : GenericFragment<VoipConferenceCallFragmentBinding
startActivity(intent) startActivity(intent)
} }
private fun showSnackBar(resourceId: Int) {
Snackbar.make(binding.coordinator, resourceId, Snackbar.LENGTH_LONG).show()
}
private fun startTimer(timerId: Int) { private fun startTimer(timerId: Int) {
val timer: Chronometer? = binding.root.findViewById(timerId) val timer: Chronometer? = binding.root.findViewById(timerId)
if (timer == null) { if (timer == null) {
@ -349,4 +378,190 @@ class ConferenceCallFragment : GenericFragment<VoipConferenceCallFragmentBinding
set.applyTo(constraintLayout) set.applyTo(constraintLayout)
} }
private fun animateConstraintLayout(
constraintLayout: ConstraintLayout,
set: ConstraintSet
) {
val trans = AutoTransition()
trans.duration = 500
trans.interpolator = AccelerateDecelerateInterpolator()
TransitionManager.beginDelayedTransition(constraintLayout, trans)
set.applyTo(constraintLayout)
}
private fun switchToActiveSpeakerLayoutForMoreThanTwoParticipants() {
if (conferenceViewModel.conferenceDisplayMode.value != ConferenceDisplayMode.ACTIVE_SPEAKER) return
val constraintLayout =
binding.root.findViewById<ConstraintLayout>(R.id.conference_constraint_layout)
?: return
val set = ConstraintSet()
set.clone(constraintLayout)
set.clear(R.id.local_participant_background, ConstraintSet.TOP)
set.clear(R.id.local_participant_background, ConstraintSet.START)
set.clear(R.id.local_participant_background, ConstraintSet.BOTTOM)
set.clear(R.id.local_participant_background, ConstraintSet.END)
val margin = resources.getDimension(R.dimen.voip_active_speaker_miniature_margin).toInt()
val portraitOrientation = resources.configuration.orientation != Configuration.ORIENTATION_LANDSCAPE
if (portraitOrientation) {
set.connect(
R.id.local_participant_background,
ConstraintSet.START,
R.id.conference_constraint_layout,
ConstraintSet.START,
margin
)
set.connect(
R.id.local_participant_background,
ConstraintSet.BOTTOM,
R.id.miniatures,
ConstraintSet.BOTTOM,
0
)
set.connect(
R.id.local_participant_background,
ConstraintSet.TOP,
R.id.miniatures,
ConstraintSet.TOP,
0
)
} else {
set.connect(
R.id.local_participant_background,
ConstraintSet.TOP,
R.id.top_barrier,
ConstraintSet.BOTTOM,
0
)
set.connect(
R.id.local_participant_background,
ConstraintSet.START,
R.id.active_speaker_background,
ConstraintSet.END,
0
)
set.connect(
R.id.local_participant_background,
ConstraintSet.END,
R.id.scroll_indicator,
ConstraintSet.START,
0
)
}
val size = resources.getDimension(R.dimen.voip_active_speaker_miniature_size).toInt()
set.constrainWidth(
R.id.local_participant_background,
size
)
set.constrainHeight(
R.id.local_participant_background,
size
)
if (corePreferences.enableAnimations) {
animateConstraintLayout(constraintLayout, set)
} else {
set.applyTo(constraintLayout)
}
}
private fun switchToActiveSpeakerLayoutForTwoParticipants() {
if (conferenceViewModel.conferenceDisplayMode.value != ConferenceDisplayMode.ACTIVE_SPEAKER) return
val constraintLayout =
binding.root.findViewById<ConstraintLayout>(R.id.conference_constraint_layout)
?: return
val set = ConstraintSet()
set.clone(constraintLayout)
set.clear(R.id.local_participant_background, ConstraintSet.TOP)
set.clear(R.id.local_participant_background, ConstraintSet.START)
set.clear(R.id.local_participant_background, ConstraintSet.BOTTOM)
set.clear(R.id.local_participant_background, ConstraintSet.END)
val margin = resources.getDimension(R.dimen.voip_active_speaker_miniature_margin).toInt()
set.connect(
R.id.local_participant_background,
ConstraintSet.BOTTOM,
R.id.conference_constraint_layout,
ConstraintSet.BOTTOM,
margin
)
// Don't know why but if we use END instead of RIGHT, margin isn't applied...
set.connect(
R.id.local_participant_background,
ConstraintSet.RIGHT,
R.id.conference_constraint_layout,
ConstraintSet.RIGHT,
margin
)
val size = resources.getDimension(R.dimen.voip_active_speaker_miniature_size).toInt()
set.constrainWidth(
R.id.local_participant_background,
size
)
set.constrainHeight(
R.id.local_participant_background,
size
)
if (corePreferences.enableAnimations) {
animateConstraintLayout(constraintLayout, set)
} else {
set.applyTo(constraintLayout)
}
}
private fun switchToActiveSpeakerLayoutWhenAlone() {
if (conferenceViewModel.conferenceDisplayMode.value != ConferenceDisplayMode.ACTIVE_SPEAKER) return
val constraintLayout =
binding.root.findViewById<ConstraintLayout>(R.id.conference_constraint_layout)
?: return
val set = ConstraintSet()
set.clone(constraintLayout)
set.connect(
R.id.local_participant_background,
ConstraintSet.BOTTOM,
R.id.conference_constraint_layout,
ConstraintSet.BOTTOM,
0
)
set.connect(
R.id.local_participant_background,
ConstraintSet.END,
R.id.conference_constraint_layout,
ConstraintSet.END,
0
)
set.connect(
R.id.local_participant_background,
ConstraintSet.TOP,
R.id.top_barrier,
ConstraintSet.BOTTOM,
0
)
set.connect(
R.id.local_participant_background,
ConstraintSet.START,
R.id.conference_constraint_layout,
ConstraintSet.START,
0
)
set.constrainWidth(R.id.local_participant_background, 0)
set.constrainHeight(R.id.local_participant_background, 0)
if (corePreferences.enableAnimations) {
animateConstraintLayout(constraintLayout, set)
} else {
set.applyTo(constraintLayout)
}
}
} }

View file

@ -19,6 +19,7 @@
*/ */
package org.linphone.activities.voip.viewmodels package org.linphone.activities.voip.viewmodels
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.coreContext
@ -45,13 +46,17 @@ class ConferenceViewModel : ViewModel() {
val conferenceParticipants = MutableLiveData<List<ConferenceParticipantData>>() val conferenceParticipants = MutableLiveData<List<ConferenceParticipantData>>()
val conferenceParticipantDevices = MutableLiveData<List<ConferenceParticipantDeviceData>>() val conferenceParticipantDevices = MutableLiveData<List<ConferenceParticipantDeviceData>>()
val conferenceDisplayMode = MutableLiveData<ConferenceDisplayMode>() val conferenceDisplayMode = MutableLiveData<ConferenceDisplayMode>()
val activeSpeakerConferenceParticipantDevices = MediatorLiveData<List<ConferenceParticipantDeviceData>>()
val isRecording = MutableLiveData<Boolean>() val isRecording = MutableLiveData<Boolean>()
val isRemotelyRecorded = MutableLiveData<Boolean>() val isRemotelyRecorded = MutableLiveData<Boolean>()
val maxParticipantsForMosaicLayout = corePreferences.maxConferenceParticipantsForMosaicLayout val maxParticipantsForMosaicLayout = corePreferences.maxConferenceParticipantsForMosaicLayout
val moreThanTwoParticipants = MutableLiveData<Boolean>()
val speakingParticipant = MutableLiveData<ConferenceParticipantDeviceData>() val speakingParticipant = MutableLiveData<ConferenceParticipantDeviceData>()
val meParticipant = MutableLiveData<ConferenceParticipantDeviceData>()
val participantAdminStatusChangedEvent: MutableLiveData<Event<ConferenceParticipantData>> by lazy { val participantAdminStatusChangedEvent: MutableLiveData<Event<ConferenceParticipantData>> by lazy {
MutableLiveData<Event<ConferenceParticipantData>>() MutableLiveData<Event<ConferenceParticipantData>>()
@ -65,6 +70,14 @@ class ConferenceViewModel : ViewModel() {
MutableLiveData<Event<Boolean>>() MutableLiveData<Event<Boolean>>()
} }
val secondParticipantJoinedEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val moreThanTwoParticipantsJoinedEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
private val conferenceListener = object : ConferenceListenerStub() { private val conferenceListener = object : ConferenceListenerStub() {
override fun onParticipantAdded(conference: Conference, participant: Participant) { override fun onParticipantAdded(conference: Conference, participant: Participant) {
Log.i("[Conference] Participant added: ${participant.address.asStringUriOnly()}") Log.i("[Conference] Participant added: ${participant.address.asStringUriOnly()}")
@ -77,11 +90,6 @@ class ConferenceViewModel : ViewModel() {
if (conferenceParticipants.value.orEmpty().isEmpty()) { if (conferenceParticipants.value.orEmpty().isEmpty()) {
allParticipantsLeftEvent.value = Event(true) allParticipantsLeftEvent.value = Event(true)
// TODO: FIXME: Temporary workaround when alone in a conference in active speaker layout
val meDeviceData = conferenceParticipantDevices.value.orEmpty().firstOrNull()
if (meDeviceData != null) {
speakingParticipant.value = meDeviceData!!
}
} }
} }
@ -91,6 +99,12 @@ class ConferenceViewModel : ViewModel() {
) { ) {
Log.i("[Conference] Participant device added: ${participantDevice.address.asStringUriOnly()}") Log.i("[Conference] Participant device added: ${participantDevice.address.asStringUriOnly()}")
addParticipantDevice(participantDevice) addParticipantDevice(participantDevice)
if (conferenceParticipantDevices.value.orEmpty().size == 2) {
secondParticipantJoinedEvent.value = Event(true)
} else if (conferenceParticipantDevices.value.orEmpty().size == 3) {
moreThanTwoParticipantsJoinedEvent.value = Event(true)
}
} }
override fun onParticipantDeviceRemoved( override fun onParticipantDeviceRemoved(
@ -99,6 +113,10 @@ class ConferenceViewModel : ViewModel() {
) { ) {
Log.i("[Conference] Participant device removed: ${participantDevice.address.asStringUriOnly()}") Log.i("[Conference] Participant device removed: ${participantDevice.address.asStringUriOnly()}")
removeParticipantDevice(participantDevice) removeParticipantDevice(participantDevice)
if (conferenceParticipantDevices.value.orEmpty().size == 2) {
secondParticipantJoinedEvent.value = Event(true)
}
} }
override fun onParticipantAdminStatusChanged( override fun onParticipantAdminStatusChanged(
@ -208,6 +226,9 @@ class ConferenceViewModel : ViewModel() {
conferenceParticipants.value = arrayListOf() conferenceParticipants.value = arrayListOf()
conferenceParticipantDevices.value = arrayListOf() conferenceParticipantDevices.value = arrayListOf()
activeSpeakerConferenceParticipantDevices.addSource(conferenceParticipantDevices) {
activeSpeakerConferenceParticipantDevices.value = conferenceParticipantDevices.value.orEmpty().drop(1)
}
subject.value = AppUtils.getString(R.string.conference_default_title) subject.value = AppUtils.getString(R.string.conference_default_title)
@ -240,6 +261,7 @@ class ConferenceViewModel : ViewModel() {
conferenceParticipants.value.orEmpty().forEach(ConferenceParticipantData::destroy) conferenceParticipants.value.orEmpty().forEach(ConferenceParticipantData::destroy)
conferenceParticipantDevices.value.orEmpty().forEach(ConferenceParticipantDeviceData::destroy) conferenceParticipantDevices.value.orEmpty().forEach(ConferenceParticipantDeviceData::destroy)
activeSpeakerConferenceParticipantDevices.value.orEmpty().forEach(ConferenceParticipantDeviceData::destroy)
super.onCleared() super.onCleared()
} }
@ -283,7 +305,13 @@ class ConferenceViewModel : ViewModel() {
if (conferenceParticipants.value.orEmpty().isEmpty()) { if (conferenceParticipants.value.orEmpty().isEmpty()) {
firstToJoinEvent.value = Event(true) firstToJoinEvent.value = Event(true)
} }
updateParticipantsDevicesList(conference) updateParticipantsDevicesList(conference)
if (conferenceParticipantDevices.value.orEmpty().size == 2) {
secondParticipantJoinedEvent.value = Event(true)
} else if (conferenceParticipantDevices.value.orEmpty().size > 2) {
moreThanTwoParticipantsJoinedEvent.value = Event(true)
}
isConferenceLocallyPaused.value = !conference.isIn isConferenceLocallyPaused.value = !conference.isIn
isMeAdmin.value = conference.me.isAdmin isMeAdmin.value = conference.me.isAdmin
@ -371,6 +399,8 @@ class ConferenceViewModel : ViewModel() {
conferenceParticipants.value.orEmpty().forEach(ConferenceParticipantData::destroy) conferenceParticipants.value.orEmpty().forEach(ConferenceParticipantData::destroy)
conferenceParticipantDevices.value.orEmpty().forEach(ConferenceParticipantDeviceData::destroy) conferenceParticipantDevices.value.orEmpty().forEach(ConferenceParticipantDeviceData::destroy)
activeSpeakerConferenceParticipantDevices.value.orEmpty().forEach(ConferenceParticipantDeviceData::destroy)
conferenceParticipants.value = arrayListOf() conferenceParticipants.value = arrayListOf()
conferenceParticipantDevices.value = arrayListOf() conferenceParticipantDevices.value = arrayListOf()
} }
@ -394,6 +424,7 @@ class ConferenceViewModel : ViewModel() {
private fun updateParticipantsDevicesList(conference: Conference) { private fun updateParticipantsDevicesList(conference: Conference) {
conferenceParticipantDevices.value.orEmpty().forEach(ConferenceParticipantDeviceData::destroy) conferenceParticipantDevices.value.orEmpty().forEach(ConferenceParticipantDeviceData::destroy)
activeSpeakerConferenceParticipantDevices.value.orEmpty().forEach(ConferenceParticipantDeviceData::destroy)
val devices = arrayListOf<ConferenceParticipantDeviceData>() val devices = arrayListOf<ConferenceParticipantDeviceData>()
val participantsList = conference.participantList val participantsList = conference.participantList
@ -415,14 +446,12 @@ class ConferenceViewModel : ViewModel() {
for (device in conference.me.devices) { for (device in conference.me.devices) {
Log.i("[Conference] Participant device for myself found: ${device.name} (${device.address.asStringUriOnly()})") Log.i("[Conference] Participant device for myself found: ${device.name} (${device.address.asStringUriOnly()})")
val deviceData = ConferenceParticipantDeviceData(device, true) val deviceData = ConferenceParticipantDeviceData(device, true)
if (devices.isEmpty()) {
// TODO: FIXME: Temporary workaround when alone in a conference in active speaker layout
speakingParticipant.value = deviceData
}
devices.add(deviceData) devices.add(deviceData)
meParticipant.value = deviceData
} }
conferenceParticipantDevices.value = devices conferenceParticipantDevices.value = devices
moreThanTwoParticipants.value = devices.size > 2
} }
private fun addParticipantDevice(device: ParticipantDevice) { private fun addParticipantDevice(device: ParticipantDevice) {
@ -448,6 +477,7 @@ class ConferenceViewModel : ViewModel() {
} }
conferenceParticipantDevices.value = sortedDevices conferenceParticipantDevices.value = sortedDevices
moreThanTwoParticipants.value = sortedDevices.size > 2
} }
private fun removeParticipantDevice(device: ParticipantDevice) { private fun removeParticipantDevice(device: ParticipantDevice) {
@ -456,6 +486,8 @@ class ConferenceViewModel : ViewModel() {
for (participantDevice in conferenceParticipantDevices.value.orEmpty()) { for (participantDevice in conferenceParticipantDevices.value.orEmpty()) {
if (participantDevice.participantDevice.address.asStringUriOnly() != device.address.asStringUriOnly()) { if (participantDevice.participantDevice.address.asStringUriOnly() != device.address.asStringUriOnly()) {
devices.add(participantDevice) devices.add(participantDevice)
} else {
participantDevice.destroy()
} }
} }
if (devices.size == conferenceParticipantDevices.value.orEmpty().size) { if (devices.size == conferenceParticipantDevices.value.orEmpty().size) {
@ -465,6 +497,7 @@ class ConferenceViewModel : ViewModel() {
} }
conferenceParticipantDevices.value = devices conferenceParticipantDevices.value = devices
moreThanTwoParticipants.value = devices.size > 2
} }
private fun sortDevicesDataList(devices: List<ConferenceParticipantDeviceData>): ArrayList<ConferenceParticipantDeviceData> { private fun sortDevicesDataList(devices: List<ConferenceParticipantDeviceData>): ArrayList<ConferenceParticipantDeviceData> {

View file

@ -75,7 +75,7 @@ class ControlsViewModel : ViewModel() {
val proximitySensorEnabled = MediatorLiveData<Boolean>() val proximitySensorEnabled = MediatorLiveData<Boolean>()
val showTakeSnaptshotButton = MutableLiveData<Boolean>() val showTakeSnapshotButton = MutableLiveData<Boolean>()
val goToConferenceParticipantsListEvent: MutableLiveData<Event<Boolean>> by lazy { val goToConferenceParticipantsListEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>() MutableLiveData<Event<Boolean>>()
@ -436,14 +436,15 @@ class ControlsViewModel : ViewModel() {
} }
isVideoEnabled.value = enabled isVideoEnabled.value = enabled
showTakeSnaptshotButton.value = enabled && corePreferences.showScreenshotButton showTakeSnapshotButton.value = enabled && corePreferences.showScreenshotButton
isSwitchCameraAvailable.value = enabled && coreContext.showSwitchCameraButton() var isVideoBeingSent = if (coreContext.core.currentCall?.conference != null) {
isSendingVideo.value = if (coreContext.core.currentCall?.conference != null) {
val videoDirection = coreContext.core.currentCall?.currentParams?.videoDirection val videoDirection = coreContext.core.currentCall?.currentParams?.videoDirection
videoDirection == MediaDirection.SendRecv || videoDirection == MediaDirection.SendOnly videoDirection == MediaDirection.SendRecv || videoDirection == MediaDirection.SendOnly
} else { } else {
true true
} }
isSendingVideo.value = isVideoBeingSent
isSwitchCameraAvailable.value = enabled && coreContext.showSwitchCameraButton() && isVideoBeingSent
} }
private fun shouldProximitySensorBeEnabled(): Boolean { private fun shouldProximitySensorBeEnabled(): Boolean {

View file

@ -337,11 +337,18 @@ private suspend fun loadContactPictureWithCoil(
size: Int = 0, size: Int = 0,
textSize: Int = 0, textSize: Int = 0,
color: Int = 0, color: Int = 0,
textColor: Int = 0 textColor: Int = 0,
defaultAvatar: String? = null
) { ) {
val context = imageView.context val context = imageView.context
if (contact == null) { if (contact == null) {
imageView.load(R.drawable.icon_single_contact_avatar) if (defaultAvatar != null) {
imageView.load(defaultAvatar) {
transformations(CircleCropTransformation())
}
} else {
imageView.load(R.drawable.icon_single_contact_avatar)
}
} else if (contact.showGroupChatAvatar) { } else if (contact.showGroupChatAvatar) {
imageView.load(AppCompatResources.getDrawable(context, R.drawable.icon_multiple_contacts_avatar)) imageView.load(AppCompatResources.getDrawable(context, R.drawable.icon_multiple_contacts_avatar))
} else { } else {
@ -430,6 +437,21 @@ fun loadVoipContactPictureWithCoil(imageView: ImageView, contact: ContactDataInt
} }
} }
@BindingAdapter("coilSelfAvatar")
fun loadSelfAvatarWithCoil(imageView: ImageView, contact: ContactDataInterface?) {
val coroutineScope = contact?.coroutineScope ?: coreContext.coroutineScope
coroutineScope.launch {
withContext(Dispatchers.Main) {
loadContactPictureWithCoil(
imageView, contact, false,
R.dimen.voip_contact_avatar_max_size, R.dimen.voip_contact_avatar_text_size,
R.attr.voipBackgroundColor, R.color.white_color,
corePreferences.defaultAccountAvatarPath
)
}
}
}
@BindingAdapter("coilGoneIfError") @BindingAdapter("coilGoneIfError")
fun loadAvatarWithCoil(imageView: ImageView, path: String?) { fun loadAvatarWithCoil(imageView: ImageView, path: String?) {
if (path != null) { if (path != null) {

View file

@ -50,13 +50,6 @@
android:orientation="horizontal" android:orientation="horizontal"
app:layout_constraintGuide_percent="1" /> app:layout_constraintGuide_percent="1" />
<androidx.constraintlayout.widget.Group
android:id="@+id/group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{controlsViewModel.fullScreenMode || controlsViewModel.pipMode ? View.GONE : View.VISIBLE}"
app:constraint_referenced_ids="remote_name,active_conference_timer,toggle_conference_recording,toggle_pause_conference" />
<TextView <TextView
android:id="@+id/remote_name" android:id="@+id/remote_name"
style="@style/call_header_title" style="@style/call_header_title"
@ -117,11 +110,18 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:visibility="@{conferenceViewModel.isRemotelyRecorded ? View.VISIBLE : View.GONE, default=gone}" /> android:visibility="@{conferenceViewModel.isRemotelyRecorded ? View.VISIBLE : View.GONE, default=gone}" />
<androidx.constraintlayout.widget.Group
android:id="@+id/active_speaker_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{conferenceViewModel.conferenceParticipantDevices.size() == 1 ? View.GONE : View.VISIBLE, default=gone}"
app:constraint_referenced_ids="active_speaker_background,speaking_participant_avatar,conference_active_speaker_remote_video,speaking_participant_name" />
<org.linphone.activities.voip.views.ScrollDotsView <org.linphone.activities.voip.views.ScrollDotsView
android:id="@+id/scroll_indicator" android:id="@+id/scroll_indicator"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:visibility="@{controlsViewModel.pipMode ? View.GONE : View.VISIBLE}" android:visibility="@{controlsViewModel.pipMode || !conferenceViewModel.moreThanTwoParticipants ? View.GONE : View.VISIBLE}"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/top_barrier" app:layout_constraintTop_toBottomOf="@id/top_barrier"
app:layout_constraintBottom_toBottomOf="@id/hinge_bottom" app:layout_constraintBottom_toBottomOf="@id/hinge_bottom"
@ -137,9 +137,11 @@
android:id="@+id/miniatures" android:id="@+id/miniatures"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="0dp" android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/top_barrier" android:layout_marginTop="5dp"
app:layout_constraintTop_toBottomOf="@id/local_participant_background"
app:layout_constraintEnd_toStartOf="@id/scroll_indicator" app:layout_constraintEnd_toStartOf="@id/scroll_indicator"
app:layout_constraintBottom_toBottomOf="@id/hinge_bottom" app:layout_constraintBottom_toBottomOf="@id/hinge_bottom"
android:visibility="@{conferenceViewModel.moreThanTwoParticipants ? View.VISIBLE : View.GONE, default=gone}"
android:scrollbars="none"> android:scrollbars="none">
<com.google.android.flexbox.FlexboxLayout <com.google.android.flexbox.FlexboxLayout
@ -147,7 +149,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:alignItems="stretch" app:alignItems="stretch"
app:entries="@{conferenceViewModel.conferenceParticipantDevices}" app:entries="@{conferenceViewModel.activeSpeakerConferenceParticipantDevices}"
app:flexDirection="column" app:flexDirection="column"
app:flexWrap="nowrap" app:flexWrap="nowrap"
app:justifyContent="flex_start" app:justifyContent="flex_start"
@ -173,6 +175,7 @@
android:layout_height="0dp" android:layout_height="0dp"
android:contentDescription="@null" android:contentDescription="@null"
coilVoipContact="@{conferenceViewModel.speakingParticipant}" coilVoipContact="@{conferenceViewModel.speakingParticipant}"
android:visibility="@{conferenceViewModel.speakingParticipant.isJoining ? View.GONE : View.VISIBLE}"
android:background="@drawable/generated_avatar_bg" android:background="@drawable/generated_avatar_bg"
app:layout_constraintDimensionRatio="1:1" app:layout_constraintDimensionRatio="1:1"
app:layout_constraintBottom_toBottomOf="@id/active_speaker_background" app:layout_constraintBottom_toBottomOf="@id/active_speaker_background"
@ -182,6 +185,16 @@
app:layout_constraintTop_toTopOf="@id/active_speaker_background" app:layout_constraintTop_toTopOf="@id/active_speaker_background"
app:layout_constraintWidth_max="@dimen/voip_contact_avatar_max_size" /> app:layout_constraintWidth_max="@dimen/voip_contact_avatar_max_size" />
<ProgressBar
android:id="@+id/speaking_participant_joining"
android:layout_width="@dimen/voip_conference_participant_joining_icon_size_active_speaker"
android:layout_height="@dimen/voip_conference_participant_joining_icon_size_active_speaker"
android:indeterminate="true"
android:indeterminateDrawable="@drawable/icon_spinner_rotating"
android:visibility="@{conferenceViewModel.speakingParticipant.isJoining ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintLeft_toLeftOf="@id/active_speaker_background"
app:layout_constraintTop_toTopOf="@id/active_speaker_background" />
<org.linphone.activities.voip.views.RoundCornersTextureView <org.linphone.activities.voip.views.RoundCornersTextureView
android:id="@+id/conference_active_speaker_remote_video" android:id="@+id/conference_active_speaker_remote_video"
android:layout_width="0dp" android:layout_width="0dp"
@ -203,6 +216,109 @@
app:layout_constraintBottom_toBottomOf="@id/active_speaker_background" app:layout_constraintBottom_toBottomOf="@id/active_speaker_background"
app:layout_constraintStart_toStartOf="@id/active_speaker_background" /> app:layout_constraintStart_toStartOf="@id/active_speaker_background" />
<View
android:id="@+id/local_participant_background"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/top_barrier"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginTop="@{controlsViewModel.fullScreenMode ? @dimen/margin_0dp : @dimen/voip_active_speaker_top_margin, default=@dimen/voip_active_speaker_top_margin}"
android:background="@drawable/shape_remote_background"
android:onClick="@{() -> controlsViewModel.toggleFullScreen()}"/>
<ImageView
android:id="@+id/local_participant_avatar"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="10dp"
android:contentDescription="@null"
coilSelfAvatar="@{conferenceViewModel.meParticipant}"
android:background="@drawable/generated_avatar_bg"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintBottom_toBottomOf="@id/local_participant_background"
app:layout_constraintEnd_toEndOf="@id/local_participant_background"
app:layout_constraintHeight_max="@dimen/voip_contact_avatar_max_size"
app:layout_constraintStart_toStartOf="@id/local_participant_background"
app:layout_constraintTop_toTopOf="@id/local_participant_background"
app:layout_constraintWidth_max="@dimen/voip_contact_avatar_max_size" />
<org.linphone.activities.voip.views.RoundCornersTextureView
android:id="@+id/local_preview_video_surface"
android:layout_width="0dp"
android:layout_height="0dp"
app:alignTopRight="false"
app:displayMode="occupy_all_space"
android:visibility="@{conferenceViewModel.meParticipant.isInConference &amp;&amp; controlsViewModel.isSendingVideo ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toBottomOf="@id/local_participant_background"
app:layout_constraintEnd_toEndOf="@id/local_participant_background"
app:layout_constraintStart_toStartOf="@id/local_participant_background"
app:layout_constraintTop_toTopOf="@id/local_participant_background" />
<TextView
android:id="@+id/local_participant_name"
style="@style/call_remote_name_font"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:layout_marginBottom="5dp"
android:text="@{conferenceViewModel.meParticipant.contact.name ?? conferenceViewModel.meParticipant.displayName}"
app:layout_constraintBottom_toBottomOf="@id/local_participant_background"
app:layout_constraintStart_toStartOf="@id/local_participant_background"
app:layout_constraintEnd_toEndOf="@id/local_participant_background"/>
<ImageView
android:id="@+id/switch_camera"
android:layout_width="@dimen/conference_miniature_switch_camera_icon_size"
android:layout_height="@dimen/conference_miniature_switch_camera_icon_size"
android:layout_margin="5dp"
android:contentDescription="@string/content_description_switch_camera"
android:onClick="@{() -> controlsViewModel.switchCamera()}"
android:src="@drawable/icon_call_camera_switch"
android:visibility="@{controlsViewModel.isSwitchCameraAvailable &amp;&amp; !controlsViewModel.pipMode &amp;&amp; !controlsViewModel.fullScreenMode ? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="@id/local_participant_background"
app:layout_constraintTop_toTopOf="@id/local_participant_background" />
<ImageView
android:id="@+id/local_participant_speaking_border"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@null"
android:src="@drawable/shape_conference_active_speaker_border"
android:visibility="@{conferenceViewModel.meParticipant.isSpeaking ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toBottomOf="@id/local_participant_background"
app:layout_constraintLeft_toLeftOf="@id/local_participant_background"
app:layout_constraintRight_toRightOf="@id/local_participant_background"
app:layout_constraintTop_toTopOf="@id/local_participant_background" />
<ImageView
android:id="@+id/local_participant_muted"
android:layout_width="@dimen/voip_conference_participant_mic_muted_icon_size_active_speaker"
android:layout_height="@dimen/voip_conference_participant_mic_muted_icon_size_active_speaker"
android:layout_margin="5dp"
android:background="@drawable/shape_toggle_pressed_background"
android:contentDescription="@string/content_description_conference_participant_mic_muted"
android:padding="2dp"
android:src="@drawable/icon_mic_muted"
android:visibility="@{conferenceViewModel.meParticipant.isMuted ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintLeft_toLeftOf="@id/local_participant_background"
app:layout_constraintTop_toTopOf="@id/local_participant_background" />
<ImageView
android:id="@+id/local_participant_paused"
android:layout_width="@dimen/voip_conference_active_speaker_miniature_avatar_size"
android:layout_height="@dimen/voip_conference_active_speaker_miniature_avatar_size"
android:background="@drawable/shape_button_background"
android:contentDescription="@string/content_description_participant_is_paused"
android:src="@drawable/icon_pause"
android:visibility="@{conferenceViewModel.conferenceCreationPending || conferenceViewModel.meParticipant.isInConference || conferenceViewModel.meParticipant.isJoining ? View.GONE : View.VISIBLE, default=gone}"
app:layout_constraintBottom_toBottomOf="@id/local_participant_background"
app:layout_constraintEnd_toEndOf="@id/local_participant_background"
app:layout_constraintStart_toStartOf="@id/local_participant_background"
app:layout_constraintTop_toTopOf="@id/local_participant_background" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</layout> </layout>

View file

@ -136,15 +136,26 @@
android:layout_margin="10dp" android:layout_margin="10dp"
android:contentDescription="@null" android:contentDescription="@null"
coilVoipContact="@{conferenceViewModel.speakingParticipant}" coilVoipContact="@{conferenceViewModel.speakingParticipant}"
android:visibility="@{conferenceViewModel.speakingParticipant.isJoining ? View.GONE : View.VISIBLE}"
android:background="@drawable/generated_avatar_bg" android:background="@drawable/generated_avatar_bg"
app:layout_constraintDimensionRatio="1:1" app:layout_constraintDimensionRatio="1:1"
app:layout_constraintBottom_toTopOf="@id/miniatures" app:layout_constraintBottom_toBottomOf="@id/active_speaker_background"
app:layout_constraintEnd_toEndOf="@id/active_speaker_background" app:layout_constraintEnd_toEndOf="@id/active_speaker_background"
app:layout_constraintHeight_max="@dimen/voip_contact_avatar_max_size" app:layout_constraintHeight_max="@dimen/voip_contact_avatar_max_size"
app:layout_constraintStart_toStartOf="@id/active_speaker_background" app:layout_constraintStart_toStartOf="@id/active_speaker_background"
app:layout_constraintTop_toBottomOf="@id/top_barrier" app:layout_constraintTop_toTopOf="@id/active_speaker_background"
app:layout_constraintWidth_max="@dimen/voip_contact_avatar_max_size" /> app:layout_constraintWidth_max="@dimen/voip_contact_avatar_max_size" />
<ProgressBar
android:id="@+id/speaking_participant_joining"
android:layout_width="@dimen/voip_conference_participant_joining_icon_size_active_speaker"
android:layout_height="@dimen/voip_conference_participant_joining_icon_size_active_speaker"
android:indeterminate="true"
android:indeterminateDrawable="@drawable/icon_spinner_rotating"
android:visibility="@{conferenceViewModel.speakingParticipant.isJoining ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintLeft_toLeftOf="@id/active_speaker_background"
app:layout_constraintTop_toTopOf="@id/active_speaker_background" />
<org.linphone.activities.voip.views.RoundCornersTextureView <org.linphone.activities.voip.views.RoundCornersTextureView
android:id="@+id/conference_active_speaker_remote_video" android:id="@+id/conference_active_speaker_remote_video"
android:layout_width="0dp" android:layout_width="0dp"
@ -170,9 +181,11 @@
android:id="@+id/miniatures" android:id="@+id/miniatures"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="5dp"
app:layout_constraintStart_toEndOf="@id/local_participant_background"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/scroll_indicator" app:layout_constraintBottom_toTopOf="@id/scroll_indicator"
android:visibility="@{conferenceViewModel.moreThanTwoParticipants ? View.VISIBLE : View.GONE, default=gone}"
android:scrollbars="none"> android:scrollbars="none">
<com.google.android.flexbox.FlexboxLayout <com.google.android.flexbox.FlexboxLayout
@ -180,7 +193,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
app:alignItems="stretch" app:alignItems="stretch"
app:entries="@{conferenceViewModel.conferenceParticipantDevices}" app:entries="@{conferenceViewModel.activeSpeakerConferenceParticipantDevices}"
app:flexDirection="row" app:flexDirection="row"
app:flexWrap="nowrap" app:flexWrap="nowrap"
app:justifyContent="flex_start" app:justifyContent="flex_start"
@ -192,7 +205,7 @@
android:id="@+id/scroll_indicator" android:id="@+id/scroll_indicator"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:visibility="@{controlsViewModel.pipMode ? View.GONE : View.VISIBLE}" android:visibility="@{controlsViewModel.pipMode || !conferenceViewModel.moreThanTwoParticipants ? View.GONE : View.VISIBLE, default=gone}"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/hinge_bottom" app:layout_constraintBottom_toBottomOf="@id/hinge_bottom"
@ -204,6 +217,109 @@
app:selectedDot="@{0}" app:selectedDot="@{0}"
app:selectedDotColor="@color/voip_dark_gray" /> app:selectedDotColor="@color/voip_dark_gray" />
<View
android:id="@+id/local_participant_background"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/top_barrier"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginTop="@{controlsViewModel.fullScreenMode ? @dimen/margin_0dp : @dimen/voip_active_speaker_top_margin, default=@dimen/voip_active_speaker_top_margin}"
android:background="@drawable/shape_remote_background"
android:onClick="@{() -> controlsViewModel.toggleFullScreen()}"/>
<ImageView
android:id="@+id/local_participant_avatar"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="10dp"
android:contentDescription="@null"
coilSelfAvatar="@{conferenceViewModel.meParticipant}"
android:background="@drawable/generated_avatar_bg"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintBottom_toBottomOf="@id/local_participant_background"
app:layout_constraintEnd_toEndOf="@id/local_participant_background"
app:layout_constraintHeight_max="@dimen/voip_contact_avatar_max_size"
app:layout_constraintStart_toStartOf="@id/local_participant_background"
app:layout_constraintTop_toTopOf="@id/local_participant_background"
app:layout_constraintWidth_max="@dimen/voip_contact_avatar_max_size" />
<org.linphone.activities.voip.views.RoundCornersTextureView
android:id="@+id/local_preview_video_surface"
android:layout_width="0dp"
android:layout_height="0dp"
app:alignTopRight="false"
app:displayMode="occupy_all_space"
android:visibility="@{conferenceViewModel.meParticipant.isInConference &amp;&amp; controlsViewModel.isSendingVideo ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toBottomOf="@id/local_participant_background"
app:layout_constraintEnd_toEndOf="@id/local_participant_background"
app:layout_constraintStart_toStartOf="@id/local_participant_background"
app:layout_constraintTop_toTopOf="@id/local_participant_background" />
<TextView
android:id="@+id/local_participant_name"
style="@style/call_remote_name_font"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:layout_marginBottom="5dp"
android:text="@{conferenceViewModel.meParticipant.contact.name ?? conferenceViewModel.meParticipant.displayName}"
app:layout_constraintBottom_toBottomOf="@id/local_participant_background"
app:layout_constraintStart_toStartOf="@id/local_participant_background"
app:layout_constraintEnd_toEndOf="@id/local_participant_background"/>
<ImageView
android:id="@+id/switch_camera"
android:layout_width="@dimen/conference_miniature_switch_camera_icon_size"
android:layout_height="@dimen/conference_miniature_switch_camera_icon_size"
android:layout_margin="5dp"
android:contentDescription="@string/content_description_switch_camera"
android:onClick="@{() -> controlsViewModel.switchCamera()}"
android:src="@drawable/icon_call_camera_switch"
android:visibility="@{controlsViewModel.isSwitchCameraAvailable &amp;&amp; !controlsViewModel.pipMode &amp;&amp; !controlsViewModel.fullScreenMode ? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="@id/local_participant_background"
app:layout_constraintTop_toTopOf="@id/local_participant_background" />
<ImageView
android:id="@+id/local_participant_speaking_border"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@null"
android:src="@drawable/shape_conference_active_speaker_border"
android:visibility="@{conferenceViewModel.meParticipant.isSpeaking ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toBottomOf="@id/local_participant_background"
app:layout_constraintLeft_toLeftOf="@id/local_participant_background"
app:layout_constraintRight_toRightOf="@id/local_participant_background"
app:layout_constraintTop_toTopOf="@id/local_participant_background" />
<ImageView
android:id="@+id/local_participant_muted"
android:layout_width="@dimen/voip_conference_participant_mic_muted_icon_size_active_speaker"
android:layout_height="@dimen/voip_conference_participant_mic_muted_icon_size_active_speaker"
android:layout_margin="5dp"
android:background="@drawable/shape_toggle_pressed_background"
android:contentDescription="@string/content_description_conference_participant_mic_muted"
android:padding="2dp"
android:src="@drawable/icon_mic_muted"
android:visibility="@{conferenceViewModel.meParticipant.isMuted ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintLeft_toLeftOf="@id/local_participant_background"
app:layout_constraintTop_toTopOf="@id/local_participant_background" />
<ImageView
android:id="@+id/local_participant_paused"
android:layout_width="@dimen/voip_conference_active_speaker_miniature_avatar_size"
android:layout_height="@dimen/voip_conference_active_speaker_miniature_avatar_size"
android:background="@drawable/shape_button_background"
android:contentDescription="@string/content_description_participant_is_paused"
android:src="@drawable/icon_pause"
android:visibility="@{conferenceViewModel.conferenceCreationPending || conferenceViewModel.meParticipant.isInConference || conferenceViewModel.meParticipant.isJoining ? View.GONE : View.VISIBLE, default=gone}"
app:layout_constraintBottom_toBottomOf="@id/local_participant_background"
app:layout_constraintEnd_toEndOf="@id/local_participant_background"
app:layout_constraintStart_toStartOf="@id/local_participant_background"
app:layout_constraintTop_toTopOf="@id/local_participant_background" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</layout> </layout>

View file

@ -58,8 +58,8 @@
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<ImageView <ImageView
android:layout_width="25dp" android:layout_width="@dimen/conference_miniature_switch_camera_icon_size"
android:layout_height="25dp" android:layout_height="@dimen/conference_miniature_switch_camera_icon_size"
android:layout_margin="5dp" android:layout_margin="5dp"
android:contentDescription="@string/content_description_switch_camera" android:contentDescription="@string/content_description_switch_camera"
android:onClick="@{() -> data.switchCamera()}" android:onClick="@{() -> data.switchCamera()}"

View file

@ -198,7 +198,7 @@
android:onClick="@{() -> callsViewModel.takeSnapshot()}" android:onClick="@{() -> callsViewModel.takeSnapshot()}"
android:enabled="@{!callsViewModel.currentCallData.isPaused &amp;&amp; !callsViewModel.currentCallData.isRemotelyPaused}" android:enabled="@{!callsViewModel.currentCallData.isPaused &amp;&amp; !callsViewModel.currentCallData.isRemotelyPaused}"
android:src="@drawable/icon_call_screenshot" android:src="@drawable/icon_call_screenshot"
android:visibility="@{!controlsViewModel.showTakeSnaptshotButton || controlsViewModel.fullScreenMode || controlsViewModel.pipMode ? View.GONE : View.VISIBLE}" android:visibility="@{!controlsViewModel.showTakeSnapshotButton || controlsViewModel.fullScreenMode || controlsViewModel.pipMode ? View.GONE : View.VISIBLE}"
app:layout_constraintStart_toStartOf="@id/background" app:layout_constraintStart_toStartOf="@id/background"
app:layout_constraintTop_toBottomOf="@id/record_call" /> app:layout_constraintTop_toBottomOf="@id/record_call" />

View file

@ -78,4 +78,5 @@
<dimen name="chat_message_content_preview_max_width">120dp</dimen> <dimen name="chat_message_content_preview_max_width">120dp</dimen>
<dimen name="voip_conference_active_speaker_dots_margin">5dp</dimen> <dimen name="voip_conference_active_speaker_dots_margin">5dp</dimen>
<dimen name="conference_waiting_room_buttons_max_width">250dp</dimen> <dimen name="conference_waiting_room_buttons_max_width">250dp</dimen>
<dimen name="conference_miniature_switch_camera_icon_size">25dp</dimen>
</resources> </resources>