Move camera flip and improve movement of some ui elements.

This commit is contained in:
Alex Hart
2024-05-22 14:18:46 -03:00
committed by Cody Henthorne
parent 6362da7a50
commit 887c173d8f
9 changed files with 222 additions and 149 deletions

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.components.webrtc
import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.constraintlayout.widget.Barrier
import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.google.android.material.bottomsheet.BottomSheetBehavior
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.util.views.SlideUpWithDependencyBehavior
import kotlin.math.max
/**
* Coordinator Layout Behavior which allows us to "pin" UI Elements to the top of the controls sheet.
*/
class SlideUpWithCallControlsBehavior(
context: Context,
attributeSet: AttributeSet?
) : SlideUpWithDependencyBehavior(context, attributeSet, offsetY = 0f) {
private var minTranslationY: Float = 0f
var onTopOfControlsChangedListener: OnTopOfControlsChangedListener? = null
override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
super.onDependentViewChanged(parent, child, dependency)
val bottomSheetBehavior = (dependency.layoutParams as CoordinatorLayout.LayoutParams).behavior as BottomSheetBehavior<*>
val slideOffset = bottomSheetBehavior.calculateSlideOffset()
if (slideOffset == 0f) {
minTranslationY = child.translationY
} else {
child.translationY = max(child.translationY, minTranslationY)
}
emitViewChanged(child)
return true
}
override fun onLayoutChild(parent: CoordinatorLayout, child: View, layoutDirection: Int): Boolean {
emitViewChanged(child)
return false
}
override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
return dependency.id == R.id.call_controls_info_parent
}
private fun emitViewChanged(child: View) {
val barrier = child.findViewById<Barrier>(R.id.call_screen_above_controls_barrier)
onTopOfControlsChangedListener?.onTopOfControlsChanged(barrier.bottom + child.translationY.toInt())
}
interface OnTopOfControlsChangedListener {
fun onTopOfControlsChanged(topOfControls: Int)
}
}

View File

@@ -26,10 +26,10 @@ import androidx.annotation.StringRes;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.appcompat.widget.Toolbar;
import androidx.compose.ui.platform.ComposeView;
import androidx.constraintlayout.widget.Barrier;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.constraintlayout.widget.Guideline;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.util.Consumer;
import androidx.core.view.WindowInsetsCompat;
import androidx.recyclerview.widget.DefaultItemAnimator;
@@ -72,6 +72,7 @@ import org.whispersystems.signalservice.api.messages.calls.HangupMessage;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
public class WebRtcCallView extends InsetAwareConstraintLayout {
@@ -128,12 +129,9 @@ public class WebRtcCallView extends InsetAwareConstraintLayout {
private RecyclerView groupReactionsFeed;
private MultiReactionBurstLayout reactionViews;
private ComposeView raiseHandSnackbar;
private Barrier pipBottomBoundaryBarrier;
private View missingPermissionContainer;
private MaterialButton allowAccessButton;
private WebRtcCallParticipantsPagerAdapter pagerAdapter;
private WebRtcCallParticipantsRecyclerAdapter recyclerAdapter;
private WebRtcReactionsRecyclerAdapter reactionsAdapter;
@@ -210,7 +208,6 @@ public class WebRtcCallView extends InsetAwareConstraintLayout {
groupReactionsFeed = findViewById(R.id.call_screen_reactions_feed);
reactionViews = findViewById(R.id.call_screen_reactions_container);
raiseHandSnackbar = findViewById(R.id.call_screen_raise_hand_view);
pipBottomBoundaryBarrier = findViewById(R.id.pip_bottom_boundary_barrier);
missingPermissionContainer = findViewById(R.id.missing_permissions_container);
allowAccessButton = findViewById(R.id.allow_access_button);
@@ -375,17 +372,17 @@ public class WebRtcCallView extends InsetAwareConstraintLayout {
rotatableControls.add(smallLocalAudioIndicator);
rotatableControls.add(ringToggle);
pipBottomBoundaryBarrier.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
if (bottom != oldBottom) {
onBarrierBottomChanged(bottom);
}
});
missingPermissionContainer.setVisibility(hasCameraPermission() ? View.GONE : View.VISIBLE);
allowAccessButton.setOnClickListener(v -> {
runIfNonNull(controlsListener, listener -> listener.onVideoChanged(videoToggle.isEnabled()));
});
ConstraintLayout aboveControls = findViewById(R.id.call_controls_floating_parent);
SlideUpWithCallControlsBehavior behavior = (SlideUpWithCallControlsBehavior) ((CoordinatorLayout.LayoutParams) aboveControls.getLayoutParams()).getBehavior();
Objects.requireNonNull(behavior).setOnTopOfControlsChangedListener(topOfControls -> {
pictureInPictureGestureHelper.setBottomVerticalBoundary(topOfControls);
});
}
@Override
@@ -986,11 +983,6 @@ public class WebRtcCallView extends InsetAwareConstraintLayout {
}
public void onControlTopChanged() {
onBarrierBottomChanged(pipBottomBoundaryBarrier.getBottom());
}
private void onBarrierBottomChanged(int barrierBottom) {
pictureInPictureGestureHelper.setBottomVerticalBoundary(barrierBottom);
}
public interface ControlsListener {

View File

@@ -94,6 +94,8 @@ class ControlsAndInfoController(
private val aboveControlsGuideline: Guideline
private val bottomSheetVisibilityListeners = mutableSetOf<BottomSheetVisibilityListener>()
private val scheduleHideControlsRunnable: Runnable = Runnable { onScheduledHide() }
private val toggleCameraDirectionView: View
private val handler: Handler?
get() = webRtcCallView.handler
@@ -110,6 +112,7 @@ class ControlsAndInfoController(
callControls = webRtcCallView.findViewById(R.id.call_controls_constraint_layout)
raiseHandComposeView = webRtcCallView.findViewById(R.id.call_screen_raise_hand_view)
aboveControlsGuideline = webRtcCallView.findViewById(R.id.call_screen_above_controls_guideline)
toggleCameraDirectionView = webRtcCallView.findViewById(R.id.call_screen_camera_direction_toggle)
callInfoComposeView.apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
@@ -321,7 +324,6 @@ class ControlsAndInfoController(
val margin = if (controlState.displaySmallCallButtons()) 4.dp else 8.dp
setControlConstraints(R.id.call_screen_speaker_toggle, controlState.displayAudioToggle(), margin)
setControlConstraints(R.id.call_screen_camera_direction_toggle, controlState.displayCameraToggle(), margin)
setControlConstraints(R.id.call_screen_video_toggle, controlState.displayVideoToggle(), margin)
setControlConstraints(R.id.call_screen_audio_mic_toggle, controlState.displayMuteAudio(), margin)
setControlConstraints(R.id.call_screen_audio_ring_toggle, controlState.displayRingToggle(), margin)
@@ -330,6 +332,8 @@ class ControlsAndInfoController(
}
constraints.applyTo(callControls)
toggleCameraDirectionView.visible = controlState.displayCameraToggle()
}
private fun onScheduledHide() {

View File

@@ -0,0 +1,43 @@
package org.thoughtcrime.securesms.util.views
import android.content.Context
import android.graphics.Rect
import android.util.AttributeSet
import android.view.View
import androidx.annotation.Px
import androidx.coordinatorlayout.widget.CoordinatorLayout
import kotlin.math.min
/**
* @param offsetY - Extra padding between the dependency and child.
* @param maxTranslationY - The maximum offset to apply to child's translationY value. This should be a negative number.
*/
abstract class SlideUpWithDependencyBehavior(
context: Context,
attributeSet: AttributeSet?,
@field:Px @param:Px private val offsetY: Float = 0f
) : CoordinatorLayout.Behavior<View>(context, attributeSet) {
private val rect = Rect()
override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
dependency.getLocalVisibleRect(rect)
val height = if (rect.top < parent.bottom) {
rect.height()
} else {
0
}
val translationY = min(0.0, (dependency.translationY - (height + offsetY)).toDouble()).toFloat()
child.translationY = translationY
return true
}
override fun onDependentViewRemoved(parent: CoordinatorLayout, child: View, dependency: View) {
child.translationY = 0f
}
abstract override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean
}

View File

@@ -1,57 +0,0 @@
package org.thoughtcrime.securesms.util.views;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Dimension;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import com.google.android.material.snackbar.Snackbar;
import org.signal.core.util.DimensionUnit;
public class SlideUpWithSnackbarBehavior extends CoordinatorLayout.Behavior<View> {
@Dimension(unit = Dimension.DP)
private static final float PAD_TOP_OF_SNACKBAR_DP = 16f;
@Px
private final float padTopOfSnackbar = DimensionUnit.DP.toPixels(PAD_TOP_OF_SNACKBAR_DP);
public SlideUpWithSnackbarBehavior(@NonNull Context context, @Nullable AttributeSet attributeSet) {
super(context, attributeSet);
}
@Override
public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent,
@NonNull View child,
@NonNull View dependency)
{
float translationY = Math.min(0, dependency.getTranslationY() - (dependency.getHeight() + padTopOfSnackbar));
child.setTranslationY(translationY);
return true;
}
@Override
public void onDependentViewRemoved(@NonNull CoordinatorLayout parent,
@NonNull View child,
@NonNull View dependency)
{
child.setTranslationY(0);
}
@SuppressLint("RestrictedApi")
@Override
public boolean layoutDependsOn(@NonNull CoordinatorLayout parent,
@NonNull View child,
@NonNull View dependency)
{
return dependency instanceof Snackbar.SnackbarLayout;
}
}

View File

@@ -0,0 +1,20 @@
package org.thoughtcrime.securesms.util.views
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.google.android.material.snackbar.Snackbar.SnackbarLayout
import org.signal.core.util.dp
class SlideUpWithSnackbarBehavior(context: Context, attributeSet: AttributeSet?) : SlideUpWithDependencyBehavior(context, attributeSet, 16f.dp) {
@SuppressLint("RestrictedApi")
override fun layoutDependsOn(
parent: CoordinatorLayout,
child: View,
dependency: View
): Boolean {
return dependency is SnackbarLayout
}
}