diff --git a/app/src/main/java/org/linphone/activities/GenericActivity.kt b/app/src/main/java/org/linphone/activities/GenericActivity.kt index 34c30a4d7..0eec20c2b 100644 --- a/app/src/main/java/org/linphone/activities/GenericActivity.kt +++ b/app/src/main/java/org/linphone/activities/GenericActivity.kt @@ -49,8 +49,7 @@ abstract class GenericActivity : AppCompatActivity() { val isDestructionPending: Boolean get() = _isDestructionPending - open fun onLayoutChanges(foldingFeature: FoldingFeature?) { - } + open fun onLayoutChanges(foldingFeature: FoldingFeature?) { } @SuppressLint("SourceLockedOrientationActivity") override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/org/linphone/activities/call/CallActivity.kt b/app/src/main/java/org/linphone/activities/call/CallActivity.kt index 022537728..0044f6973 100644 --- a/app/src/main/java/org/linphone/activities/call/CallActivity.kt +++ b/app/src/main/java/org/linphone/activities/call/CallActivity.kt @@ -24,16 +24,16 @@ import android.content.res.Configuration import android.content.res.Resources import android.os.Bundle import android.view.Gravity -import android.view.MotionEvent import android.view.View +import androidx.constraintlayout.widget.ConstraintSet import androidx.databinding.DataBindingUtil import androidx.lifecycle.ViewModelProvider +import androidx.window.layout.FoldingFeature import kotlinx.coroutines.* import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.R -import org.linphone.activities.call.viewmodels.ControlsFadingViewModel -import org.linphone.activities.call.viewmodels.SharedCallViewModel +import org.linphone.activities.call.viewmodels.* import org.linphone.activities.main.MainActivity import org.linphone.compatibility.Compatibility import org.linphone.core.tools.Log @@ -44,9 +44,7 @@ class CallActivity : ProximitySensorActivity() { private lateinit var viewModel: ControlsFadingViewModel private lateinit var sharedViewModel: SharedCallViewModel - private var previewX: Float = 0f - private var previewY: Float = 0f - private lateinit var videoZoomHelper: VideoZoomHelper + private var foldingFeature: FoldingFeature? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -58,7 +56,7 @@ class CallActivity : ProximitySensorActivity() { binding.lifecycleOwner = this viewModel = ViewModelProvider(this).get(ControlsFadingViewModel::class.java) - binding.viewModel = viewModel + binding.controlsFadingViewModel = viewModel sharedViewModel = ViewModelProvider(this).get(SharedCallViewModel::class.java) @@ -84,38 +82,24 @@ class CallActivity : ProximitySensorActivity() { } ) - coreContext.core.nativeVideoWindowId = binding.remoteVideoSurface - coreContext.core.nativePreviewWindowId = binding.localPreviewVideoSurface - - binding.setPreviewTouchListener { v, event -> - when (event.action) { - MotionEvent.ACTION_DOWN -> { - previewX = v.x - event.rawX - previewY = v.y - event.rawY - } - MotionEvent.ACTION_MOVE -> { - v.animate() - .x(event.rawX + previewX) - .y(event.rawY + previewY) - .setDuration(0) - .start() - } - else -> { - v.performClick() - false - } - } - true - } - - videoZoomHelper = VideoZoomHelper(this, binding.remoteVideoSurface) - viewModel.proximitySensorEnabled.observe( this, { enableProximitySensor(it) } ) + + viewModel.videoEnabled.observe( + this, + { + updateConstraintSetDependingOnFoldingState() + } + ) + } + + override fun onLayoutChanges(foldingFeature: FoldingFeature?) { + this.foldingFeature = foldingFeature + updateConstraintSetDependingOnFoldingState() } override fun onResume() { @@ -206,4 +190,23 @@ class CallActivity : ProximitySensorActivity() { View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION } + + 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) + } } 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 2641b6f9c..fee9e5f2a 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 @@ -72,13 +72,19 @@ class ControlsFragment : GenericFragment() { ViewModelProvider(this).get(SharedCallViewModel::class.java) } - callsViewModel = ViewModelProvider(this).get(CallsViewModel::class.java) + callsViewModel = requireActivity().run { + ViewModelProvider(this).get(CallsViewModel::class.java) + } binding.viewModel = callsViewModel - controlsViewModel = ViewModelProvider(this).get(ControlsViewModel::class.java) + controlsViewModel = requireActivity().run { + ViewModelProvider(this).get(ControlsViewModel::class.java) + } binding.controlsViewModel = controlsViewModel - conferenceViewModel = ViewModelProvider(this).get(ConferenceViewModel::class.java) + conferenceViewModel = requireActivity().run { + ViewModelProvider(this).get(ConferenceViewModel::class.java) + } binding.conferenceViewModel = conferenceViewModel callsViewModel.currentCallViewModel.observe( diff --git a/app/src/main/java/org/linphone/activities/call/fragments/VideoRenderingFragment.kt b/app/src/main/java/org/linphone/activities/call/fragments/VideoRenderingFragment.kt new file mode 100644 index 000000000..1498ca318 --- /dev/null +++ b/app/src/main/java/org/linphone/activities/call/fragments/VideoRenderingFragment.kt @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2010-2021 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.fragments + +import android.os.Bundle +import android.view.MotionEvent +import android.view.View +import androidx.lifecycle.ViewModelProvider +import org.linphone.LinphoneApplication.Companion.coreContext +import org.linphone.R +import org.linphone.activities.GenericFragment +import org.linphone.activities.call.VideoZoomHelper +import org.linphone.activities.call.viewmodels.CallsViewModel +import org.linphone.activities.call.viewmodels.ConferenceViewModel +import org.linphone.activities.call.viewmodels.ControlsFadingViewModel +import org.linphone.databinding.CallVideoFragmentBinding + +class VideoRenderingFragment : GenericFragment() { + private lateinit var controlsFadingViewModel: ControlsFadingViewModel + private lateinit var callsViewModel: CallsViewModel + private lateinit var conferenceViewModel: ConferenceViewModel + + private var previewX: Float = 0f + private var previewY: Float = 0f + private lateinit var videoZoomHelper: VideoZoomHelper + + override fun getLayoutId(): Int = R.layout.call_video_fragment + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.lifecycleOwner = this + + controlsFadingViewModel = requireActivity().run { + ViewModelProvider(this).get(ControlsFadingViewModel::class.java) + } + binding.controlsFadingViewModel = controlsFadingViewModel + + callsViewModel = requireActivity().run { + ViewModelProvider(this).get(CallsViewModel::class.java) + } + + conferenceViewModel = requireActivity().run { + ViewModelProvider(this).get(ConferenceViewModel::class.java) + } + binding.conferenceViewModel = conferenceViewModel + + coreContext.core.nativeVideoWindowId = binding.remoteVideoSurface + coreContext.core.nativePreviewWindowId = binding.localPreviewVideoSurface + + binding.setPreviewTouchListener { v, event -> + when (event.action) { + MotionEvent.ACTION_DOWN -> { + previewX = v.x - event.rawX + previewY = v.y - event.rawY + } + MotionEvent.ACTION_MOVE -> { + v.animate() + .x(event.rawX + previewX) + .y(event.rawY + previewY) + .setDuration(0) + .start() + } + else -> { + v.performClick() + false + } + } + true + } + + videoZoomHelper = VideoZoomHelper(requireContext(), binding.remoteVideoSurface) + } +} 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 83bb8007f..e8a6a61a1 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 @@ -58,7 +58,8 @@ class CallsViewModel : ViewModel() { if (currentCall == null) { currentCallViewModel.value?.destroy() } else if (currentCallViewModel.value?.call != currentCall) { - currentCallViewModel.value = CallViewModel(currentCall) + val viewModel = CallViewModel(currentCall) + currentCallViewModel.value = viewModel } if (state == Call.State.End || state == Call.State.Released || state == Call.State.Error) { @@ -98,7 +99,9 @@ class CallsViewModel : ViewModel() { noActiveCall.value = currentCall == null if (currentCall != null) { currentCallViewModel.value?.destroy() - currentCallViewModel.value = CallViewModel(currentCall) + + val viewModel = CallViewModel(currentCall) + currentCallViewModel.value = viewModel } callPausedByRemote.value = currentCall?.state == Call.State.PausedByRemote diff --git a/app/src/main/java/org/linphone/activities/call/viewmodels/ControlsFadingViewModel.kt b/app/src/main/java/org/linphone/activities/call/viewmodels/ControlsFadingViewModel.kt index d2a8745f0..1d231a8db 100644 --- a/app/src/main/java/org/linphone/activities/call/viewmodels/ControlsFadingViewModel.kt +++ b/app/src/main/java/org/linphone/activities/call/viewmodels/ControlsFadingViewModel.kt @@ -40,12 +40,16 @@ class ControlsFadingViewModel : ViewModel() { val isVideoPreviewHidden = MutableLiveData() val isVideoPreviewResizedForPip = MutableLiveData() - private val videoEnabled = MutableLiveData() - private val nonEarpieceOutputAudioDevice = MutableLiveData() + val videoEnabled = MutableLiveData() + val proximitySensorEnabled: MediatorLiveData = MediatorLiveData() + private val nonEarpieceOutputAudioDevice = MutableLiveData() + private var timer: Timer? = null + private var disabled: Boolean = false + private val listener = object : CoreListenerStub() { override fun onCallStateChanged( core: Core, @@ -109,6 +113,15 @@ class ControlsFadingViewModel : ViewModel() { startTimer() } + fun disable(disable: Boolean) { + disabled = disable + if (disabled) { + stopTimer() + } else { + startTimer() + } + } + private fun shouldEnableProximitySensor(): Boolean { return !(videoEnabled.value ?: false) && !(nonEarpieceOutputAudioDevice.value ?: false) } @@ -121,6 +134,7 @@ class ControlsFadingViewModel : ViewModel() { private fun startTimer() { timer?.cancel() + if (disabled) return timer = Timer("Hide UI controls scheduler") timer?.schedule( diff --git a/app/src/main/res/drawable/call_video_conference_participant_background.xml b/app/src/main/res/drawable/call_video_conference_participant_background.xml new file mode 100644 index 000000000..f3cf29b28 --- /dev/null +++ b/app/src/main/res/drawable/call_video_conference_participant_background.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout/call_activity.xml b/app/src/main/res/layout/call_activity.xml index ccf0839a6..15c6da418 100644 --- a/app/src/main/res/layout/call_activity.xml +++ b/app/src/main/res/layout/call_activity.xml @@ -1,14 +1,12 @@ + xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto"> - @@ -19,50 +17,54 @@ android:keepScreenOn="true" android:background="?attr/backgroundColor"> - - + android:layout_height="0dp" + tools:layout="@layout/call_video_fragment" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toTopOf="@id/hinge_bottom"/> - + - + + + android:layout_height="@dimen/status_fragment_size" + tools:layout="@layout/call_status_fragment" + app:layout_constraintTop_toBottomOf="@id/hinge_top"/> - + - - - - - + + + + + + + + + + + + + + + + + + + \ No newline at end of file