mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Reworked in-call fragment to properly handle foldable phones
This commit is contained in:
parent
0dbc9f7a8a
commit
18254fd385
6 changed files with 163 additions and 85 deletions
|
|
@ -76,6 +76,7 @@ dependencies {
|
|||
implementation "androidx.slidingpanelayout:slidingpanelayout:1.2.0"
|
||||
|
||||
implementation "androidx.core:core-telecom:1.0.0-alpha01"
|
||||
implementation 'androidx.window:window:1.1.0'
|
||||
|
||||
def nav_version = "2.7.2"
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ import androidx.lifecycle.ViewModelProvider
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.window.layout.FoldingFeature
|
||||
import androidx.window.layout.WindowInfoTracker
|
||||
import androidx.window.layout.WindowLayoutInfo
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.linphone.LinphoneApplication
|
||||
import org.linphone.R
|
||||
import org.linphone.core.tools.Log
|
||||
|
|
@ -78,6 +83,15 @@ class VoipActivity : AppCompatActivity() {
|
|||
binding = DataBindingUtil.setContentView(this, R.layout.voip_activity)
|
||||
binding.lifecycleOwner = this
|
||||
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
WindowInfoTracker
|
||||
.getOrCreate(this@VoipActivity)
|
||||
.windowLayoutInfo(this@VoipActivity)
|
||||
.collect { newLayoutInfo ->
|
||||
updateCurrentLayout(newLayoutInfo)
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel = run {
|
||||
ViewModelProvider(this)[SharedCallViewModel::class.java]
|
||||
}
|
||||
|
|
@ -158,6 +172,20 @@ class VoipActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateCurrentLayout(newLayoutInfo: WindowLayoutInfo) {
|
||||
if (newLayoutInfo.displayFeatures.isNotEmpty()) {
|
||||
for (feature in newLayoutInfo.displayFeatures) {
|
||||
val foldingFeature = feature as? FoldingFeature
|
||||
if (foldingFeature != null) {
|
||||
Log.i(
|
||||
"$TAG Folding feature state changed: ${foldingFeature.state}, orientation is ${foldingFeature.orientation}"
|
||||
)
|
||||
sharedViewModel.foldingState.value = foldingFeature
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun showBlueToast(message: String, @DrawableRes icon: Int) {
|
||||
val blueToast = AppUtils.getBlueToast(this, binding.toastsArea, message, icon)
|
||||
binding.toastsArea.addView(blueToast.root)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ import android.view.MotionEvent
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.window.layout.FoldingFeature
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.R
|
||||
|
|
@ -96,13 +98,17 @@ class ActiveCallFragment : GenericCallFragment() {
|
|||
binding.lifecycleOwner = viewLifecycleOwner
|
||||
binding.viewModel = callViewModel
|
||||
|
||||
val standardBottomSheetBehavior = BottomSheetBehavior.from(binding.bottomBar.root)
|
||||
standardBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
val bottomSheetBehavior = BottomSheetBehavior.from(binding.bottomBar.root)
|
||||
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
|
||||
sharedViewModel = requireActivity().run {
|
||||
ViewModelProvider(this)[SharedCallViewModel::class.java]
|
||||
}
|
||||
|
||||
sharedViewModel.foldingState.observe(viewLifecycleOwner) { feature ->
|
||||
updateHingeRelatedConstraints(feature)
|
||||
}
|
||||
|
||||
callViewModel.fullScreenMode.observe(viewLifecycleOwner) { hide ->
|
||||
Log.i("$TAG Switching full screen mode to ${if (hide) "ON" else "OFF"}")
|
||||
sharedViewModel.toggleFullScreenEvent.value = Event(hide)
|
||||
|
|
@ -154,16 +160,16 @@ class ActiveCallFragment : GenericCallFragment() {
|
|||
|
||||
callViewModel.toggleExtraActionsBottomSheetEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
val state = standardBottomSheetBehavior.state
|
||||
val state = bottomSheetBehavior.state
|
||||
if (state == BottomSheetBehavior.STATE_COLLAPSED) {
|
||||
standardBottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
} else if (state == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
standardBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
standardBottomSheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
|
||||
bottomSheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
when (newState) {
|
||||
BottomSheetBehavior.STATE_COLLAPSED, BottomSheetBehavior.STATE_HIDDEN -> {
|
||||
|
|
@ -194,4 +200,24 @@ class ActiveCallFragment : GenericCallFragment() {
|
|||
super.onPause()
|
||||
binding.localPreviewVideoSurface.setOnTouchListener(null)
|
||||
}
|
||||
|
||||
private fun updateHingeRelatedConstraints(feature: FoldingFeature) {
|
||||
Log.i("$TAG Updating constraint layout hinges: $feature")
|
||||
|
||||
val constraintLayout = binding.constraintLayout
|
||||
val set = ConstraintSet()
|
||||
set.clone(constraintLayout)
|
||||
|
||||
if (feature.state == FoldingFeature.State.HALF_OPENED) {
|
||||
set.setGuidelinePercent(R.id.hinge_top, 0.5f)
|
||||
set.setGuidelinePercent(R.id.hinge_bottom, 0.5f)
|
||||
sharedViewModel.folded.value = true
|
||||
} else {
|
||||
set.setGuidelinePercent(R.id.hinge_top, 0f)
|
||||
set.setGuidelinePercent(R.id.hinge_bottom, 1f)
|
||||
sharedViewModel.folded.value = false
|
||||
}
|
||||
|
||||
set.applyTo(constraintLayout)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,13 @@ package org.linphone.ui.voip.viewmodel
|
|||
import androidx.annotation.UiThread
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.window.layout.FoldingFeature
|
||||
import org.linphone.utils.Event
|
||||
|
||||
class SharedCallViewModel @UiThread constructor() : ViewModel() {
|
||||
val toggleFullScreenEvent = MutableLiveData<Event<Boolean>>()
|
||||
|
||||
val foldingState = MutableLiveData<FoldingFeature>()
|
||||
|
||||
val folded = MutableLiveData<Boolean>()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,104 @@
|
|||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/constraint_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/in_call_black">
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/hinge_top"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/hinge_bottom"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="1" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/background"
|
||||
android:src="@drawable/shape_round_in_call_gray_background"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_marginBottom="@dimen/in_call_main_actions_menu_height"
|
||||
app:layout_constraintTop_toBottomOf="@id/call_direction_label"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<io.getstream.avatarview.AvatarView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="@dimen/avatar_in_call_size"
|
||||
android:layout_height="@dimen/avatar_in_call_size"
|
||||
android:background="@drawable/shape_avatar_background"
|
||||
contactAvatar="@{viewModel.contact}"
|
||||
app:avatarViewInitialsBackgroundColor="@color/blue_outgoing_message"
|
||||
app:avatarViewInitialsTextColor="@color/gray_9"
|
||||
app:avatarViewInitialsTextSize="36sp"
|
||||
app:avatarViewInitialsTextStyle="bold"
|
||||
app:avatarViewPlaceholder="@drawable/contact_avatar"
|
||||
app:avatarViewShape="circle"
|
||||
app:avatarViewBorderWidth="0dp"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
app:layout_constraintEnd_toEndOf="@id/background"
|
||||
app:layout_constraintStart_toStartOf="@id/background"
|
||||
app:layout_constraintTop_toBottomOf="@id/call_direction_label"
|
||||
app:layout_constraintBottom_toTopOf="@id/name"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/trust_badge"
|
||||
android:layout_width="@dimen/avatar_presence_badge_in_call_size"
|
||||
android:layout_height="@dimen/avatar_presence_badge_in_call_size"
|
||||
android:src="@drawable/trusted"
|
||||
android:visibility="@{viewModel.contact.showTrust ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintStart_toStartOf="@id/avatar"
|
||||
app:layout_constraintBottom_toBottomOf="@id/avatar"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style_300"
|
||||
android:id="@+id/name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="15dp"
|
||||
android:text="@{viewModel.displayedName, default=`John Doe`}"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="22sp"
|
||||
app:layout_constraintTop_toBottomOf="@id/avatar"
|
||||
app:layout_constraintBottom_toTopOf="@id/address"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style"
|
||||
android:id="@+id/address"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{viewModel.displayedAddress, default=`sip:johndoe@sip.linphone.org`}"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintTop_toBottomOf="@id/name"
|
||||
app:layout_constraintBottom_toBottomOf="@id/background"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<org.linphone.ui.voip.view.RoundCornersTextureView
|
||||
android:id="@+id/remote_video_surface"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:onClick="@{() -> viewModel.toggleFullScreen()}"
|
||||
android:visibility="@{viewModel.isVideoEnabled ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="@id/hinge_bottom"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/call_direction_icon"
|
||||
android:layout_width="@dimen/icon_size"
|
||||
|
|
@ -44,7 +138,7 @@
|
|||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toEndOf="@id/call_direction_icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/hinge_top"
|
||||
app:layout_constraintBottom_toTopOf="@id/background"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
|
|
@ -83,88 +177,11 @@
|
|||
android:layout_marginEnd="10dp"
|
||||
android:src="@drawable/camera_rotate"
|
||||
android:visibility="@{!viewModel.fullScreenMode && viewModel.isVideoEnabled && viewModel.showSwitchCamera ? View.VISIBLE : View.GONE}"
|
||||
app:tint="@color/white"
|
||||
app:layout_constraintTop_toTopOf="@id/call_direction_label"
|
||||
app:layout_constraintBottom_toBottomOf="@id/call_direction_label"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:tint="@color/white" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/background"
|
||||
android:src="@drawable/shape_round_in_call_gray_background"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_marginBottom="@dimen/in_call_main_actions_menu_height"
|
||||
app:layout_constraintTop_toBottomOf="@id/call_direction_label"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<io.getstream.avatarview.AvatarView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="@dimen/avatar_in_call_size"
|
||||
android:layout_height="@dimen/avatar_in_call_size"
|
||||
android:background="@drawable/shape_avatar_background"
|
||||
contactAvatar="@{viewModel.contact}"
|
||||
app:avatarViewInitialsBackgroundColor="@color/blue_outgoing_message"
|
||||
app:avatarViewInitialsTextColor="@color/gray_9"
|
||||
app:avatarViewInitialsTextSize="36sp"
|
||||
app:avatarViewInitialsTextStyle="bold"
|
||||
app:avatarViewPlaceholder="@drawable/contact_avatar"
|
||||
app:avatarViewShape="circle"
|
||||
app:avatarViewBorderWidth="0dp"
|
||||
app:layout_constraintEnd_toEndOf="@id/background"
|
||||
app:layout_constraintStart_toStartOf="@id/background"
|
||||
app:layout_constraintTop_toTopOf="@id/background"
|
||||
app:layout_constraintBottom_toBottomOf="@id/background"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/trust_badge"
|
||||
android:layout_width="@dimen/avatar_presence_badge_in_call_size"
|
||||
android:layout_height="@dimen/avatar_presence_badge_in_call_size"
|
||||
android:src="@drawable/trusted"
|
||||
android:visibility="@{viewModel.contact.showTrust ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintStart_toStartOf="@id/avatar"
|
||||
app:layout_constraintBottom_toBottomOf="@id/avatar"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style_300"
|
||||
android:id="@+id/name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="15dp"
|
||||
android:text="@{viewModel.displayedName, default=`John Doe`}"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="22sp"
|
||||
app:layout_constraintTop_toBottomOf="@id/avatar"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style"
|
||||
android:id="@+id/address"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{viewModel.displayedAddress, default=`sip:johndoe@sip.linphone.org`}"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintTop_toBottomOf="@id/name"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<org.linphone.ui.voip.view.RoundCornersTextureView
|
||||
android:id="@+id/remote_video_surface"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginBottom="@{viewModel.fullScreenMode ? @dimen/zero : @dimen/in_call_main_actions_menu_height, default=@dimen/in_call_main_actions_menu_height}"
|
||||
android:onClick="@{() -> viewModel.toggleFullScreen()}"
|
||||
android:visibility="@{viewModel.isVideoEnabled ? View.VISIBLE : View.GONE, default=gone}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/background" />
|
||||
|
||||
<org.linphone.ui.voip.view.RoundCornersTextureView
|
||||
android:id="@+id/local_preview_video_surface"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:keepScreenOn="true"
|
||||
tools:context=".ui.voip.VoipActivity">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue