diff --git a/app/build.gradle b/app/build.gradle index 5e7251d80..44ae7d78b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -203,11 +203,17 @@ repositories { maven { name "local linphone-sdk maven repository" url file(LinphoneSdkBuildDir + '/maven_repository/') + content { + includeGroup "org.linphone" + } } maven { name "linphone.org maven repository" url "https://linphone.org/maven_repository" + content { + includeGroup "org.linphone" + } } } @@ -286,4 +292,4 @@ if (crashlyticsEnabled()) { assembleDebug.finalizedBy(uploadCrashlyticsSymbolFileDebug) packageDebugBundle.finalizedBy(uploadCrashlyticsSymbolFileDebug) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/linphone/activities/call/ProximitySensorActivity.kt b/app/src/main/java/org/linphone/activities/call/ProximitySensorActivity.kt index 3f4a6d2a7..212ddd87f 100644 --- a/app/src/main/java/org/linphone/activities/call/ProximitySensorActivity.kt +++ b/app/src/main/java/org/linphone/activities/call/ProximitySensorActivity.kt @@ -39,7 +39,7 @@ abstract class ProximitySensorActivity : GenericActivity() { override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) { } override fun onSensorChanged(event: SensorEvent) { - if (event.timestamp == 0L) return + if (event.timestamp == 0L || !proximitySensorEnabled) return if (isProximitySensorNearby(event)) { if (!proximityWakeLock.isHeld) { Log.i("[Proximity Sensor Activity] Acquiring proximity wake lock") @@ -89,6 +89,12 @@ abstract class ProximitySensorActivity : GenericActivity() { super.onPause() } + override fun onDestroy() { + enableProximitySensor(false) + + super.onDestroy() + } + protected fun enableProximitySensor(enable: Boolean) { if (!proximitySensorFound) { Log.w("[Proximity Sensor Activity] Couldn't find proximity sensor in this device, skipping") diff --git a/app/src/main/java/org/linphone/activities/call/fragments/ControlsFragment.kt b/app/src/main/java/org/linphone/activities/call/fragments/ControlsFragment.kt index dc6182a23..f46eb0a3c 100644 --- a/app/src/main/java/org/linphone/activities/call/fragments/ControlsFragment.kt +++ b/app/src/main/java/org/linphone/activities/call/fragments/ControlsFragment.kt @@ -35,6 +35,7 @@ import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.R import org.linphone.activities.GenericFragment import org.linphone.activities.call.viewmodels.CallsViewModel +import org.linphone.activities.call.viewmodels.ConferenceViewModel import org.linphone.activities.call.viewmodels.ControlsViewModel import org.linphone.activities.call.viewmodels.SharedCallViewModel import org.linphone.activities.main.MainActivity @@ -51,6 +52,7 @@ import org.linphone.utils.PermissionHelper class ControlsFragment : GenericFragment() { private lateinit var callsViewModel: CallsViewModel private lateinit var controlsViewModel: ControlsViewModel + private lateinit var conferenceViewModel: ConferenceViewModel private lateinit var sharedViewModel: SharedCallViewModel private var dialog: Dialog? = null @@ -75,6 +77,9 @@ class ControlsFragment : GenericFragment() { controlsViewModel = ViewModelProvider(this).get(ControlsViewModel::class.java) binding.controlsViewModel = controlsViewModel + conferenceViewModel = ViewModelProvider(this).get(ConferenceViewModel::class.java) + binding.conferenceViewModel = conferenceViewModel + callsViewModel.currentCallViewModel.observe(viewLifecycleOwner, { if (it != null) { binding.activeCallTimer.base = diff --git a/app/src/main/java/org/linphone/activities/call/viewmodels/CallsViewModel.kt b/app/src/main/java/org/linphone/activities/call/viewmodels/CallsViewModel.kt index c427043ab..1aac1e78e 100644 --- a/app/src/main/java/org/linphone/activities/call/viewmodels/CallsViewModel.kt +++ b/app/src/main/java/org/linphone/activities/call/viewmodels/CallsViewModel.kt @@ -22,9 +22,8 @@ package org.linphone.activities.call.viewmodels import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import org.linphone.LinphoneApplication.Companion.coreContext -import org.linphone.core.Call -import org.linphone.core.Core -import org.linphone.core.CoreListenerStub +import org.linphone.core.* +import org.linphone.core.tools.Log import org.linphone.utils.Event import org.linphone.utils.PermissionHelper @@ -35,10 +34,6 @@ class CallsViewModel : ViewModel() { val pausedCalls = MutableLiveData>() - val conferenceCalls = MutableLiveData>() - - val isConferencePaused = MutableLiveData() - val noMoreCallEvent: MutableLiveData> by lazy { MutableLiveData>() } @@ -52,14 +47,9 @@ class CallsViewModel : ViewModel() { } private val listener = object : CoreListenerStub() { - override fun onCallStateChanged( - core: Core, - call: Call, - state: Call.State, - message: String - ) { - callPausedByRemote.value = state == Call.State.PausedByRemote - isConferencePaused.value = !coreContext.core.isInConference + override fun onCallStateChanged(core: Core, call: Call, state: Call.State, message: String) { + Log.i("[Calls VM] Call state changed: $state") + callPausedByRemote.value = (state == Call.State.PausedByRemote) and (call.conference == null) val currentCall = core.currentCall if (currentCall == null) { @@ -71,14 +61,11 @@ class CallsViewModel : ViewModel() { if (state == Call.State.End || state == Call.State.Released || state == Call.State.Error) { if (core.callsNb == 0) { noMoreCallEvent.value = Event(true) - conferenceCalls.value = arrayListOf() } else { removeCallFromPausedListIfPresent(call) - removeCallFromConferenceIfPresent(call) } } else if (state == Call.State.Paused) { addCallToPausedList(call) - removeCallFromConferenceIfPresent(call) } else if (state == Call.State.Resuming) { removeCallFromPausedListIfPresent(call) } else if (call.state == Call.State.UpdatedByRemote) { @@ -91,16 +78,8 @@ class CallsViewModel : ViewModel() { call.deferUpdate() callUpdateEvent.value = Event(call) } - } else { - if (state == Call.State.StreamsRunning) { - callUpdateEvent.value = Event(call) - } - - if (call.conference != null) { - addCallToConferenceListIfNotAlreadyInIt(call) - } else { - removeCallFromConferenceIfPresent(call) - } + } else if (state == Call.State.StreamsRunning) { + callUpdateEvent.value = Event(call) } } } @@ -112,20 +91,14 @@ class CallsViewModel : ViewModel() { if (currentCall != null) { currentCallViewModel.value = CallViewModel(currentCall) } - callPausedByRemote.value = currentCall?.state == Call.State.PausedByRemote - isConferencePaused.value = !coreContext.core.isInConference - val conferenceList = arrayListOf() + callPausedByRemote.value = currentCall?.state == Call.State.PausedByRemote + for (call in coreContext.core.calls) { if (call.state == Call.State.Paused || call.state == Call.State.Pausing) { addCallToPausedList(call) - } else { - if (call.conference != null) { - conferenceList.add(CallViewModel(call)) - } } } - conferenceCalls.value = conferenceList } override fun onCleared() { @@ -138,20 +111,6 @@ class CallsViewModel : ViewModel() { coreContext.answerCallVideoUpdateRequest(call, accept) } - fun pauseConference() { - if (coreContext.core.isInConference) { - coreContext.core.leaveConference() - isConferencePaused.value = true - } - } - - fun resumeConference() { - if (!coreContext.core.isInConference) { - coreContext.core.enterConference() - isConferencePaused.value = false - } - } - fun takeScreenshot() { if (!PermissionHelper.get().hasWriteExternalStorage()) { askWriteExternalStoragePermissionEvent.value = Event(true) @@ -161,9 +120,17 @@ class CallsViewModel : ViewModel() { } private fun addCallToPausedList(call: Call) { + if (call.conference != null) return // Conference will be displayed as paused, no need to display the call as well + val list = arrayListOf() list.addAll(pausedCalls.value.orEmpty()) + for (pausedCallViewModel in list) { + if (pausedCallViewModel.call == call) { + return + } + } + val viewModel = CallViewModel(call) list.add(viewModel) pausedCalls.value = list @@ -182,31 +149,4 @@ class CallsViewModel : ViewModel() { pausedCalls.value = list } - - private fun addCallToConferenceListIfNotAlreadyInIt(call: Call) { - val list = arrayListOf() - list.addAll(conferenceCalls.value.orEmpty()) - - for (viewModel in list) { - if (viewModel.call == call) return - } - - val viewModel = CallViewModel(call) - list.add(viewModel) - conferenceCalls.value = list - } - - private fun removeCallFromConferenceIfPresent(call: Call) { - val list = arrayListOf() - list.addAll(conferenceCalls.value.orEmpty()) - - for (viewModel in list) { - if (viewModel.call == call) { - list.remove(viewModel) - break - } - } - - conferenceCalls.value = list - } } diff --git a/app/src/main/java/org/linphone/activities/call/viewmodels/ConferenceParticipantViewModel.kt b/app/src/main/java/org/linphone/activities/call/viewmodels/ConferenceParticipantViewModel.kt new file mode 100644 index 000000000..b18cee149 --- /dev/null +++ b/app/src/main/java/org/linphone/activities/call/viewmodels/ConferenceParticipantViewModel.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-android + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.linphone.activities.call.viewmodels + +import androidx.lifecycle.MutableLiveData +import org.linphone.contact.GenericContactViewModel +import org.linphone.core.Conference +import org.linphone.core.Participant +import org.linphone.core.tools.Log + +class ConferenceParticipantViewModel( + private val conference: Conference, + val participant: Participant +) : + GenericContactViewModel(participant.address) { + private val isAdmin = MutableLiveData() + val isMeAdmin = MutableLiveData() + + init { + isAdmin.value = participant.isAdmin + isMeAdmin.value = conference.me.isAdmin + Log.i("[Conference Participant VM] Participant ${participant.address.asStringUriOnly()} is ${if (participant.isAdmin) "admin" else "not admin"}") + Log.i("[Conference Participant VM] Me is ${if (conference.me.isAdmin) "admin" else "not admin"} and is ${if (conference.me.isFocus) "focus" else "not focus"}") + } + + fun removeFromConference() { + Log.i("[Conference Participant VM] Removing participant ${participant.address.asStringUriOnly()} from conference $conference") + conference.removeParticipant(participant) + } +} diff --git a/app/src/main/java/org/linphone/activities/call/viewmodels/ConferenceViewModel.kt b/app/src/main/java/org/linphone/activities/call/viewmodels/ConferenceViewModel.kt new file mode 100644 index 000000000..925c4a4be --- /dev/null +++ b/app/src/main/java/org/linphone/activities/call/viewmodels/ConferenceViewModel.kt @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-android + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.linphone.activities.call.viewmodels + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import org.linphone.LinphoneApplication.Companion.coreContext +import org.linphone.core.* +import org.linphone.core.tools.Log + +class ConferenceViewModel : ViewModel() { + val isConferencePaused = MutableLiveData() + + val isMeConferenceFocus = MutableLiveData() + + val conferenceAddress = MutableLiveData
() + + val conferenceParticipants = MutableLiveData>() + + val isInConference = MutableLiveData() + + private val conferenceListener = object : ConferenceListenerStub() { + override fun onParticipantAdded(conference: Conference, participant: Participant) { + if (conference.isMe(participant.address)) { + Log.i("[Conference VM] Entered conference") + isConferencePaused.value = false + } else { + Log.i("[Conference VM] Participant added") + updateParticipantsList(conference) + } + } + + override fun onParticipantRemoved(conference: Conference, participant: Participant) { + if (conference.isMe(participant.address)) { + Log.i("[Conference VM] Left conference") + isConferencePaused.value = true + } else { + Log.i("[Conference VM] Participant removed") + updateParticipantsList(conference) + } + } + + override fun onParticipantAdminStatusChanged( + conference: Conference, + participant: Participant + ) { + Log.i("[Conference VM] Participant admin status changed") + updateParticipantsList(conference) + } + } + + private val listener = object : CoreListenerStub() { + override fun onConferenceStateChanged( + core: Core, + conference: Conference, + state: Conference.State + ) { + Log.i("[Conference VM] Conference state changed: $state") + isConferencePaused.value = !conference.isIn + + if (state == Conference.State.Instantiated) { + conference.addListener(conferenceListener) + } else if (state == Conference.State.Created) { + updateParticipantsList(conference) + isMeConferenceFocus.value = conference.me.isFocus + conferenceAddress.value = conference.conferenceAddress + } else if (state == Conference.State.Terminated || state == Conference.State.TerminationFailed) { + isInConference.value = false + conference.removeListener(conferenceListener) + conferenceParticipants.value = arrayListOf() + } + } + } + + init { + coreContext.core.addListener(listener) + + isConferencePaused.value = coreContext.core.conference?.isIn != true + isMeConferenceFocus.value = false + conferenceParticipants.value = arrayListOf() + isInConference.value = false + + val conference = coreContext.core.conference + if (conference != null) { + conference.addListener(conferenceListener) + isMeConferenceFocus.value = conference.me.isFocus + updateParticipantsList(conference) + } + } + + override fun onCleared() { + coreContext.core.removeListener(listener) + + super.onCleared() + } + + fun pauseConference() { + val defaultProxyConfig = coreContext.core.defaultProxyConfig + val localAddress = defaultProxyConfig?.identityAddress + val participants = arrayOf
() + val remoteConference = coreContext.core.searchConference(null, localAddress, conferenceAddress.value, participants) + val localConference = coreContext.core.searchConference(null, conferenceAddress.value, conferenceAddress.value, participants) + val conference = remoteConference ?: localConference + + if (conference != null) { + Log.i("[Conference VM] Leaving conference with address ${conferenceAddress.value?.asStringUriOnly()} temporarily") + conference.leave() + } else { + Log.w("[Conference VM] Unable to find conference with address ${conferenceAddress.value?.asStringUriOnly()}") + } + } + + fun resumeConference() { + val defaultProxyConfig = coreContext.core.defaultProxyConfig + val localAddress = defaultProxyConfig?.identityAddress + val participants = arrayOf
() + val remoteConference = coreContext.core.searchConference(null, localAddress, conferenceAddress.value, participants) + val localConference = coreContext.core.searchConference(null, conferenceAddress.value, conferenceAddress.value, participants) + val conference = remoteConference ?: localConference + + if (conference != null) { + Log.i("[Conference VM] Entering again conference with address ${conferenceAddress.value?.asStringUriOnly()}") + conference.enter() + } else { + Log.w("[Conference VM] Unable to find conference with address ${conferenceAddress.value?.asStringUriOnly()}") + } + } + + private fun updateParticipantsList(conference: Conference) { + val participants = arrayListOf() + for (participant in conference.participantList) { + Log.i("[Conference VM] Participant found: ${participant.address.asStringUriOnly()}") + val viewModel = ConferenceParticipantViewModel(conference, participant) + participants.add(viewModel) + } + conferenceParticipants.value = participants + isInConference.value = participants.isNotEmpty() + } +} diff --git a/app/src/main/java/org/linphone/activities/call/viewmodels/ControlsViewModel.kt b/app/src/main/java/org/linphone/activities/call/viewmodels/ControlsViewModel.kt index ffd041fcf..b2ee1a071 100644 --- a/app/src/main/java/org/linphone/activities/call/viewmodels/ControlsViewModel.kt +++ b/app/src/main/java/org/linphone/activities/call/viewmodels/ControlsViewModel.kt @@ -285,8 +285,8 @@ class ControlsViewModel : ViewModel() { if (conference != null && core.isInConference) { val params = core.createConferenceParams() - val videoEnabled = conference.currentParams.videoEnabled() - params.enableVideo(!videoEnabled) + val videoEnabled = conference.currentParams.isVideoEnabled + params.isVideoEnabled = !videoEnabled Log.i("[Controls VM] Conference current param for video is $videoEnabled") conference.updateParams(params) } else if (currentCall != null) { @@ -362,7 +362,7 @@ class ControlsViewModel : ViewModel() { val currentCallVideoEnabled = core.currentCall?.currentParams?.videoEnabled() ?: false val params = core.createConferenceParams() - params.enableVideo(currentCallVideoEnabled) + params.isVideoEnabled = currentCallVideoEnabled Log.i("[Call] Setting videoEnabled to [$currentCallVideoEnabled] in conference params") val conference = core.conference ?: core.createConferenceWithParams(params) diff --git a/app/src/main/java/org/linphone/activities/call/viewmodels/StatisticsListViewModel.kt b/app/src/main/java/org/linphone/activities/call/viewmodels/StatisticsListViewModel.kt index 56f8f3f85..47bf460e7 100644 --- a/app/src/main/java/org/linphone/activities/call/viewmodels/StatisticsListViewModel.kt +++ b/app/src/main/java/org/linphone/activities/call/viewmodels/StatisticsListViewModel.kt @@ -37,16 +37,8 @@ class StatisticsListViewModel : ViewModel() { state: Call.State, message: String ) { - if (state == Call.State.End || state == Call.State.Error) { - val newList = arrayListOf() - for (stat in callStatsList.value.orEmpty()) { - if (stat.call != call) { - newList.add(stat) - } else { - stat.destroy() - } - } - callStatsList.value = newList + if (state == Call.State.End || state == Call.State.Error || state == Call.State.Connected) { + computeCallsList() } } } @@ -54,13 +46,7 @@ class StatisticsListViewModel : ViewModel() { init { coreContext.core.addListener(listener) - val list = arrayListOf() - for (call in coreContext.core.calls) { - if (call.state != Call.State.End && call.state != Call.State.Released && call.state != Call.State.Error) { - list.add(CallStatisticsData(call)) - } - } - callStatsList.value = list + computeCallsList() } override fun onCleared() { @@ -69,4 +55,14 @@ class StatisticsListViewModel : ViewModel() { super.onCleared() } + + private fun computeCallsList() { + val list = arrayListOf() + for (call in coreContext.core.calls) { + if (call.state != Call.State.End && call.state != Call.State.Released && call.state != Call.State.Error) { + list.add(CallStatisticsData(call)) + } + } + callStatsList.value = list + } } diff --git a/app/src/main/java/org/linphone/activities/call/viewmodels/StatusViewModel.kt b/app/src/main/java/org/linphone/activities/call/viewmodels/StatusViewModel.kt index 981eb3a3c..425872eeb 100644 --- a/app/src/main/java/org/linphone/activities/call/viewmodels/StatusViewModel.kt +++ b/app/src/main/java/org/linphone/activities/call/viewmodels/StatusViewModel.kt @@ -133,7 +133,7 @@ class StatusViewModel : StatusViewModel() { } private fun updateCallQualityIcon() { - val call = coreContext.core.currentCall + val call = coreContext.core.currentCall ?: coreContext.core.calls.firstOrNull() val quality = call?.currentQuality ?: 0f callQualityIcon.value = when { quality >= 4 -> R.drawable.call_quality_indicator_4 diff --git a/app/src/main/java/org/linphone/activities/call/views/ConferenceCallView.kt b/app/src/main/java/org/linphone/activities/call/views/ConferenceParticipantView.kt similarity index 67% rename from app/src/main/java/org/linphone/activities/call/views/ConferenceCallView.kt rename to app/src/main/java/org/linphone/activities/call/views/ConferenceParticipantView.kt index 35d709ffa..484288dbc 100644 --- a/app/src/main/java/org/linphone/activities/call/views/ConferenceCallView.kt +++ b/app/src/main/java/org/linphone/activities/call/views/ConferenceParticipantView.kt @@ -26,11 +26,12 @@ import android.view.LayoutInflater import android.widget.LinearLayout import androidx.databinding.DataBindingUtil import org.linphone.R -import org.linphone.activities.call.viewmodels.CallViewModel -import org.linphone.databinding.CallConferenceBinding +import org.linphone.activities.call.viewmodels.ConferenceParticipantViewModel +import org.linphone.core.tools.Log +import org.linphone.databinding.CallConferenceParticipantBinding -class ConferenceCallView : LinearLayout { - private lateinit var binding: CallConferenceBinding +class ConferenceParticipantView : LinearLayout { + private lateinit var binding: CallConferenceParticipantBinding constructor(context: Context) : super(context) { init(context) @@ -53,15 +54,18 @@ class ConferenceCallView : LinearLayout { fun init(context: Context) { binding = DataBindingUtil.inflate( - LayoutInflater.from(context), R.layout.call_conference, this, true + LayoutInflater.from(context), R.layout.call_conference_participant, this, true ) } - fun setViewModel(viewModel: CallViewModel) { + fun setViewModel(viewModel: ConferenceParticipantViewModel) { binding.viewModel = viewModel - binding.callTimer.base = - SystemClock.elapsedRealtime() - (1000 * viewModel.call.duration) // Linphone timestamps are in seconds + val currentTimeSecs = System.currentTimeMillis() + val participantTime = viewModel.participant.creationTime * 1000 // Linphone timestamps are in seconds + val diff = currentTimeSecs - participantTime + Log.i("[Conference Participant] Participant joined conference at $participantTime == ${diff / 1000} seconds ago.") + binding.callTimer.base = SystemClock.elapsedRealtime() - diff binding.callTimer.start() } } diff --git a/app/src/main/java/org/linphone/core/CoreContext.kt b/app/src/main/java/org/linphone/core/CoreContext.kt index d2f40d5c5..7b3a566d7 100644 --- a/app/src/main/java/org/linphone/core/CoreContext.kt +++ b/app/src/main/java/org/linphone/core/CoreContext.kt @@ -506,7 +506,7 @@ class CoreContext(val context: Context, coreConfig: Config) { fun isVideoCallOrConferenceActive(): Boolean { val conference = core.conference return if (conference != null && core.isInConference) { - conference.currentParams.videoEnabled() + conference.currentParams.isVideoEnabled() } else { core.currentCall?.currentParams?.videoEnabled() ?: false } diff --git a/app/src/main/res/drawable/conference.xml b/app/src/main/res/drawable/conference.xml new file mode 100644 index 000000000..25f309f2f --- /dev/null +++ b/app/src/main/res/drawable/conference.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/drawable/conference_remove_participant.xml b/app/src/main/res/drawable/conference_remove_participant.xml new file mode 100644 index 000000000..d8fcc664b --- /dev/null +++ b/app/src/main/res/drawable/conference_remove_participant.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/app/src/main/res/layout-land/call_controls_fragment.xml b/app/src/main/res/layout-land/call_controls_fragment.xml index 2cbea4bf1..2a5a57756 100644 --- a/app/src/main/res/layout-land/call_controls_fragment.xml +++ b/app/src/main/res/layout-land/call_controls_fragment.xml @@ -12,6 +12,9 @@ + - - - - - - - - - - - - - - - - - + app:controlsViewModel="@{controlsViewModel}" + app:conferenceViewModel="@{conferenceViewModel}" /> - - - - - - - - - + app:conferenceViewModel="@{conferenceViewModel}" /> - + name="controlsViewModel" + type="org.linphone.activities.call.viewmodels.ControlsViewModel" /> + - + android:layout_height="60dp"> - - - - - - - - - + android:layout_centerVertical="true" + android:paddingLeft="10dp" + android:textColor="?attr/primaryTextColor" + android:textSize="30sp" + android:text="@string/call_conference_title" /> + android:padding="10dp" + android:scaleType="fitCenter" + android:background="@drawable/round_button_background" + android:src="@drawable/camera_switch" /> - + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/call_conference_cell.xml b/app/src/main/res/layout/call_conference_cell.xml index faec22306..c47b3f0e5 100644 --- a/app/src/main/res/layout/call_conference_cell.xml +++ b/app/src/main/res/layout/call_conference_cell.xml @@ -5,10 +5,10 @@ + type="org.linphone.activities.call.viewmodels.ConferenceParticipantViewModel" /> - diff --git a/app/src/main/res/layout/call_conference_participant.xml b/app/src/main/res/layout/call_conference_participant.xml new file mode 100644 index 000000000..c8a331066 --- /dev/null +++ b/app/src/main/res/layout/call_conference_participant.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/call_controls_fragment.xml b/app/src/main/res/layout/call_controls_fragment.xml index 4e8c0ffe4..792b13874 100644 --- a/app/src/main/res/layout/call_controls_fragment.xml +++ b/app/src/main/res/layout/call_controls_fragment.xml @@ -11,6 +11,9 @@ + - - - - - - - - - - - - - - - - - + app:controlsViewModel="@{controlsViewModel}" + app:conferenceViewModel="@{conferenceViewModel}" /> - - - - - - - - - + app:conferenceViewModel="@{conferenceViewModel}" /> + + + + + + + + + + + + + + + + + + \ No newline at end of file