Added broadcast mode for scheduled conferences
This commit is contained in:
parent
2334a0e6a8
commit
4b27d1180a
29 changed files with 660 additions and 50 deletions
|
@ -63,6 +63,8 @@ class ChatMessageContentData(
|
|||
val isConferenceSchedule = MutableLiveData<Boolean>()
|
||||
val isConferenceUpdated = MutableLiveData<Boolean>()
|
||||
val isConferenceCancelled = MutableLiveData<Boolean>()
|
||||
val isBroadcast = MutableLiveData<Boolean>()
|
||||
val isSpeaker = MutableLiveData<Boolean>()
|
||||
|
||||
val fileName = MutableLiveData<String>()
|
||||
val filePath = MutableLiveData<String>()
|
||||
|
@ -378,14 +380,26 @@ class ChatMessageContentData(
|
|||
var participantsCount = conferenceInfo.participants.size
|
||||
val organizer = conferenceInfo.organizer
|
||||
var organizerFound = false
|
||||
var allSpeaker = true
|
||||
isSpeaker.value = true
|
||||
for (info in conferenceInfo.participantInfos) {
|
||||
val participant = info.address
|
||||
if (participant.weakEqual(chatMessage.chatRoom.localAddress)) {
|
||||
isSpeaker.value = info.role == Participant.Role.Speaker
|
||||
}
|
||||
|
||||
if (info.role == Participant.Role.Listener) {
|
||||
allSpeaker = false
|
||||
}
|
||||
|
||||
if (organizer != null) {
|
||||
for (participant in conferenceInfo.participants) {
|
||||
if (participant.weakEqual(organizer)) {
|
||||
organizerFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
isBroadcast.value = allSpeaker == false
|
||||
|
||||
if (!organizerFound) participantsCount += 1 // +1 for organizer
|
||||
conferenceParticipantCount.value = String.format(
|
||||
AppUtils.getString(R.string.conference_invite_participants_count),
|
||||
|
|
|
@ -19,15 +19,35 @@
|
|||
*/
|
||||
package org.linphone.activities.main.conference.data
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import org.linphone.contact.GenericContactData
|
||||
import org.linphone.core.Address
|
||||
import org.linphone.utils.LinphoneUtils
|
||||
|
||||
class ConferenceSchedulingParticipantData(
|
||||
private val sipAddress: Address,
|
||||
val sipAddress: Address,
|
||||
val showLimeBadge: Boolean = false,
|
||||
val showDivider: Boolean = true
|
||||
val showDivider: Boolean = true,
|
||||
val showBroadcastControls: Boolean = false,
|
||||
val speaker: Boolean = false,
|
||||
private val onAddedToSpeakers: ((data: ConferenceSchedulingParticipantData) -> Unit)? = null,
|
||||
private val onRemovedFromSpeakers: ((data: ConferenceSchedulingParticipantData) -> Unit)? = null
|
||||
) :
|
||||
GenericContactData(sipAddress) {
|
||||
val isSpeaker = MutableLiveData<Boolean>()
|
||||
|
||||
val sipUri: String get() = LinphoneUtils.getDisplayableAddress(sipAddress)
|
||||
|
||||
init {
|
||||
isSpeaker.value = speaker
|
||||
}
|
||||
|
||||
fun changeIsSpeaker() {
|
||||
isSpeaker.value = isSpeaker.value == false
|
||||
if (isSpeaker.value == true) {
|
||||
onAddedToSpeakers?.invoke(this)
|
||||
} else {
|
||||
onRemovedFromSpeakers?.invoke(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.linphone.LinphoneApplication.Companion.coreContext
|
|||
import org.linphone.R
|
||||
import org.linphone.core.ConferenceInfo
|
||||
import org.linphone.core.ConferenceInfo.State
|
||||
import org.linphone.core.Participant
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.utils.LinphoneUtils
|
||||
import org.linphone.utils.TimestampUtils
|
||||
|
@ -45,9 +46,12 @@ class ScheduledConferenceData(val conferenceInfo: ConferenceInfo, private val is
|
|||
val participantsExpanded = MutableLiveData<String>()
|
||||
val showDuration = MutableLiveData<Boolean>()
|
||||
val isConferenceCancelled = MutableLiveData<Boolean>()
|
||||
val isBroadcast = MutableLiveData<Boolean>()
|
||||
val speakersExpanded = MutableLiveData<String>()
|
||||
|
||||
init {
|
||||
expanded.value = false
|
||||
isBroadcast.value = false
|
||||
|
||||
address.value = conferenceInfo.uri?.asStringUriOnly()
|
||||
subject.value = conferenceInfo.subject
|
||||
|
@ -141,24 +145,48 @@ class ScheduledConferenceData(val conferenceInfo: ConferenceInfo, private val is
|
|||
private fun computeParticipantsLists() {
|
||||
var participantsListShort = ""
|
||||
var participantsListExpanded = ""
|
||||
var speakersListExpanded = ""
|
||||
|
||||
var allSpeaker = true
|
||||
for (info in conferenceInfo.participantInfos) {
|
||||
val participant = info.address
|
||||
Log.i(
|
||||
"[Scheduled Conference] Conference [${subject.value}] participant [${participant.asStringUriOnly()}] is a [${info.role}]"
|
||||
)
|
||||
|
||||
for (participant in conferenceInfo.participants) {
|
||||
val contact = coreContext.contactsManager.findContactByAddress(participant)
|
||||
val name = if (contact != null) {
|
||||
contact.name
|
||||
} else {
|
||||
LinphoneUtils.getDisplayName(
|
||||
participant
|
||||
)
|
||||
LinphoneUtils.getDisplayName(participant)
|
||||
}
|
||||
val address = participant.asStringUriOnly()
|
||||
participantsListShort += "$name, "
|
||||
when (info.role) {
|
||||
Participant.Role.Speaker -> {
|
||||
speakersListExpanded += "$name ($address)\n"
|
||||
}
|
||||
Participant.Role.Listener -> {
|
||||
participantsListExpanded += "$name ($address)\n"
|
||||
allSpeaker = false
|
||||
}
|
||||
else -> { // For meetings created before 5.3 SDK
|
||||
participantsListExpanded += "$name ($address)\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
participantsListShort = participantsListShort.dropLast(2)
|
||||
participantsListExpanded = participantsListExpanded.dropLast(1)
|
||||
speakersListExpanded = speakersListExpanded.dropLast(1)
|
||||
|
||||
participantsShort.value = participantsListShort
|
||||
participantsExpanded.value = participantsListExpanded
|
||||
speakersExpanded.value = speakersListExpanded
|
||||
|
||||
// If all participants have Speaker role then it is a meeting, else it is a broadcast
|
||||
isBroadcast.value = allSpeaker == false
|
||||
Log.i(
|
||||
"[Scheduled Conference] Conference [${subject.value}] is a ${if (allSpeaker) "meeting" else "broadcast"}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,9 @@ class ConferenceWaitingRoomFragment : GenericFragment<ConferenceWaitingRoomFragm
|
|||
val conferenceSubject = arguments?.getString("Subject")
|
||||
viewModel.subject.value = conferenceSubject
|
||||
|
||||
val address = arguments?.getString("Address")
|
||||
viewModel.findConferenceInfoByAddress(address)
|
||||
|
||||
viewModel.cancelConferenceJoiningEvent.observe(
|
||||
viewLifecycleOwner
|
||||
) {
|
||||
|
|
|
@ -23,6 +23,7 @@ import androidx.lifecycle.MediatorLiveData
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import java.util.*
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.LinphoneApplication.Companion.corePreferences
|
||||
import org.linphone.R
|
||||
import org.linphone.activities.main.conference.data.ConferenceSchedulingParticipantData
|
||||
import org.linphone.activities.main.conference.data.Duration
|
||||
|
@ -30,6 +31,7 @@ import org.linphone.activities.main.conference.data.TimeZoneData
|
|||
import org.linphone.contact.ContactsSelectionViewModel
|
||||
import org.linphone.core.*
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.utils.AppUtils
|
||||
import org.linphone.utils.Event
|
||||
import org.linphone.utils.LinphoneUtils
|
||||
import org.linphone.utils.TimestampUtils
|
||||
|
@ -41,6 +43,10 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
|
|||
val scheduleForLater = MutableLiveData<Boolean>()
|
||||
val isUpdate = MutableLiveData<Boolean>()
|
||||
|
||||
val isBroadcastAllowed = MutableLiveData<Boolean>()
|
||||
val mode = MutableLiveData<String>()
|
||||
val modesList: List<String>
|
||||
|
||||
val formattedDate = MutableLiveData<String>()
|
||||
val formattedTime = MutableLiveData<String>()
|
||||
|
||||
|
@ -50,6 +56,7 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
|
|||
val sendInviteViaEmail = MutableLiveData<Boolean>()
|
||||
|
||||
val participantsData = MutableLiveData<List<ConferenceSchedulingParticipantData>>()
|
||||
val speakersData = MutableLiveData<List<ConferenceSchedulingParticipantData>>()
|
||||
|
||||
val address = MutableLiveData<Address>()
|
||||
|
||||
|
@ -74,6 +81,8 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
|
|||
private var confInfo: ConferenceInfo? = null
|
||||
private val conferenceScheduler = coreContext.core.createConferenceScheduler()
|
||||
|
||||
private val selectedSpeakersAddresses = MutableLiveData<ArrayList<Address>>()
|
||||
|
||||
private val listener = object : ConferenceSchedulerListenerStub() {
|
||||
override fun onStateChanged(
|
||||
conferenceScheduler: ConferenceScheduler,
|
||||
|
@ -154,6 +163,13 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
|
|||
scheduleForLater.value = false
|
||||
isUpdate.value = false
|
||||
|
||||
isBroadcastAllowed.value = !corePreferences.disableBroadcastConference
|
||||
modesList = arrayListOf(
|
||||
AppUtils.getString(R.string.conference_schedule_mode_meeting),
|
||||
AppUtils.getString(R.string.conference_schedule_mode_broadcast)
|
||||
)
|
||||
mode.value = modesList.first() // Meeting by default
|
||||
|
||||
isEncrypted.value = false
|
||||
sendInviteViaChat.value = true
|
||||
sendInviteViaEmail.value = false
|
||||
|
@ -185,6 +201,7 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
|
|||
override fun onCleared() {
|
||||
conferenceScheduler.removeListener(listener)
|
||||
participantsData.value.orEmpty().forEach(ConferenceSchedulingParticipantData::destroy)
|
||||
speakersData.value.orEmpty().forEach(ConferenceSchedulingParticipantData::destroy)
|
||||
|
||||
super.onCleared()
|
||||
}
|
||||
|
@ -195,6 +212,7 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
|
|||
}
|
||||
|
||||
fun populateFromConferenceInfo(conferenceInfo: ConferenceInfo) {
|
||||
// Pre-set data from existing conference info, used when editing an already scheduled broadcast or meeting
|
||||
confInfo = conferenceInfo
|
||||
|
||||
address.value = conferenceInfo.uri
|
||||
|
@ -213,10 +231,26 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
|
|||
scheduleForLater.value = conferenceDuration > 0
|
||||
|
||||
val participantsList = arrayListOf<Address>()
|
||||
for (participant in conferenceInfo.participants) {
|
||||
val speakersList = arrayListOf<Address>()
|
||||
for (info in conferenceInfo.participantInfos) {
|
||||
val participant = info.address
|
||||
participantsList.add(participant)
|
||||
if (info.role == Participant.Role.Speaker) {
|
||||
speakersList.add(participant)
|
||||
}
|
||||
}
|
||||
if (participantsList.count() == speakersList.count()) {
|
||||
// All participants are speaker, this is a meeting, clear speakers
|
||||
Log.i("[Conference Creation] Conference info is a meeting")
|
||||
speakersList.clear()
|
||||
mode.value = modesList.first()
|
||||
} else {
|
||||
Log.i("[Conference Creation] Conference info is a broadcast")
|
||||
mode.value = modesList.last()
|
||||
}
|
||||
selectedAddresses.value = participantsList
|
||||
selectedSpeakersAddresses.value = speakersList
|
||||
|
||||
computeParticipantsData()
|
||||
}
|
||||
|
||||
|
@ -241,14 +275,57 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
|
|||
|
||||
fun computeParticipantsData() {
|
||||
participantsData.value.orEmpty().forEach(ConferenceSchedulingParticipantData::destroy)
|
||||
val list = arrayListOf<ConferenceSchedulingParticipantData>()
|
||||
speakersData.value.orEmpty().forEach(ConferenceSchedulingParticipantData::destroy)
|
||||
|
||||
val participantsList = arrayListOf<ConferenceSchedulingParticipantData>()
|
||||
val speakersList = arrayListOf<ConferenceSchedulingParticipantData>()
|
||||
|
||||
for (address in selectedAddresses.value.orEmpty()) {
|
||||
val data = ConferenceSchedulingParticipantData(address, isEncrypted.value == true)
|
||||
list.add(data)
|
||||
val isSpeaker = address in selectedSpeakersAddresses.value.orEmpty()
|
||||
val data = ConferenceSchedulingParticipantData(
|
||||
address,
|
||||
showLimeBadge = isEncrypted.value == true,
|
||||
showBroadcastControls = isModeBroadcastCurrentlySelected(),
|
||||
speaker = isSpeaker,
|
||||
onAddedToSpeakers = { data ->
|
||||
Log.i(
|
||||
"[Conference Creation] Participant [${address.asStringUriOnly()}] added to speakers"
|
||||
)
|
||||
val participants = arrayListOf<ConferenceSchedulingParticipantData>()
|
||||
participants.addAll(participantsData.value.orEmpty())
|
||||
participants.remove(data)
|
||||
participantsData.value = participants
|
||||
|
||||
val speakers = arrayListOf<ConferenceSchedulingParticipantData>()
|
||||
speakers.addAll(speakersData.value.orEmpty())
|
||||
speakers.add(data)
|
||||
speakersData.value = speakers
|
||||
},
|
||||
onRemovedFromSpeakers = { data ->
|
||||
Log.i(
|
||||
"[Conference Creation] Participant [${address.asStringUriOnly()}] removed from speakers"
|
||||
)
|
||||
val speakers = arrayListOf<ConferenceSchedulingParticipantData>()
|
||||
speakers.addAll(speakersData.value.orEmpty())
|
||||
speakers.remove(data)
|
||||
speakersData.value = speakers
|
||||
|
||||
val participants = arrayListOf<ConferenceSchedulingParticipantData>()
|
||||
participants.addAll(participantsData.value.orEmpty())
|
||||
participants.add(data)
|
||||
participantsData.value = participants
|
||||
}
|
||||
)
|
||||
|
||||
if (isSpeaker) {
|
||||
speakersList.add(data)
|
||||
} else {
|
||||
participantsList.add(data)
|
||||
}
|
||||
}
|
||||
|
||||
participantsData.value = list
|
||||
participantsData.value = participantsList
|
||||
speakersData.value = speakersList
|
||||
}
|
||||
|
||||
fun createConference() {
|
||||
|
@ -260,8 +337,6 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
|
|||
|
||||
conferenceCreationInProgress.value = true
|
||||
val core = coreContext.core
|
||||
val participants = arrayOfNulls<Address>(selectedAddresses.value.orEmpty().size)
|
||||
selectedAddresses.value?.toArray(participants)
|
||||
val localAccount = core.defaultAccount
|
||||
val localAddress = localAccount?.params?.identityAddress
|
||||
|
||||
|
@ -273,7 +348,25 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
|
|||
conferenceInfo.organizer = localAddress
|
||||
conferenceInfo.subject = subject.value
|
||||
conferenceInfo.description = description.value
|
||||
conferenceInfo.setParticipants(participants)
|
||||
|
||||
val participants = arrayOfNulls<ParticipantInfo>(selectedAddresses.value.orEmpty().size)
|
||||
var index = 0
|
||||
val isBroadcast = isModeBroadcastCurrentlySelected()
|
||||
for (participant in participantsData.value.orEmpty()) {
|
||||
val info = Factory.instance().createParticipantInfo(participant.sipAddress)
|
||||
// For meetings, all participants must have Speaker role
|
||||
info?.role = if (isBroadcast) Participant.Role.Listener else Participant.Role.Speaker
|
||||
participants[index] = info
|
||||
index += 1
|
||||
}
|
||||
for (speaker in speakersData.value.orEmpty()) {
|
||||
val info = Factory.instance().createParticipantInfo(speaker.sipAddress)
|
||||
info?.role = Participant.Role.Speaker
|
||||
participants[index] = info
|
||||
index += 1
|
||||
}
|
||||
conferenceInfo.setParticipantInfos(participants)
|
||||
|
||||
if (scheduleForLater.value == true) {
|
||||
val startTime = getConferenceStartTimestamp()
|
||||
conferenceInfo.dateTime = startTime
|
||||
|
@ -287,6 +380,10 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
|
|||
conferenceScheduler.info = conferenceInfo
|
||||
}
|
||||
|
||||
fun isModeBroadcastCurrentlySelected(): Boolean {
|
||||
return mode.value == AppUtils.getString(R.string.conference_schedule_mode_broadcast)
|
||||
}
|
||||
|
||||
private fun computeTimeZonesList(): List<TimeZoneData> {
|
||||
return TimeZone.getAvailableIDs().map { id -> TimeZoneData(TimeZone.getTimeZone(id)) }.toList().sorted()
|
||||
}
|
||||
|
|
|
@ -61,6 +61,8 @@ class ConferenceWaitingRoomViewModel : MessageNotifierViewModel() {
|
|||
|
||||
val networkReachable = MutableLiveData<Boolean>()
|
||||
|
||||
val isConferenceBroadcastWithListenerRole = MutableLiveData<Boolean>()
|
||||
|
||||
val askPermissionEvent: MutableLiveData<Event<String>> by lazy {
|
||||
MutableLiveData<Event<String>>()
|
||||
}
|
||||
|
@ -198,7 +200,10 @@ class ConferenceWaitingRoomViewModel : MessageNotifierViewModel() {
|
|||
}
|
||||
|
||||
layoutMenuSelected.value = false
|
||||
updateLayout()
|
||||
when (core.defaultConferenceLayout) {
|
||||
Conference.Layout.Grid -> setMosaicLayout()
|
||||
else -> setActiveSpeakerLayout()
|
||||
}
|
||||
|
||||
if (AudioRouteUtils.isBluetoothAudioRouteAvailable()) {
|
||||
setBluetoothAudioRoute()
|
||||
|
@ -216,6 +221,44 @@ class ConferenceWaitingRoomViewModel : MessageNotifierViewModel() {
|
|||
super.onCleared()
|
||||
}
|
||||
|
||||
fun findConferenceInfoByAddress(stringAddress: String?) {
|
||||
if (stringAddress != null) {
|
||||
val address = Factory.instance().createAddress(stringAddress)
|
||||
if (address != null) {
|
||||
val conferenceInfo = coreContext.core.findConferenceInformationFromUri(address)
|
||||
if (conferenceInfo != null) {
|
||||
val myself = conferenceInfo.participantInfos.find {
|
||||
it.address.asStringUriOnly() == coreContext.core.defaultAccount?.params?.identityAddress?.asStringUriOnly()
|
||||
}
|
||||
if (myself != null) {
|
||||
Log.i(
|
||||
"[Conference Waiting Room] Found our participant, it's role is [${myself.role}]"
|
||||
)
|
||||
val areWeListener = myself.role == Participant.Role.Listener
|
||||
isConferenceBroadcastWithListenerRole.value = areWeListener
|
||||
|
||||
if (areWeListener) {
|
||||
callParams.isVideoEnabled = false
|
||||
callParams.videoDirection = MediaDirection.Inactive
|
||||
updateVideoState()
|
||||
updateLayout()
|
||||
}
|
||||
} else {
|
||||
Log.e(
|
||||
"[Conference Waiting Room] Failed to find ourselves in participants info"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Log.e(
|
||||
"[Conference Waiting Room] Failed to find conference info using address [$stringAddress]"
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.e("[Conference Waiting Room] Can't find conference info using null address!")
|
||||
}
|
||||
}
|
||||
|
||||
fun cancel() {
|
||||
cancelConferenceJoiningEvent.value = Event(true)
|
||||
}
|
||||
|
|
|
@ -35,8 +35,16 @@ class ConferencesSettingsViewModel : GenericSettingsViewModel() {
|
|||
val layoutLabels = MutableLiveData<ArrayList<String>>()
|
||||
private val layoutValues = arrayListOf<Int>()
|
||||
|
||||
val enableBroadcastListener = object : SettingListenerStub() {
|
||||
override fun onBoolValueChanged(newValue: Boolean) {
|
||||
prefs.disableBroadcastConference = !newValue
|
||||
}
|
||||
}
|
||||
val enableBroadcast = MutableLiveData<Boolean>()
|
||||
|
||||
init {
|
||||
initLayoutsList()
|
||||
enableBroadcast.value = !prefs.disableBroadcastConference
|
||||
}
|
||||
|
||||
private fun initLayoutsList() {
|
||||
|
|
|
@ -34,10 +34,13 @@ class ConferenceParticipantData(
|
|||
|
||||
val isAdmin = MutableLiveData<Boolean>()
|
||||
val isMeAdmin = MutableLiveData<Boolean>()
|
||||
val isSpeaker = MutableLiveData<Boolean>()
|
||||
|
||||
init {
|
||||
isAdmin.value = participant.isAdmin
|
||||
isMeAdmin.value = conference.me.isAdmin
|
||||
isSpeaker.value = participant.role == Participant.Role.Speaker
|
||||
|
||||
Log.i(
|
||||
"[Conference Participant] Participant ${participant.address.asStringUriOnly()} is ${if (participant.isAdmin) "admin" else "not admin"}"
|
||||
)
|
||||
|
|
|
@ -56,9 +56,13 @@ class ConferenceViewModel : ViewModel() {
|
|||
val twoOrMoreParticipants = MutableLiveData<Boolean>()
|
||||
val moreThanTwoParticipants = MutableLiveData<Boolean>()
|
||||
|
||||
val speakingParticipantFound = MutableLiveData<Boolean>()
|
||||
val speakingParticipant = MutableLiveData<ConferenceParticipantDeviceData>()
|
||||
val meParticipant = MutableLiveData<ConferenceParticipantDeviceData>()
|
||||
|
||||
val isBroadcast = MutableLiveData<Boolean>()
|
||||
val isMeListenerOnly = MutableLiveData<Boolean>()
|
||||
|
||||
val participantAdminStatusChangedEvent: MutableLiveData<Event<ConferenceParticipantData>> by lazy {
|
||||
MutableLiveData<Event<ConferenceParticipantData>>()
|
||||
}
|
||||
|
@ -207,6 +211,7 @@ class ConferenceViewModel : ViewModel() {
|
|||
speakingParticipant.value?.isActiveSpeaker?.value = false
|
||||
device.isActiveSpeaker.value = true
|
||||
speakingParticipant.value = device!!
|
||||
speakingParticipantFound.value = true
|
||||
} else if (device == null) {
|
||||
Log.w(
|
||||
"[Conference] Participant device [${participantDevice.address.asStringUriOnly()}] is the active speaker but couldn't find it in devices list"
|
||||
|
@ -528,6 +533,23 @@ class ConferenceViewModel : ViewModel() {
|
|||
|
||||
val activelySpeakingParticipantDevice = conference.activeSpeakerParticipantDevice
|
||||
var foundActivelySpeakingParticipantDevice = false
|
||||
speakingParticipantFound.value = false
|
||||
|
||||
val conferenceInfo = conference.core.findConferenceInformationFromUri(
|
||||
conference.conferenceAddress
|
||||
)
|
||||
var allSpeaker = true
|
||||
for (info in conferenceInfo?.participantInfos.orEmpty()) {
|
||||
if (info.role == Participant.Role.Listener) {
|
||||
allSpeaker = false
|
||||
}
|
||||
}
|
||||
isBroadcast.value = !allSpeaker
|
||||
if (!allSpeaker) {
|
||||
Log.i(
|
||||
"[Conference] Not all participants are speaker, considering it is a broadcast"
|
||||
)
|
||||
}
|
||||
|
||||
for (participant in participantsList) {
|
||||
val participantDevices = participant.devices
|
||||
|
@ -539,6 +561,18 @@ class ConferenceViewModel : ViewModel() {
|
|||
Log.i(
|
||||
"[Conference] Participant device found: ${device.name} (${device.address.asStringUriOnly()})"
|
||||
)
|
||||
|
||||
val info = conferenceInfo?.participantInfos?.find {
|
||||
it.address.weakEqual(participant.address)
|
||||
}
|
||||
if (info != null) {
|
||||
Log.i("[Conference] Participant role is [${info.role.name}]")
|
||||
val listener = info.role == Participant.Role.Listener || info.role == Participant.Role.Unknown
|
||||
if (listener) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
val deviceData = ConferenceParticipantDeviceData(device, false)
|
||||
devices.add(deviceData)
|
||||
|
||||
|
@ -549,6 +583,7 @@ class ConferenceViewModel : ViewModel() {
|
|||
speakingParticipant.value = deviceData
|
||||
deviceData.isActiveSpeaker.value = true
|
||||
foundActivelySpeakingParticipantDevice = true
|
||||
speakingParticipantFound.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -560,12 +595,26 @@ class ConferenceViewModel : ViewModel() {
|
|||
val deviceData = devices.first()
|
||||
speakingParticipant.value = deviceData
|
||||
deviceData.isActiveSpeaker.value = true
|
||||
speakingParticipantFound.value = false
|
||||
}
|
||||
|
||||
for (device in conference.me.devices) {
|
||||
Log.i(
|
||||
"[Conference] Participant device for myself found: ${device.name} (${device.address.asStringUriOnly()})"
|
||||
)
|
||||
|
||||
val info = conferenceInfo?.participantInfos?.find {
|
||||
it.address.weakEqual(device.address)
|
||||
}
|
||||
if (info != null) {
|
||||
Log.i("[Conference] Me role is [${info.role.name}]")
|
||||
val listener = info.role == Participant.Role.Listener || info.role == Participant.Role.Unknown
|
||||
isMeListenerOnly.value = listener
|
||||
if (listener) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
val deviceData = ConferenceParticipantDeviceData(device, true)
|
||||
devices.add(deviceData)
|
||||
meParticipant.value = deviceData
|
||||
|
@ -601,6 +650,7 @@ class ConferenceViewModel : ViewModel() {
|
|||
if (speakingParticipant.value == null) {
|
||||
speakingParticipant.value = deviceData
|
||||
deviceData.isActiveSpeaker.value = true
|
||||
speakingParticipantFound.value = false
|
||||
}
|
||||
|
||||
conferenceParticipantDevices.value = sortedDevices
|
||||
|
@ -641,6 +691,7 @@ class ConferenceViewModel : ViewModel() {
|
|||
val deviceData = devices[1]
|
||||
speakingParticipant.value = deviceData
|
||||
deviceData.isActiveSpeaker.value = true
|
||||
speakingParticipantFound.value = false
|
||||
}
|
||||
|
||||
conferenceParticipantDevices.value = devices
|
||||
|
|
|
@ -377,6 +377,12 @@ class CorePreferences constructor(private val context: Context) {
|
|||
config.setBool("app", "enter_video_conference_enable_full_screen_mode", value)
|
||||
}
|
||||
|
||||
var disableBroadcastConference: Boolean
|
||||
get() = config.getBool("app", "disable_broadcast_conference_feature", true)
|
||||
set(value) {
|
||||
config.setBool("app", "disable_broadcast_conference_feature", value)
|
||||
}
|
||||
|
||||
/* Assistant */
|
||||
|
||||
var firstStart: Boolean
|
||||
|
|
9
app/src/main/res/drawable/rect_orange_button.xml
Normal file
9
app/src/main/res/drawable/rect_orange_button.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_enabled="false"
|
||||
android:drawable="@drawable/shape_rect_orange_disabled_button" />
|
||||
<item android:state_pressed="true"
|
||||
android:drawable="@drawable/shape_rect_green_button" />
|
||||
<item
|
||||
android:drawable="@drawable/shape_rect_orange_button" />
|
||||
</selector>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
<corners android:radius="5dp" />
|
||||
<size android:height="20dp" android:width="20dp"/>
|
||||
<solid android:color="@color/primary_color_disabled"/>
|
||||
</shape>
|
|
@ -166,6 +166,7 @@
|
|||
android:id="@+id/active_speaker_background"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="@{conferenceViewModel.speakingParticipantFound ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintTop_toBottomOf="@id/top_barrier"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/miniatures"
|
||||
|
@ -179,6 +180,7 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:contentDescription="@null"
|
||||
android:visibility="@{conferenceViewModel.speakingParticipantFound ? View.VISIBLE : View.GONE}"
|
||||
coilVoipContact="@{conferenceViewModel.speakingParticipant}"
|
||||
android:background="@drawable/generated_avatar_bg"
|
||||
app:layout_constraintBottom_toBottomOf="@id/active_speaker_background"
|
||||
|
@ -196,7 +198,7 @@
|
|||
android:background="@drawable/shape_button_background"
|
||||
android:contentDescription="@string/content_description_participant_is_paused"
|
||||
android:src="@drawable/icon_pause"
|
||||
android:visibility="@{conferenceViewModel.speakingParticipant.isInConference ? View.GONE : View.VISIBLE, default=gone}"
|
||||
android:visibility="@{conferenceViewModel.speakingParticipantFound && conferenceViewModel.speakingParticipant.isInConference ? View.GONE : View.VISIBLE, default=gone}"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintBottom_toBottomOf="@id/active_speaker_background"
|
||||
app:layout_constraintEnd_toEndOf="@id/active_speaker_background"
|
||||
|
@ -253,6 +255,7 @@
|
|||
android:id="@+id/local_participant_background"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="@{conferenceViewModel.isMeListenerOnly ? View.GONE : View.VISIBLE}"
|
||||
app:layout_constraintTop_toBottomOf="@id/top_barrier"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
@ -267,6 +270,7 @@
|
|||
android:layout_height="0dp"
|
||||
android:layout_margin="10dp"
|
||||
android:contentDescription="@null"
|
||||
android:visibility="@{conferenceViewModel.isMeListenerOnly ? View.GONE : View.VISIBLE}"
|
||||
coilSelfAvatar="@{conferenceViewModel.meParticipant}"
|
||||
android:background="@drawable/generated_avatar_bg"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
|
@ -297,6 +301,7 @@
|
|||
android:layout_marginStart="5dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:visibility="@{conferenceViewModel.isMeListenerOnly ? View.GONE : View.VISIBLE}"
|
||||
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"
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/conference_invite_title_font"
|
||||
android:text="@string/conference_invite_title"
|
||||
android:text="@{data.isBroadcast ? @string/conference_broadcast_invite_title : @string/conference_invite_title, default=@string/conference_invite_title}"
|
||||
android:visibility="@{data.isConferenceUpdated || data.isConferenceCancelled ? View.GONE : View.VISIBLE}"/>
|
||||
|
||||
<TextView
|
||||
|
@ -67,6 +67,15 @@
|
|||
android:text="@{data.conferenceParticipantCount, default=`3 participants`}"
|
||||
app:drawableStartCompat="@drawable/icon_schedule_participants"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="23dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:visibility="@{data.isBroadcast && data.isSpeaker ? View.VISIBLE : View.GONE}"
|
||||
style="@style/conference_invite_dat_time_font"
|
||||
android:text="@string/conference_broadcast_invite_you_are_speaker"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginStart="5dp"
|
||||
|
@ -118,9 +118,24 @@
|
|||
android:visibility="@{data.isConferenceCancelled ? View.VISIBLE : View.GONE, default=gone}"
|
||||
android:text="@{data.canEdit ? @string/conference_scheduled_cancelled_by_me : @string/conference_scheduled_cancelled_by_organizer, default=@string/conference_scheduled_cancelled_by_organizer}"/>
|
||||
|
||||
<TextView
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/meeting_type"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
style="@style/conference_invite_subject_font"
|
||||
android:text="@{data.isBroadcast() ? @string/conference_broadcast_title : @string/conference_meeting_title, default=@string/conference_meeting_title}"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
|
@ -128,6 +143,8 @@
|
|||
android:maxLines="2"
|
||||
android:text="@{data.subject, default=`Lorem Ipsum`}"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:visibility="@{data.expanded ? View.GONE : View.VISIBLE}"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -182,24 +199,61 @@
|
|||
android:contentDescription="@string/content_description_toggle_conference_info_details"
|
||||
android:src="@drawable/button_conference_info" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/participants_icon"
|
||||
<TextView
|
||||
style="@style/conference_invite_desc_font"
|
||||
android:id="@+id/speakers_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:contentDescription="@string/content_description_conference_participants"
|
||||
android:text="@string/conference_invite_speakers_list_title"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/voip_dark_gray"
|
||||
android:drawableStart="@drawable/icon_schedule_participants"
|
||||
android:drawablePadding="5dp"
|
||||
android:visibility="@{data.isConferenceCancelled || !data.isBroadcast ? View.GONE : View.VISIBLE}"
|
||||
android:src="@drawable/icon_schedule_participants"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/speakers_list"
|
||||
style="@style/conference_invite_desc_font"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="23dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_toStartOf="@id/selected_info"
|
||||
android:layout_below="@id/speakers_title"
|
||||
android:background="@color/transparent_color"
|
||||
android:drawablePadding="5dp"
|
||||
android:visibility="@{data.isConferenceCancelled || !data.isBroadcast ? View.GONE : View.VISIBLE}"
|
||||
android:text="@{data.speakersExpanded, default=`Daryl Martin\nMartin Pecheur`}"
|
||||
tools:ignore="LabelFor" />
|
||||
|
||||
<TextView
|
||||
style="@style/conference_invite_desc_font"
|
||||
android:id="@+id/participants_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/speakers_list"
|
||||
android:layout_alignParentStart="true"
|
||||
android:text="@string/conference_invite_participants_list_title"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/voip_dark_gray"
|
||||
android:drawableStart="@drawable/icon_schedule_participants"
|
||||
android:drawablePadding="5dp"
|
||||
android:visibility="@{data.isConferenceCancelled ? View.GONE : View.VISIBLE}"
|
||||
android:src="@drawable/icon_schedule_participants"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/participants_list"
|
||||
style="@style/conference_invite_desc_font"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginStart="23dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_toStartOf="@id/selected_info"
|
||||
android:layout_toEndOf="@id/participants_icon"
|
||||
android:layout_below="@id/participants_title"
|
||||
android:background="@color/transparent_color"
|
||||
android:drawablePadding="5dp"
|
||||
android:visibility="@{data.isConferenceCancelled ? View.GONE : View.VISIBLE}"
|
||||
|
@ -241,7 +295,7 @@
|
|||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:visibility="@{data.isConferenceCancelled ? View.GONE : View.VISIBLE}"
|
||||
android:text="@string/conference_schedule_address_title"/>
|
||||
android:text="@{data.isBroadcast ? @string/conference_schedule_broadcast_address_title : @string/conference_schedule_address_title, default=@string/conference_schedule_address_title}"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -137,6 +137,31 @@
|
|||
android:layout_margin="10dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:orientation="vertical"
|
||||
android:visibility="@{viewModel.isBroadcastAllowed ? View.VISIBLE : View.GONE, default=gone}">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/conference_scheduling_font"
|
||||
android:text="@string/conference_schedule_mode"/>
|
||||
|
||||
<Spinner
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:layout_marginTop="5dp"
|
||||
style="@style/conference_scheduling_font"
|
||||
android:background="@drawable/icon_spinner_background"
|
||||
app:entries="@{viewModel.modesList}"
|
||||
app:selectedValue="@={viewModel.mode}"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/conference_scheduling_participant_cell_height"
|
||||
android:gravity="center_vertical">
|
||||
android:gravity="center_vertical"
|
||||
android:background="@color/white_color">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
@ -76,6 +77,17 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:onClick="@{() -> data.changeIsSpeaker()}"
|
||||
android:visibility="@{data.showBroadcastControls ? View.VISIBLE : View.GONE}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:src="@{data.isSpeaker ? @drawable/field_remove : @drawable/field_add, default=@drawable/field_add}"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="5dp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
style="@style/calls_list_header_font"
|
||||
android:text="@{viewModel.scheduleForLater ? @string/conference_schedule_summary : @string/conference_group_call_summary, default=@string/conference_group_call_summary}"/>
|
||||
android:text="@{viewModel.isModeBroadcastCurrentlySelected() ? @string/conference_schedule_broadcast_summary : viewModel.scheduleForLater ? @string/conference_schedule_summary : @string/conference_group_call_summary, default=@string/conference_group_call_summary}"/>
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
|
@ -268,14 +268,49 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
style="@style/conference_scheduling_font"
|
||||
android:visibility="@{viewModel.scheduleForLater && viewModel.sendInviteViaChat ? View.VISIBLE : View.GONE}"
|
||||
android:text="@string/conference_schedule_send_invite_chat_summary"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="@{viewModel.isModeBroadcastCurrentlySelected() ? View.VISIBLE : View.GONE}"
|
||||
android:background="?attr/voipFormDisabledFieldBackgroundColor">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
style="@style/calls_list_header_font"
|
||||
android:text="@string/conference_schedule_speakers_list"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="@{viewModel.isModeBroadcastCurrentlySelected() ? View.VISIBLE : View.GONE}"
|
||||
app:entries="@{viewModel.speakersData}"
|
||||
app:layout="@{@layout/conference_scheduling_participant_cell}" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
style="@style/calls_list_header_font"
|
||||
android:textColor="@color/light_grey_color"
|
||||
android:visibility="@{viewModel.isModeBroadcastCurrentlySelected() && viewModel.speakersData.size() == 0 ? View.VISIBLE : View.GONE}"
|
||||
android:text="@string/conference_schedule_speakers_list_empty"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/voipFormDisabledFieldBackgroundColor">
|
||||
|
||||
<TextView
|
||||
|
@ -288,6 +323,16 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
style="@style/calls_list_header_font"
|
||||
android:textColor="@color/light_grey_color"
|
||||
android:visibility="@{viewModel.participantsData.size() == 0 ? View.VISIBLE : View.GONE, default=gone}"
|
||||
android:text="@string/conference_schedule_participants_list_empty"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -310,10 +355,28 @@
|
|||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/shape_rect_orange_button"
|
||||
android:background="@drawable/rect_orange_button"
|
||||
android:visibility="@{viewModel.isModeBroadcastCurrentlySelected() ? View.GONE : View.VISIBLE}"
|
||||
android:text="@{viewModel.isUpdate ? @string/conference_schedule_edit : viewModel.scheduleForLater ? @string/conference_schedule_start : @string/conference_group_call_create, default=@string/conference_group_call_create}"
|
||||
style="@style/big_orange_button_font"/>
|
||||
|
||||
<TextView
|
||||
android:onClick="@{() -> viewModel.createConference()}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/rect_orange_button"
|
||||
android:enabled="@{viewModel.participantsData.size() != 0 && viewModel.speakersData.size() != 0}"
|
||||
android:visibility="@{viewModel.isModeBroadcastCurrentlySelected() ? View.VISIBLE : View.GONE, default=gone}"
|
||||
android:text="@{viewModel.isUpdate ? @string/conference_broadcast_edit : @string/conference_broadcast_start, default=@string/conference_broadcast_start}"
|
||||
style="@style/big_orange_button_font"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<include
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:text="@string/conference_waiting_room_video_disabled"
|
||||
android:text="@{viewModel.isConferenceBroadcastWithListenerRole ? @string/conference_waiting_room_listener_broadcast : @string/conference_waiting_room_video_disabled, default=@string/conference_waiting_room_video_disabled}"
|
||||
android:visibility="@{viewModel.isVideoEnabled ? View.GONE : View.VISIBLE}" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
@ -234,7 +234,7 @@
|
|||
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:enabled="@{!viewModel.joinInProgress}"
|
||||
android:enabled="@{!viewModel.isConferenceBroadcastWithListenerRole && !viewModel.joinInProgress}"
|
||||
android:onClick="@{() -> viewModel.toggleMuteMicrophone()}"
|
||||
android:padding="5dp"
|
||||
android:selected="@{viewModel.isMicrophoneMuted}"
|
||||
|
@ -284,7 +284,7 @@
|
|||
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.joinInProgress && viewModel.isVideoAvailable && !viewModel.isLowBandwidth}"
|
||||
android:enabled="@{!viewModel.isConferenceBroadcastWithListenerRole && !viewModel.joinInProgress && viewModel.isVideoAvailable && !viewModel.isLowBandwidth}"
|
||||
android:onClick="@{() -> viewModel.toggleVideo()}"
|
||||
android:padding="5dp"
|
||||
android:selected="@{viewModel.isVideoEnabled}"
|
||||
|
|
|
@ -240,7 +240,6 @@
|
|||
linphone:title="@{@string/account_setting_conference_factory_address}"
|
||||
linphone:listener="@{viewModel.conferenceFactoryUriListener}"
|
||||
linphone:defaultValue="@{viewModel.conferenceFactoryUri}"
|
||||
android:visibility="gone"
|
||||
linphone:inputType="@{InputType.TYPE_CLASS_TEXT}"/>
|
||||
|
||||
<include
|
||||
|
@ -248,7 +247,6 @@
|
|||
linphone:title="@{@string/account_setting_audio_video_conference_factory_address}"
|
||||
linphone:listener="@{viewModel.audioVideoConferenceFactoryUriListener}"
|
||||
linphone:defaultValue="@{viewModel.audioVideoConferenceFactoryUri}"
|
||||
android:visibility="gone"
|
||||
linphone:inputType="@{InputType.TYPE_CLASS_TEXT}"/>
|
||||
|
||||
<include
|
||||
|
@ -256,7 +254,6 @@
|
|||
linphone:title="@{@string/account_setting_end_to_end_encryption_keys_server_url}"
|
||||
linphone:listener="@{viewModel.limeServerUrlListener}"
|
||||
linphone:defaultValue="@{viewModel.limeServerUrl}"
|
||||
android:visibility="gone"
|
||||
linphone:inputType="@{InputType.TYPE_CLASS_TEXT}"/>
|
||||
|
||||
<include
|
||||
|
|
|
@ -72,6 +72,13 @@
|
|||
linphone:selectedIndex="@{viewModel.layoutIndex}"
|
||||
linphone:labels="@{viewModel.layoutLabels}"/>
|
||||
|
||||
<include
|
||||
layout="@layout/settings_widget_switch"
|
||||
linphone:title="@{@string/conferences_settings_enable_broadcast_title}"
|
||||
linphone:subtitle="@{@string/conferences_settings_enable_broadcast_summary}"
|
||||
linphone:listener="@{viewModel.enableBroadcastListener}"
|
||||
linphone:checked="@={viewModel.enableBroadcast}"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
|
|
@ -46,10 +46,10 @@
|
|||
android:layout_marginEnd="5dp"
|
||||
android:background="@drawable/button_background_reverse"
|
||||
android:contentDescription="@{callsViewModel.isMicrophoneMuted ? @string/content_description_disable_mic_mute : @string/content_description_enable_mic_mute}"
|
||||
android:enabled="@{callsViewModel.isMuteMicrophoneEnabled}"
|
||||
android:enabled="@{callsViewModel.isMuteMicrophoneEnabled && !conferenceViewModel.isMeListenerOnly}"
|
||||
android:onClick="@{() -> callsViewModel.toggleMuteMicrophone()}"
|
||||
android:padding="5dp"
|
||||
android:selected="@{callsViewModel.isMicrophoneMuted}"
|
||||
android:selected="@{callsViewModel.isMicrophoneMuted || conferenceViewModel.isMeListenerOnly}"
|
||||
android:src="@drawable/icon_toggle_mic"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="W,1:1"
|
||||
|
@ -96,10 +96,10 @@
|
|||
android:layout_marginStart="5dp"
|
||||
android:background="@drawable/button_background_reverse"
|
||||
android:contentDescription="@{controlsViewModel.isVideoEnabled && controlsViewModel.isSendingVideo ? @string/content_description_disable_video : @string/content_description_enable_video}"
|
||||
android:enabled="@{controlsViewModel.isVideoAvailable && !controlsViewModel.isVideoUpdateInProgress}"
|
||||
android:enabled="@{controlsViewModel.isVideoAvailable && !controlsViewModel.isVideoUpdateInProgress && !conferenceViewModel.isMeListenerOnly}"
|
||||
android:onClick="@{() -> (!conferenceViewModel.conferenceExists || conferenceViewModel.conferenceDisplayMode != ConferenceDisplayMode.AUDIO_ONLY) ? controlsViewModel.toggleVideo() : conferenceViewModel.switchLayoutFromAudioOnlyToActiveSpeaker()}"
|
||||
android:padding="5dp"
|
||||
android:selected="@{controlsViewModel.isVideoEnabled && controlsViewModel.isSendingVideo}"
|
||||
android:selected="@{controlsViewModel.isVideoEnabled && controlsViewModel.isSendingVideo && !conferenceViewModel.isMeListenerOnly}"
|
||||
android:visibility="@{controlsViewModel.hideVideo ? View.GONE : View.VISIBLE}"
|
||||
android:src="@drawable/icon_toggle_camera"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
android:gravity="center"
|
||||
android:onClick="@{() -> controlsViewModel.showNumpad()}"
|
||||
android:text="@string/call_action_numpad"
|
||||
android:enabled="@{!conferenceViewModel.isBroadcast}"
|
||||
app:drawableTopCompat="@drawable/icon_call_numpad"
|
||||
app:layout_constraintBottom_toBottomOf="@id/chat"
|
||||
app:layout_constraintEnd_toStartOf="@id/call_stats"
|
||||
|
@ -175,6 +176,7 @@
|
|||
android:gravity="center"
|
||||
android:onClick="@{() -> controlsViewModel.goToCallsList()}"
|
||||
android:text="@string/call_action_calls_list"
|
||||
android:enabled="@{!conferenceViewModel.isBroadcast}"
|
||||
app:drawableTopCompat="@drawable/icon_calls_list"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
|
@ -125,6 +125,7 @@
|
|||
android:id="@+id/active_speaker_background"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="@{conferenceViewModel.speakingParticipantFound ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintTop_toBottomOf="@id/top_barrier"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
@ -139,6 +140,7 @@
|
|||
android:layout_height="0dp"
|
||||
android:layout_margin="10dp"
|
||||
android:contentDescription="@null"
|
||||
android:visibility="@{conferenceViewModel.speakingParticipantFound ? View.VISIBLE : View.GONE}"
|
||||
coilVoipContact="@{conferenceViewModel.speakingParticipant}"
|
||||
android:background="@drawable/generated_avatar_bg"
|
||||
app:layout_constraintBottom_toBottomOf="@id/active_speaker_background"
|
||||
|
@ -157,7 +159,7 @@
|
|||
android:background="@drawable/shape_button_background"
|
||||
android:contentDescription="@string/content_description_participant_is_paused"
|
||||
android:src="@drawable/icon_pause"
|
||||
android:visibility="@{conferenceViewModel.speakingParticipant.isInConference ? View.GONE : View.VISIBLE, default=gone}"
|
||||
android:visibility="@{conferenceViewModel.speakingParticipantFound && conferenceViewModel.speakingParticipant.isInConference ? View.GONE : View.VISIBLE, default=gone}"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintBottom_toBottomOf="@id/active_speaker_background"
|
||||
app:layout_constraintEnd_toEndOf="@id/active_speaker_background"
|
||||
|
@ -254,6 +256,7 @@
|
|||
android:id="@+id/local_participant_background"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="@{conferenceViewModel.isMeListenerOnly ? View.GONE : View.VISIBLE}"
|
||||
app:layout_constraintTop_toBottomOf="@id/top_barrier"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
@ -268,6 +271,7 @@
|
|||
android:layout_height="0dp"
|
||||
android:layout_margin="10dp"
|
||||
android:contentDescription="@null"
|
||||
android:visibility="@{conferenceViewModel.isMeListenerOnly ? View.GONE : View.VISIBLE}"
|
||||
coilSelfAvatar="@{conferenceViewModel.meParticipant}"
|
||||
android:background="@drawable/generated_avatar_bg"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
|
@ -298,6 +302,7 @@
|
|||
android:layout_marginStart="5dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:visibility="@{conferenceViewModel.isMeListenerOnly ? View.GONE : View.VISIBLE}"
|
||||
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"
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View"/>
|
||||
<variable
|
||||
name="data"
|
||||
type="org.linphone.activities.voip.data.ConferenceParticipantData" />
|
||||
</data>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_margin="5dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="@dimen/contact_avatar_size"
|
||||
android:layout_height="@dimen/contact_avatar_size"
|
||||
android:contentDescription="@null"
|
||||
coilContact="@{data}"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:background="@drawable/generated_avatar_bg"
|
||||
android:src="@drawable/voip_single_contact_avatar"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/delete"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerInParent="true"
|
||||
android:contentDescription="@string/content_description_remove_contact_from_chat_room"
|
||||
android:onClick="@{() -> data.removeParticipantFromConference()}"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:src="@drawable/icon_cancel_alt"
|
||||
android:visibility="@{data.isMeAdmin ? View.VISIBLE : View.INVISIBLE}" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/speakerLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toStartOf="@id/delete"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:visibility="@{data.isSpeaker ? View.VISIBLE : View.INVISIBLE}">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_gravity="center"
|
||||
android:adjustViewBounds="true"
|
||||
android:padding="5dp"
|
||||
android:scaleType="centerInside"
|
||||
android:contentDescription="@string/content_description_contact_is_speaker"
|
||||
android:src="@drawable/check_selected" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:text="@string/conference_participants_list_speaker_label"
|
||||
android:textColor="?attr/primaryTextColor" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_toStartOf="@id/speakerLayout"
|
||||
android:layout_toEndOf="@id/avatar"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.linphone.views.MarqueeTextView
|
||||
style="@style/contact_name_list_cell_font"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:text="@{data.contact.name ?? data.displayName, default=`Bilbo Baggins`}" />
|
||||
|
||||
<org.linphone.views.MarqueeTextView
|
||||
android:id="@+id/sipUri"
|
||||
style="@style/sip_uri_small_font"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:text="@{data.sipUri, default=`sip:bilbo.baggins@sip.linphone.org`}" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</layout>
|
|
@ -76,7 +76,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:entries="@{conferenceViewModel.conferenceParticipants}"
|
||||
app:layout="@{@layout/voip_conference_participant_cell}" />
|
||||
app:layout="@{conferenceViewModel.isBroadcast ? @layout/voip_conference_participant_broadcast_cell : @layout/voip_conference_participant_cell}" />
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
|
|
|
@ -799,4 +799,25 @@
|
|||
<string name="chat_message_many_reactions">%s réactions</string>
|
||||
<string name="conference_invitation_notification_short_desc">invitation à une conférence</string>
|
||||
<string name="chat_message_voice_recording_notification_short_desc">message vocal</string>
|
||||
<string name="conference_schedule_mode">Mode</string>
|
||||
<string name="conference_schedule_mode_meeting">Réunion</string>
|
||||
<string name="conference_schedule_mode_broadcast">Webinar</string>
|
||||
<string name="conference_schedule_broadcast_summary">Informations du webinar</string>
|
||||
<string name="conference_schedule_speakers_list">Liste des intervenants</string>
|
||||
<string name="conference_schedule_speakers_list_empty">Choisissez au moins un intervenant</string>
|
||||
<string name="conference_schedule_participants_list_empty">Choisissez au moins un participant</string>
|
||||
<string name="conference_broadcast_start">Planifier un webinar</string>
|
||||
<string name="conference_broadcast_edit">Modifier le webinar</string>
|
||||
<string name="conference_meeting_title">Réunion :</string>
|
||||
<string name="conference_broadcast_title">Webinar :</string>
|
||||
<string name="conference_invite_speakers_list_title">Intervenants</string>
|
||||
<string name="conference_invite_participants_list_title">Participants</string>
|
||||
<string name="conference_broadcast_invite_title">Invitation au webinar :</string>
|
||||
<string name="conference_broadcast_invite_you_are_speaker">(vous êtes un intervenant)</string>
|
||||
<string name="conference_schedule_broadcast_address_title">Adresse du webinar</string>
|
||||
<string name="conference_waiting_room_listener_broadcast">Vous n\'êtes pas un intervenant de ce webinar</string>
|
||||
<string name="conferences_settings_enable_broadcast_title">Autoriser les webinar</string>
|
||||
<string name="conferences_settings_enable_broadcast_summary">Fonctionalité encore en béta !</string>
|
||||
<string name="conference_participants_list_speaker_label">Intervenant</string>
|
||||
<string name="content_description_contact_is_speaker">Contact is a speaker</string>
|
||||
</resources>
|
|
@ -6,6 +6,7 @@
|
|||
<!-- You can use https://material.io/design/color/#tools-for-picking-colors to get dark color value from primary -->
|
||||
<color name="primary_dark_color">#e65000</color>
|
||||
<color name="primary_light_color">#ffab4d</color>
|
||||
<color name="primary_color_disabled">#4DFE5E00</color> <!-- Try to have a color that renders well on both light & dark theme -->
|
||||
<color name="notification_led_color">#ff8000</color>
|
||||
|
||||
<color name="black_color">#000000</color>
|
||||
|
|
|
@ -274,11 +274,15 @@
|
|||
<string name="conference_schedule_subject_hint">Meeting subject</string>
|
||||
<string name="conference_group_call_subject_hint">Group call subject</string>
|
||||
<string name="conference_schedule_address_title">Meeting address</string>
|
||||
<string name="conference_schedule_broadcast_address_title">Broadcast address</string>
|
||||
<string name="conference_schedule_description_title">Add a description</string>
|
||||
<string name="conference_schedule_description_hint">Description</string>
|
||||
<string name="conference_schedule_date">Date</string>
|
||||
<string name="conference_schedule_time">Time</string>
|
||||
<string name="conference_schedule_duration">Duration</string>
|
||||
<string name="conference_schedule_mode">Mode</string>
|
||||
<string name="conference_schedule_mode_meeting">Meeting</string>
|
||||
<string name="conference_schedule_mode_broadcast">Broadcast</string>
|
||||
<string name="conference_schedule_timezone">Timezone</string>
|
||||
<string name="conference_schedule_send_invite_chat">Send invite via &appName;</string>
|
||||
<string name="conference_schedule_send_updated_invite_chat">Send update via &appName;</string>
|
||||
|
@ -287,11 +291,17 @@
|
|||
<string name="conference_schedule_encryption">Would you like to encrypt the meeting?</string>
|
||||
<string name="conference_schedule_send_invite_chat_summary">Invite will be sent out from my &appName; account</string>
|
||||
<string name="conference_schedule_participants_list">Participants list</string>
|
||||
<string name="conference_schedule_speakers_list">Speakers list</string>
|
||||
<string name="conference_schedule_speakers_list_empty">Select at least one speaker</string>
|
||||
<string name="conference_schedule_participants_list_empty">Select at least one participant</string>
|
||||
<string name="conference_organizer">Organizer</string>
|
||||
<string name="conference_schedule_summary">Meeting info</string>
|
||||
<string name="conference_group_call_summary">Group call info</string>
|
||||
<string name="conference_schedule_broadcast_summary">Broadcast info</string>
|
||||
<string name="conference_schedule_start">Schedule meeting</string>
|
||||
<string name="conference_schedule_edit">Edit meeting</string>
|
||||
<string name="conference_broadcast_start">Schedule broadcast</string>
|
||||
<string name="conference_broadcast_edit">Edit broadcast</string>
|
||||
<string name="conference_group_call_create">Start group call</string>
|
||||
<string name="conference_schedule_address_copied_to_clipboard">Meeting address copied into clipboard</string>
|
||||
<string name="conference_schedule_info_not_sent_to_participant">Failed to send meeting info to a participant</string>
|
||||
|
@ -300,6 +310,8 @@
|
|||
<string name="conference_default_title">Remote group call</string>
|
||||
<string name="conference_local_title">Local group call</string>
|
||||
<string name="conference_invite_title">Meeting invite:</string>
|
||||
<string name="conference_broadcast_invite_title">Broadcast invite:</string>
|
||||
<string name="conference_broadcast_invite_you_are_speaker">(you are a speaker)</string>
|
||||
<string name="conference_update_title">Meeting has been updated:</string>
|
||||
<string name="conference_cancel_title">Meeting has been cancelled:</string>
|
||||
<string name="conference_description_title">Description</string>
|
||||
|
@ -311,6 +323,7 @@
|
|||
<string name="conference_waiting_room_start_call">Start</string>
|
||||
<string name="conference_waiting_room_cancel_call">Cancel</string>
|
||||
<string name="conference_waiting_room_video_disabled">Video is currently disabled</string>
|
||||
<string name="conference_waiting_room_listener_broadcast">You aren\'t a speaker in this broadcast</string>
|
||||
<string name="conference_scheduled">Meetings</string>
|
||||
<string name="conference_layout_too_many_participants_for_mosaic">You can\'t change group call layout as there is too many participants</string>
|
||||
<string name="conference_too_many_participants_for_mosaic_layout">There is too many participants for mosaic layout, switching to active speaker</string>
|
||||
|
@ -346,6 +359,11 @@
|
|||
</plurals>
|
||||
<string name="conference_scheduled_cancelled_by_organizer">Conference has been cancelled by organizer</string>
|
||||
<string name="conference_scheduled_cancelled_by_me">You have cancelled the conference</string>
|
||||
<string name="conference_meeting_title">Meeting:</string>
|
||||
<string name="conference_broadcast_title">Broadcast:</string>
|
||||
<string name="conference_invite_speakers_list_title">Speakers</string>
|
||||
<string name="conference_invite_participants_list_title">Participants</string>
|
||||
<string name="conference_participants_list_speaker_label">Speaker</string>
|
||||
|
||||
<!-- Call -->
|
||||
<string name="call_incoming_title">Incoming Call</string>
|
||||
|
@ -746,6 +764,8 @@
|
|||
|
||||
<!-- Conferences settings -->
|
||||
<string name="conferences_settings_layout_title">Default layout</string>
|
||||
<string name="conferences_settings_enable_broadcast_title">Allow broadcasts</string>
|
||||
<string name="conferences_settings_enable_broadcast_summary">Feature currently in beta!</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="notification_channel_service_id" translatable="false">linphone_notification_service_id</string>
|
||||
|
@ -851,6 +871,7 @@
|
|||
<string name="content_description_remove_contact_from_chat_room">Remove contact from conversation</string>
|
||||
<string name="content_description_contact_is_admin">Contact is an admin in this conversation</string>
|
||||
<string name="content_description_contact_is_not_admin">Contact isn\'t an admin in this conversation</string>
|
||||
<string name="content_description_contact_is_speaker">Contact is a speaker</string>
|
||||
<string name="content_description_ephemeral_chat_room">Messages are ephemeral in this conversation</string>
|
||||
<string name="content_description_muted_chat_room">Notifications are disabled for this conversation</string>
|
||||
<string name="content_description_contact_can_do_encryption">Contact can be invited in encrypted conversations</string>
|
||||
|
|
Loading…
Reference in a new issue