From b803ae9a6105f308f05ada5c21600e091f5cb50d Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Thu, 16 Jul 2020 11:15:19 +0200 Subject: [PATCH] Changes to reflect new conference API Should fix various conference UI issues More UI fixes related to conference Fixes & improvements Fixed remove participant button invisible in dark mode API changes Handle conference enter and leave Fixed conference UI issues Small UI improvements Improve logging Improved paused conference cell Use isMe if participant added or removed is focus Update method names after changes to conference API --- app/build.gradle | 8 +- .../call/ProximitySensorActivity.kt | 8 +- .../call/fragments/ControlsFragment.kt | 5 + .../call/viewmodels/CallsViewModel.kt | 94 ++--------- .../ConferenceParticipantViewModel.kt | 47 ++++++ .../call/viewmodels/ConferenceViewModel.kt | 156 ++++++++++++++++++ .../call/viewmodels/ControlsViewModel.kt | 6 +- .../viewmodels/StatisticsListViewModel.kt | 30 ++-- .../call/viewmodels/StatusViewModel.kt | 2 +- ...llView.kt => ConferenceParticipantView.kt} | 20 ++- .../java/org/linphone/core/CoreContext.kt | 2 +- app/src/main/res/drawable/conference.xml | 7 + .../conference_remove_participant.xml | 8 + .../layout-land/call_controls_fragment.xml | 118 ++----------- app/src/main/res/layout/call_conference.xml | 108 ++++++------ .../main/res/layout/call_conference_cell.xml | 4 +- .../layout/call_conference_participant.xml | 80 +++++++++ .../res/layout/call_controls_fragment.xml | 120 ++------------ app/src/main/res/layout/conference_paused.xml | 54 ++++++ 19 files changed, 506 insertions(+), 371 deletions(-) create mode 100644 app/src/main/java/org/linphone/activities/call/viewmodels/ConferenceParticipantViewModel.kt create mode 100644 app/src/main/java/org/linphone/activities/call/viewmodels/ConferenceViewModel.kt rename app/src/main/java/org/linphone/activities/call/views/{ConferenceCallView.kt => ConferenceParticipantView.kt} (67%) create mode 100644 app/src/main/res/drawable/conference.xml create mode 100644 app/src/main/res/drawable/conference_remove_participant.xml create mode 100644 app/src/main/res/layout/call_conference_participant.xml create mode 100644 app/src/main/res/layout/conference_paused.xml 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