From 862c5e47c8cfe271f67e372294d672d4c27712d5 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Thu, 3 Mar 2022 15:58:19 +0100 Subject: [PATCH] Split ActiveCallOrConferenceFragment into two fragments --- .../org/linphone/activities/Navigation.kt | 79 ++++-- .../linphone/activities/voip/CallActivity.kt | 94 ++++--- .../linphone/activities/voip/data/CallData.kt | 17 +- ...eFragment.kt => ConferenceCallFragment.kt} | 120 +++------ .../voip/fragments/SingleCallFragment.kt | 250 ++++++++++++++++++ .../voip/viewmodels/ControlsViewModel.kt | 15 +- app/src/main/res/layout/voip_activity.xml | 2 +- ....xml => voip_conference_call_fragment.xml} | 48 +--- ...cipant_remote_active_speaker_miniature.xml | 2 +- ...oip_conference_participant_remote_grid.xml | 2 +- .../res/layout/voip_single_call_fragment.xml | 149 +++++++++++ .../main/res/navigation/call_nav_graph.xml | 45 +++- 12 files changed, 599 insertions(+), 224 deletions(-) rename app/src/main/java/org/linphone/activities/voip/fragments/{ActiveCallOrConferenceFragment.kt => ConferenceCallFragment.kt} (76%) create mode 100644 app/src/main/java/org/linphone/activities/voip/fragments/SingleCallFragment.kt rename app/src/main/res/layout/{voip_active_call_or_conference_fragment.xml => voip_conference_call_fragment.xml} (70%) create mode 100644 app/src/main/res/layout/voip_single_call_fragment.xml diff --git a/app/src/main/java/org/linphone/activities/Navigation.kt b/app/src/main/java/org/linphone/activities/Navigation.kt index a5239baad..ff26bdb3b 100644 --- a/app/src/main/java/org/linphone/activities/Navigation.kt +++ b/app/src/main/java/org/linphone/activities/Navigation.kt @@ -48,10 +48,7 @@ import org.linphone.activities.main.history.fragments.MasterCallLogsFragment import org.linphone.activities.main.settings.fragments.* import org.linphone.activities.main.sidemenu.fragments.SideMenuFragment import org.linphone.activities.voip.CallActivity -import org.linphone.activities.voip.fragments.ActiveCallOrConferenceFragment -import org.linphone.activities.voip.fragments.ConferenceParticipantsFragment -import org.linphone.activities.voip.fragments.IncomingCallFragment -import org.linphone.activities.voip.fragments.OutgoingCallFragment +import org.linphone.activities.voip.fragments.* import org.linphone.contact.NativeContact import org.linphone.core.Address @@ -959,11 +956,21 @@ internal fun SideMenuFragment.navigateToScheduledConferences() { /* Calls related */ internal fun CallActivity.navigateToActiveCall() { - if (findNavController(R.id.nav_host_fragment).currentDestination?.id != R.id.activeCallOrConferenceFragment) { + if (findNavController(R.id.nav_host_fragment).currentDestination?.id != R.id.singleCallFragment) { findNavController(R.id.nav_host_fragment).navigate( - R.id.action_global_activeCallOrConferenceFragment, + R.id.action_global_singleCallFragment, null, - popupTo(R.id.activeCallOrConferenceFragment, false) + popupTo(R.id.conferenceCallFragment, true) + ) + } +} + +internal fun CallActivity.navigateToConferenceCall() { + if (findNavController(R.id.nav_host_fragment).currentDestination?.id != R.id.conferenceCallFragment) { + findNavController(R.id.nav_host_fragment).navigate( + R.id.action_global_conferenceCallFragment, + null, + popupTo(R.id.singleCallFragment, true) ) } } @@ -972,7 +979,7 @@ internal fun CallActivity.navigateToOutgoingCall() { findNavController(R.id.nav_host_fragment).navigate( R.id.action_global_outgoingCallFragment, null, - popupTo(R.id.activeCallOrConferenceFragment, false) + popupTo(R.id.singleCallFragment, false) ) } @@ -982,60 +989,80 @@ internal fun CallActivity.navigateToIncomingCall(earlyMediaVideoEnabled: Boolean findNavController(R.id.nav_host_fragment).navigate( R.id.action_global_incomingCallFragment, args, - popupTo(R.id.activeCallOrConferenceFragment, false) + popupTo(R.id.singleCallFragment, false) ) } internal fun OutgoingCallFragment.navigateToActiveCall() { findNavController().navigate( - R.id.action_global_activeCallOrConferenceFragment, + R.id.action_global_singleCallFragment, null, - popupTo(R.id.activeCallOrConferenceFragment, false) + popupTo(R.id.singleCallFragment, false) ) } internal fun IncomingCallFragment.navigateToActiveCall() { findNavController().navigate( - R.id.action_global_activeCallOrConferenceFragment, + R.id.action_global_singleCallFragment, null, - popupTo(R.id.activeCallOrConferenceFragment, false) + popupTo(R.id.singleCallFragment, false) ) } -internal fun ActiveCallOrConferenceFragment.navigateToCallsList() { - if (findNavController().currentDestination?.id == R.id.activeCallOrConferenceFragment) { +internal fun SingleCallFragment.navigateToCallsList() { + if (findNavController().currentDestination?.id == R.id.singleCallFragment) { findNavController().navigate( - R.id.action_activeCallOrConferenceFragment_to_callsListFragment, + R.id.action_singleCallFragment_to_callsListFragment, null, popupTo() ) } } -internal fun ActiveCallOrConferenceFragment.navigateToConferenceParticipants() { - if (findNavController().currentDestination?.id == R.id.activeCallOrConferenceFragment) { +internal fun SingleCallFragment.navigateToConferenceParticipants() { + if (findNavController().currentDestination?.id == R.id.singleCallFragment) { findNavController().navigate( - R.id.action_activeCallOrConferenceFragment_to_conferenceParticipantsFragment, + R.id.action_singleCallFragment_to_conferenceParticipantsFragment, null, popupTo() ) } } -internal fun ActiveCallOrConferenceFragment.navigateToChat(args: Bundle) { - if (findNavController().currentDestination?.id == R.id.activeCallOrConferenceFragment) { +internal fun SingleCallFragment.navigateToConferenceLayout() { + if (findNavController().currentDestination?.id == R.id.singleCallFragment) { findNavController().navigate( - R.id.action_activeCallOrConferenceFragment_to_chatFragment, - args, + R.id.action_singleCallFragment_to_conferenceLayoutFragment, + null, popupTo() ) } } -internal fun ActiveCallOrConferenceFragment.navigateToConferenceLayout() { - if (findNavController().currentDestination?.id == R.id.activeCallOrConferenceFragment) { +internal fun ConferenceCallFragment.navigateToCallsList() { + if (findNavController().currentDestination?.id == R.id.conferenceCallFragment) { findNavController().navigate( - R.id.action_activeCallOrConferenceFragment_to_conferenceLayoutFragment, + R.id.action_conferenceCallFragment_to_callsListFragment, + null, + popupTo() + ) + } +} + +internal fun ConferenceCallFragment.navigateToConferenceParticipants() { + if (findNavController().currentDestination?.id == R.id.conferenceCallFragment) { + findNavController().navigate( + R.id.action_conferenceCallFragment_to_conferenceParticipantsFragment, + null, + popupTo() + ) + } +} + +internal fun ConferenceCallFragment.navigateToConferenceLayout() { + if (findNavController().currentDestination?.id == R.id.conferenceCallFragment) { + findNavController().navigate( + R.id.action_conferenceCallFragment_to_conferenceLayoutFragment, null, popupTo() ) diff --git a/app/src/main/java/org/linphone/activities/voip/CallActivity.kt b/app/src/main/java/org/linphone/activities/voip/CallActivity.kt index 6e3700777..420731a53 100644 --- a/app/src/main/java/org/linphone/activities/voip/CallActivity.kt +++ b/app/src/main/java/org/linphone/activities/voip/CallActivity.kt @@ -29,27 +29,28 @@ import androidx.databinding.DataBindingUtil import androidx.lifecycle.ViewModelProvider import androidx.navigation.findNavController import androidx.window.layout.FoldingFeature -import org.linphone.LinphoneApplication import org.linphone.LinphoneApplication.Companion.coreContext +import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.R -import org.linphone.activities.ProximitySensorActivity +import org.linphone.activities.* import org.linphone.activities.main.MainActivity import org.linphone.activities.navigateToActiveCall -import org.linphone.activities.navigateToIncomingCall -import org.linphone.activities.navigateToOutgoingCall import org.linphone.activities.voip.viewmodels.CallsViewModel +import org.linphone.activities.voip.viewmodels.ConferenceViewModel import org.linphone.activities.voip.viewmodels.ControlsViewModel import org.linphone.compatibility.Compatibility import org.linphone.core.Call import org.linphone.core.tools.Log import org.linphone.databinding.VoipActivityBinding import org.linphone.mediastream.Version +import org.linphone.utils.Event import org.linphone.utils.PermissionHelper class CallActivity : ProximitySensorActivity() { private lateinit var binding: VoipActivityBinding private lateinit var controlsViewModel: ControlsViewModel private lateinit var callsViewModel: CallsViewModel + private lateinit var conferenceViewModel: ConferenceViewModel private var foldingFeature: FoldingFeature? = null @@ -77,6 +78,8 @@ class CallActivity : ProximitySensorActivity() { callsViewModel = ViewModelProvider(navControllerStoreOwner)[CallsViewModel::class.java] + conferenceViewModel = ViewModelProvider(navControllerStoreOwner)[ConferenceViewModel::class.java] + callsViewModel.noMoreCallEvent.observe( this ) { @@ -112,6 +115,39 @@ class CallActivity : ProximitySensorActivity() { Compatibility.enableAutoEnterPiP(this, enabled) } + callsViewModel.currentCallData.observe( + this + ) { callData -> + if (callData.call.conference == null) { + Log.i("[Call] Current call isn't linked to a conference, changing fragment") + navigateToActiveCall() + } else { + Log.i("[Call] Current call is linked to a conference, changing fragment") + navigateToConferenceCall() + } + } + + conferenceViewModel.conferenceExists.observe( + this + ) { exists -> + if (exists) { + Log.i("[Call] Found active conference, changing fragment") + navigateToConferenceCall() + } else { + Log.i("[Call] Conference no longer exists, changing fragment") + navigateToActiveCall() + } + } + + conferenceViewModel.isConferenceLocallyPaused.observe( + this + ) { paused -> + if (!paused) { + Log.i("[Call] Entered conference, make sure conference fragment is active") + navigateToConferenceCall() + } + } + if (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) { checkPermissions() } @@ -151,28 +187,22 @@ class CallActivity : ProximitySensorActivity() { } else { finish() } - } else { - coreContext.removeCallOverlay() + return + } + coreContext.removeCallOverlay() - val currentCall = coreContext.core.currentCall - if (currentCall == null) { - Log.e("[Call] No current call found, assume active call") - navigateToActiveCall() - return + val currentCall = coreContext.core.currentCall + when (currentCall?.state) { + Call.State.OutgoingInit, Call.State.OutgoingEarlyMedia, Call.State.OutgoingProgress, Call.State.OutgoingRinging -> { + navigateToOutgoingCall() } - - when (currentCall.state) { - Call.State.OutgoingInit, Call.State.OutgoingEarlyMedia, Call.State.OutgoingProgress, Call.State.OutgoingRinging -> { - navigateToOutgoingCall() - } - Call.State.IncomingReceived, Call.State.IncomingEarlyMedia -> { - val earlyMediaVideoEnabled = LinphoneApplication.corePreferences.acceptEarlyMedia && - currentCall.state == Call.State.IncomingEarlyMedia && - currentCall.currentParams.isVideoEnabled - navigateToIncomingCall(earlyMediaVideoEnabled) - } - else -> navigateToActiveCall() + Call.State.IncomingReceived, Call.State.IncomingEarlyMedia -> { + val earlyMediaVideoEnabled = corePreferences.acceptEarlyMedia && + currentCall.state == Call.State.IncomingEarlyMedia && + currentCall.currentParams.isVideoEnabled + navigateToIncomingCall(earlyMediaVideoEnabled) } + else -> {} } } @@ -251,21 +281,7 @@ class CallActivity : ProximitySensorActivity() { } private fun updateConstraintSetDependingOnFoldingState() { - /*val feature = foldingFeature ?: return - val constraintLayout = binding.constraintLayout - val set = ConstraintSet() - set.clone(constraintLayout) - - if (feature.state == FoldingFeature.State.HALF_OPENED && viewModel.videoEnabled.value == true) { - set.setGuidelinePercent(R.id.hinge_top, 0.5f) - set.setGuidelinePercent(R.id.hinge_bottom, 0.5f) - viewModel.disable(true) - } else { - set.setGuidelinePercent(R.id.hinge_top, 0f) - set.setGuidelinePercent(R.id.hinge_bottom, 1f) - viewModel.disable(false) - } - - set.applyTo(constraintLayout)*/ + val feature = foldingFeature ?: return + controlsViewModel.foldingStateChangedEvent.value = Event(feature.state) } } diff --git a/app/src/main/java/org/linphone/activities/voip/data/CallData.kt b/app/src/main/java/org/linphone/activities/voip/data/CallData.kt index 588438589..5abe932bb 100644 --- a/app/src/main/java/org/linphone/activities/voip/data/CallData.kt +++ b/app/src/main/java/org/linphone/activities/voip/data/CallData.kt @@ -20,7 +20,6 @@ package org.linphone.activities.voip.data import android.view.View -import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData import java.util.* import kotlinx.coroutines.* @@ -47,7 +46,6 @@ open class CallData(val call: Call) : GenericContactData(call.remoteAddress) { val isInRemoteConference = MutableLiveData() val remoteConferenceSubject = MutableLiveData() - val isActiveAndNotInConference = MediatorLiveData() val isOutgoing = MutableLiveData() val isIncoming = MutableLiveData() @@ -92,17 +90,6 @@ open class CallData(val call: Call) : GenericContactData(call.remoteAddress) { call.addListener(listener) isRemotelyRecorded.value = call.remoteParams?.isRecording - isActiveAndNotInConference.value = true - isActiveAndNotInConference.addSource(isPaused) { - updateActiveAndNotInConference() - } - isActiveAndNotInConference.addSource(isRemotelyPaused) { - updateActiveAndNotInConference() - } - isActiveAndNotInConference.addSource(isInRemoteConference) { - updateActiveAndNotInConference() - } - update() // initChatRoom() @@ -308,7 +295,7 @@ open class CallData(val call: Call) : GenericContactData(call.remoteAddress) { Log.i("[Call] Starting 30 seconds timer to automatically decline video request") } - private fun updateActiveAndNotInConference() { - isActiveAndNotInConference.value = isPaused.value == false && isRemotelyPaused.value == false && isInRemoteConference.value == false + fun isActiveAndNotInConference(): Boolean { + return isPaused.value == false && isRemotelyPaused.value == false && isInRemoteConference.value == false } } diff --git a/app/src/main/java/org/linphone/activities/voip/fragments/ActiveCallOrConferenceFragment.kt b/app/src/main/java/org/linphone/activities/voip/fragments/ConferenceCallFragment.kt similarity index 76% rename from app/src/main/java/org/linphone/activities/voip/fragments/ActiveCallOrConferenceFragment.kt rename to app/src/main/java/org/linphone/activities/voip/fragments/ConferenceCallFragment.kt index 621d45d10..8e29abe36 100644 --- a/app/src/main/java/org/linphone/activities/voip/fragments/ActiveCallOrConferenceFragment.kt +++ b/app/src/main/java/org/linphone/activities/voip/fragments/ConferenceCallFragment.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2021 Belledonne Communications SARL. + * Copyright (c) 2010-2022 Belledonne Communications SARL. * * This file is part of linphone-android * (see https://www.linphone.org). @@ -31,36 +31,33 @@ import android.widget.RelativeLayout import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding import androidx.navigation.navGraphViewModels +import androidx.window.layout.FoldingFeature import com.google.android.material.snackbar.Snackbar import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.R -import org.linphone.activities.* +import org.linphone.activities.GenericFragment import org.linphone.activities.main.MainActivity -import org.linphone.activities.main.viewmodels.DialogViewModel import org.linphone.activities.navigateToCallsList +import org.linphone.activities.navigateToConferenceLayout import org.linphone.activities.navigateToConferenceParticipants import org.linphone.activities.voip.viewmodels.CallsViewModel import org.linphone.activities.voip.viewmodels.ConferenceViewModel import org.linphone.activities.voip.viewmodels.ControlsViewModel import org.linphone.activities.voip.viewmodels.StatisticsListViewModel import org.linphone.activities.voip.views.RoundCornersTextureView -import org.linphone.core.* +import org.linphone.core.Conference +import org.linphone.core.StreamType import org.linphone.core.tools.Log -import org.linphone.databinding.VoipActiveCallOrConferenceFragmentBindingImpl -import org.linphone.mediastream.video.capture.CaptureTextureView -import org.linphone.utils.AppUtils -import org.linphone.utils.DialogUtils +import org.linphone.databinding.VoipConferenceCallFragmentBinding -class ActiveCallOrConferenceFragment : GenericFragment() { +class ConferenceCallFragment : GenericFragment() { private val controlsViewModel: ControlsViewModel by navGraphViewModels(R.id.call_nav_graph) private val callsViewModel: CallsViewModel by navGraphViewModels(R.id.call_nav_graph) private val conferenceViewModel: ConferenceViewModel by navGraphViewModels(R.id.call_nav_graph) private val statsViewModel: StatisticsListViewModel by navGraphViewModels(R.id.call_nav_graph) - private var dialog: Dialog? = null - - override fun getLayoutId(): Int = R.layout.voip_active_call_or_conference_fragment + override fun getLayoutId(): Int = R.layout.voip_conference_call_fragment override fun onStart() { useMaterialSharedAxisXForwardAnimation = false @@ -146,17 +143,6 @@ class ActiveCallOrConferenceFragment : GenericFragment(R.id.active_call_timer) - timer.base = - SystemClock.elapsedRealtime() - (1000 * it.call.duration) // Linphone timestamps are in seconds - timer.start() - } - } - controlsViewModel.goToConferenceParticipantsListEvent.observe( viewLifecycleOwner ) { @@ -181,7 +167,7 @@ class ActiveCallOrConferenceFragment : GenericFragment + updateHingeRelatedConstraints(state) + } + } + callsViewModel.callUpdateEvent.observe( viewLifecycleOwner ) { it.consume { call -> - if (call.state == Call.State.StreamsRunning) { - dialog?.dismiss() - } else if (call.state == Call.State.UpdatedByRemote) { - if (coreContext.core.isVideoEnabled) { - val remoteVideo = call.remoteParams?.isVideoEnabled ?: false - val localVideo = call.currentParams.isVideoEnabled - if (remoteVideo && !localVideo) { - showCallVideoUpdateDialog(call) - } - } else { - Log.w("[Call] Video display & capture are disabled, don't show video dialog") - } - } - val conference = call.conference if (conference != null && conferenceViewModel.conference.value == null) { Log.i("[Call] Found conference attached to call and no conference in dedicated view model, init & configure it") @@ -216,7 +196,7 @@ class ActiveCallOrConferenceFragment : GenericFragment @@ -229,12 +209,6 @@ class ActiveCallOrConferenceFragment : GenericFragment(R.id.remote_layout) - val remoteVideoView = remoteLayout.findViewById(R.id.remote_video_surface) - coreContext.core.nativeVideoWindowId = remoteVideoView - val localVideoView = remoteLayout.findViewById(R.id.local_preview_video_surface) - coreContext.core.nativePreviewWindowId = localVideoView - binding.stubbedConferenceActiveSpeakerLayout.setOnInflateListener { _, inflated -> Log.i("[Call] Active speaker conference layout inflated") val binding = DataBindingUtil.bind(inflated) @@ -271,16 +245,6 @@ class ActiveCallOrConferenceFragment : GenericFragment - val binding = DataBindingUtil.bind(inflated) - binding?.lifecycleOwner = viewLifecycleOwner - } - - binding.stubbedRemotelyPausedCall.setOnInflateListener { _, inflated -> - val binding = DataBindingUtil.bind(inflated) - binding?.lifecycleOwner = viewLifecycleOwner - } - binding.stubbedPausedConference.setOnInflateListener { _, inflated -> val binding = DataBindingUtil.bind(inflated) binding?.lifecycleOwner = viewLifecycleOwner @@ -293,36 +257,6 @@ class ActiveCallOrConferenceFragment : GenericFragment. + */ +package org.linphone.activities.voip.fragments + +import android.app.Dialog +import android.content.Intent +import android.os.Bundle +import android.os.SystemClock +import android.view.View +import android.widget.Chronometer +import android.widget.LinearLayout +import androidx.databinding.DataBindingUtil +import androidx.databinding.ViewDataBinding +import androidx.navigation.navGraphViewModels +import androidx.window.layout.FoldingFeature +import com.google.android.material.snackbar.Snackbar +import org.linphone.LinphoneApplication.Companion.coreContext +import org.linphone.R +import org.linphone.activities.* +import org.linphone.activities.main.MainActivity +import org.linphone.activities.main.viewmodels.DialogViewModel +import org.linphone.activities.voip.viewmodels.CallsViewModel +import org.linphone.activities.voip.viewmodels.ConferenceViewModel +import org.linphone.activities.voip.viewmodels.ControlsViewModel +import org.linphone.activities.voip.viewmodels.StatisticsListViewModel +import org.linphone.activities.voip.views.RoundCornersTextureView +import org.linphone.core.* +import org.linphone.core.tools.Log +import org.linphone.databinding.VoipSingleCallFragmentBinding +import org.linphone.mediastream.video.capture.CaptureTextureView +import org.linphone.utils.AppUtils +import org.linphone.utils.DialogUtils + +class SingleCallFragment : GenericFragment() { + private val controlsViewModel: ControlsViewModel by navGraphViewModels(R.id.call_nav_graph) + private val callsViewModel: CallsViewModel by navGraphViewModels(R.id.call_nav_graph) + private val conferenceViewModel: ConferenceViewModel by navGraphViewModels(R.id.call_nav_graph) + private val statsViewModel: StatisticsListViewModel by navGraphViewModels(R.id.call_nav_graph) + + private var dialog: Dialog? = null + + override fun getLayoutId(): Int = R.layout.voip_single_call_fragment + + override fun onStart() { + useMaterialSharedAxisXForwardAnimation = false + + super.onStart() + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + controlsViewModel.hideCallStats() // In case it was toggled on during incoming/outgoing fragment was visible + + binding.lifecycleOwner = viewLifecycleOwner + + binding.controlsViewModel = controlsViewModel + + binding.callsViewModel = callsViewModel + + binding.conferenceViewModel = conferenceViewModel + + binding.statsViewModel = statsViewModel + + callsViewModel.currentCallData.observe( + viewLifecycleOwner + ) { + if (it != null) { + val timer = binding.root.findViewById(R.id.active_call_timer) + timer.base = + SystemClock.elapsedRealtime() - (1000 * it.call.duration) // Linphone timestamps are in seconds + timer.start() + } + } + + controlsViewModel.goToConferenceParticipantsListEvent.observe( + viewLifecycleOwner + ) { + it.consume { + navigateToConferenceParticipants() + } + } + + controlsViewModel.goToChatEvent.observe( + viewLifecycleOwner + ) { + it.consume { + goToChat() + } + } + + controlsViewModel.goToCallsListEvent.observe( + viewLifecycleOwner + ) { + it.consume { + navigateToCallsList() + } + } + + controlsViewModel.goToConferenceLayoutSettingsEvent.observe( + viewLifecycleOwner + ) { + it.consume { + navigateToConferenceLayout() + } + } + + controlsViewModel.foldingStateChangedEvent.observe( + viewLifecycleOwner + ) { + it.consume { state -> + updateHingeRelatedConstraints(state) + } + } + + callsViewModel.callUpdateEvent.observe( + viewLifecycleOwner + ) { + it.consume { call -> + if (call.state == Call.State.StreamsRunning) { + dialog?.dismiss() + } else if (call.state == Call.State.UpdatedByRemote) { + if (coreContext.core.isVideoEnabled) { + val remoteVideo = call.remoteParams?.isVideoEnabled ?: false + val localVideo = call.currentParams.isVideoEnabled + if (remoteVideo && !localVideo) { + showCallVideoUpdateDialog(call) + } + } else { + Log.w("[Call] Video display & capture are disabled, don't show video dialog") + } + } + } + } + + controlsViewModel.goToDialerEvent.observe( + viewLifecycleOwner + ) { + it.consume { isCallTransfer -> + val intent = Intent() + intent.setClass(requireContext(), MainActivity::class.java) + intent.putExtra("Dialer", true) + intent.putExtra("Transfer", isCallTransfer) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(intent) + } + } + + val remoteLayout = binding.root.findViewById(R.id.remote_layout) + val remoteVideoView = remoteLayout.findViewById(R.id.remote_video_surface) + coreContext.core.nativeVideoWindowId = remoteVideoView + val localVideoView = remoteLayout.findViewById(R.id.local_preview_video_surface) + coreContext.core.nativePreviewWindowId = localVideoView + + binding.stubbedAudioRoutes.setOnInflateListener { _, inflated -> + val binding = DataBindingUtil.bind(inflated) + binding?.lifecycleOwner = viewLifecycleOwner + } + + binding.stubbedNumpad.setOnInflateListener { _, inflated -> + val binding = DataBindingUtil.bind(inflated) + binding?.lifecycleOwner = viewLifecycleOwner + } + + binding.stubbedCallStats.setOnInflateListener { _, inflated -> + val binding = DataBindingUtil.bind(inflated) + binding?.lifecycleOwner = viewLifecycleOwner + } + + binding.stubbedPausedCall.setOnInflateListener { _, inflated -> + val binding = DataBindingUtil.bind(inflated) + binding?.lifecycleOwner = viewLifecycleOwner + } + + binding.stubbedRemotelyPausedCall.setOnInflateListener { _, inflated -> + val binding = DataBindingUtil.bind(inflated) + binding?.lifecycleOwner = viewLifecycleOwner + } + } + + override fun onPause() { + super.onPause() + + controlsViewModel.hideExtraButtons(true) + } + + private fun showCallVideoUpdateDialog(call: Call) { + val viewModel = DialogViewModel(AppUtils.getString(R.string.call_video_update_requested_dialog)) + dialog = DialogUtils.getVoipDialog(requireContext(), viewModel) + + viewModel.showCancelButton( + { + coreContext.answerCallVideoUpdateRequest(call, false) + dialog?.dismiss() + }, + getString(R.string.dialog_decline) + ) + + viewModel.showOkButton( + { + coreContext.answerCallVideoUpdateRequest(call, true) + dialog?.dismiss() + }, + getString(R.string.dialog_accept) + ) + + dialog?.show() + } + + private fun goToChat() { + val intent = Intent() + intent.setClass(requireContext(), MainActivity::class.java) + intent.putExtra("Chat", true) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(intent) + } + + private fun updateHingeRelatedConstraints(state: FoldingFeature.State) { + /*val constraintLayout = binding.constraintLayout + val set = ConstraintSet() + set.clone(constraintLayout) + + if (state == FoldingFeature.State.HALF_OPENED) { + set.setGuidelinePercent(R.id.hinge_top, 0.5f) + set.setGuidelinePercent(R.id.hinge_bottom, 0.5f) + } else { + set.setGuidelinePercent(R.id.hinge_top, 0f) + set.setGuidelinePercent(R.id.hinge_bottom, 1f) + } + + set.applyTo(constraintLayout)*/ + } +} diff --git a/app/src/main/java/org/linphone/activities/voip/viewmodels/ControlsViewModel.kt b/app/src/main/java/org/linphone/activities/voip/viewmodels/ControlsViewModel.kt index 7423de803..083101e97 100644 --- a/app/src/main/java/org/linphone/activities/voip/viewmodels/ControlsViewModel.kt +++ b/app/src/main/java/org/linphone/activities/voip/viewmodels/ControlsViewModel.kt @@ -27,6 +27,7 @@ import android.view.animation.LinearInterpolator import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.window.layout.FoldingFeature import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.R @@ -90,7 +91,7 @@ class ControlsViewModel : ViewModel() { MutableLiveData>() } - val goToConferenceLayoutSettings: MutableLiveData> by lazy { + val goToConferenceLayoutSettingsEvent: MutableLiveData> by lazy { MutableLiveData>() } @@ -98,10 +99,14 @@ class ControlsViewModel : ViewModel() { MutableLiveData>() } - val goToDialer: MutableLiveData> by lazy { + val goToDialerEvent: MutableLiveData> by lazy { MutableLiveData>() } + val foldingStateChangedEvent: MutableLiveData> by lazy { + MutableLiveData>() + } + private val nonEarpieceOutputAudioDevice = MutableLiveData() private var previewX: Float = 0f @@ -405,15 +410,15 @@ class ControlsViewModel : ViewModel() { } fun goToConferenceLayout() { - goToConferenceLayoutSettings.value = Event(true) + goToConferenceLayoutSettingsEvent.value = Event(true) } fun goToDialerForCallTransfer() { - goToDialer.value = Event(true) + goToDialerEvent.value = Event(true) } fun goToDialerForNewCall() { - goToDialer.value = Event(false) + goToDialerEvent.value = Event(false) } private fun updateUI() { diff --git a/app/src/main/res/layout/voip_activity.xml b/app/src/main/res/layout/voip_activity.xml index 8a2f06742..8b6c65cfc 100644 --- a/app/src/main/res/layout/voip_activity.xml +++ b/app/src/main/res/layout/voip_activity.xml @@ -33,7 +33,7 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toBottomOf="@id/status_fragment" app:navGraph="@navigation/call_nav_graph" - tools:layout="@layout/voip_active_call_or_conference_fragment" /> + tools:layout="@layout/voip_single_call_fragment" /> diff --git a/app/src/main/res/layout/voip_active_call_or_conference_fragment.xml b/app/src/main/res/layout/voip_conference_call_fragment.xml similarity index 70% rename from app/src/main/res/layout/voip_active_call_or_conference_fragment.xml rename to app/src/main/res/layout/voip_conference_call_fragment.xml index 96aa3b285..1e77dba5f 100644 --- a/app/src/main/res/layout/voip_active_call_or_conference_fragment.xml +++ b/app/src/main/res/layout/voip_conference_call_fragment.xml @@ -42,8 +42,8 @@ android:id="@+id/stubbed_conference_active_speaker_layout" android:inflatedId="@+id/conference_active_speaker_layout" android:layout="@layout/voip_conference_active_speaker" - android:visibility="@{conferenceViewModel.conferenceActiveSpeakerDisplayMode && conferenceViewModel.conferenceExists && !callsViewModel.currentCallData.isActiveAndNotInConference ? View.VISIBLE : View.GONE, default=gone}" - app:inflatedVisibility="@{conferenceViewModel.conferenceActiveSpeakerDisplayMode && conferenceViewModel.conferenceExists && !callsViewModel.currentCallData.isActiveAndNotInConference ? View.VISIBLE : View.GONE}" + android:visibility="@{conferenceViewModel.conferenceActiveSpeakerDisplayMode ? View.VISIBLE : View.GONE, default=gone}" + app:inflatedVisibility="@{conferenceViewModel.conferenceActiveSpeakerDisplayMode ? View.VISIBLE : View.GONE}" android:layout_width="match_parent" android:layout_height="match_parent" app:conferenceViewModel="@{conferenceViewModel}" @@ -52,8 +52,8 @@ - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/call_nav_graph.xml b/app/src/main/res/navigation/call_nav_graph.xml index a21bd990f..db1228ecd 100644 --- a/app/src/main/res/navigation/call_nav_graph.xml +++ b/app/src/main/res/navigation/call_nav_graph.xml @@ -3,29 +3,52 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/call_nav_graph" - app:startDestination="@id/activeCallOrConferenceFragment"> + app:startDestination="@id/singleCallFragment"> + android:id="@+id/singleCallFragment" + android:name="org.linphone.activities.voip.fragments.SingleCallFragment" + tools:layout="@layout/voip_single_call_fragment" + android:label="SingleCallFragment" > - + + + + + + + + + +