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

@ -16,6 +16,10 @@
-keep class androidx.window.** { *; }
-keepclassmembers class * extends androidx.constraintlayout.motion.widget.Key {
public <init>();
}
# AGP generated dont warns
-dontwarn com.android.org.conscrypt.SSLParametersImpl
-dontwarn org.apache.harmony.xnet.provider.jsse.SSLParametersImpl

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
}
}

View file

@ -16,6 +16,84 @@
tools:layout_height="match_parent"
tools:layout_width="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/call_controls_floating_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="org.thoughtcrime.securesms.components.webrtc.SlideUpWithCallControlsBehavior">
<org.thoughtcrime.securesms.components.recyclerview.NoTouchingRecyclerView
android:id="@+id/call_screen_reactions_feed"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:clickable="false"
android:orientation="vertical"
android:scrollbars="none"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:reverseLayout="true"
tools:itemCount="2"
tools:listitem="@layout/webrtc_call_reaction_recycler_item" />
<org.thoughtcrime.securesms.stories.viewer.reply.reaction.MultiReactionBurstLayout
android:id="@+id/call_screen_reactions_container"
android:layout_width="0dp"
android:layout_height="0dp"
android:clickable="false"
android:focusable="false"
app:layout_constraintBottom_toBottomOf="@id/call_screen_reactions_feed"
app:layout_constraintEnd_toEndOf="@id/call_screen_reactions_feed"
app:layout_constraintStart_toStartOf="@id/call_screen_reactions_feed"
app:layout_constraintTop_toTopOf="@id/call_screen_reactions_feed" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/call_screen_raise_hand_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
app:layout_constraintBottom_toTopOf="@id/call_screen_pending_recipients"
app:layout_constraintEnd_toEndOf="parent" />
<ViewStub
android:id="@+id/call_screen_pending_recipients"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="32dp"
android:inflatedId="@+id/call_screen_pending_recipients"
android:layout="@layout/call_screen_pending_participants_view"
app:layout_constraintBottom_toTopOf="@id/call_screen_camera_direction_toggle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="@+id/call_screen_camera_direction_toggle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:clickable="false"
android:contentDescription="@string/WebRtcCallView__toggle_camera_direction"
android:scaleType="fitXY"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/webrtc_call_screen_camera_toggle" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/call_screen_above_controls_barrier"
app:barrierDirection="top"
app:constraint_referenced_ids="call_screen_camera_direction_toggle,call_screen_pending_recipients,call_screen_raise_hand_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</androidx.constraintlayout.widget.ConstraintLayout>
<FrameLayout
android:id="@+id/call_controls_info_parent"
android:layout_width="match_parent"
@ -56,28 +134,12 @@
android:scaleType="fitXY"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/call_screen_start_call_controls"
app:layout_constraintEnd_toStartOf="@id/call_screen_camera_direction_toggle"
app:layout_constraintEnd_toStartOf="@id/call_screen_video_toggle"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/webrtc_call_screen_speaker_toggle"
tools:visibility="visible" />
<ImageView
android:id="@+id/call_screen_camera_direction_toggle"
android:layout_width="@dimen/webrtc_button_size"
android:layout_height="@dimen/webrtc_button_size"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="30dp"
android:clickable="false"
android:contentDescription="@string/WebRtcCallView__toggle_camera_direction"
android:scaleType="fitXY"
app:layout_constraintBottom_toTopOf="@id/call_screen_start_call_controls"
app:layout_constraintEnd_toStartOf="@id/call_screen_video_toggle"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/call_screen_speaker_toggle"
app:srcCompat="@drawable/webrtc_call_screen_camera_toggle" />
<org.thoughtcrime.securesms.components.AccessibleToggleButton
android:id="@+id/call_screen_video_toggle"
style="@style/WebRtcCallV2CompoundButton"
@ -90,7 +152,7 @@
app:layout_constraintBottom_toTopOf="@id/call_screen_start_call_controls"
app:layout_constraintEnd_toStartOf="@id/call_screen_audio_mic_toggle"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/call_screen_camera_direction_toggle"
app:layout_constraintStart_toEndOf="@id/call_screen_speaker_toggle"
tools:checked="true" />
<org.thoughtcrime.securesms.components.AccessibleToggleButton

View file

@ -95,25 +95,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<org.thoughtcrime.securesms.components.recyclerview.NoTouchingRecyclerView
android:id="@+id/call_screen_reactions_feed"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:clickable="false"
android:orientation="vertical"
android:scrollbars="none"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toTopOf="@+id/call_screen_pending_recipients"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:reverseLayout="true"
tools:itemCount="2"
tools:listitem="@layout/webrtc_call_reaction_recycler_item" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/call_screen_show_participants_guideline"
android:layout_width="0dp"
@ -324,17 +305,6 @@
app:layout_constraintStart_toStartOf="parent"
tools:visibility="gone" />
<org.thoughtcrime.securesms.stories.viewer.reply.reaction.MultiReactionBurstLayout
android:id="@+id/call_screen_reactions_container"
android:layout_width="0dp"
android:layout_height="0dp"
android:clickable="false"
android:focusable="false"
app:layout_constraintBottom_toBottomOf="@id/call_screen_reactions_feed"
app:layout_constraintEnd_toEndOf="@id/call_screen_reactions_feed"
app:layout_constraintStart_toStartOf="@id/call_screen_reactions_feed"
app:layout_constraintTop_toTopOf="@id/call_screen_reactions_feed" />
<com.google.android.material.button.MaterialButton
android:id="@+id/call_screen_error_cancel"
style="@style/Widget.Signal.Button.Flat"
@ -365,18 +335,6 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ViewStub
android:id="@+id/call_screen_pending_recipients"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="32dp"
android:inflatedId="@+id/call_screen_pending_recipients"
android:layout="@layout/call_screen_pending_participants_view"
app:layout_constraintBottom_toTopOf="@id/call_screen_above_controls_guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ViewStub
android:id="@+id/call_screen_call_link_warning"
android:layout_width="match_parent"
@ -396,20 +354,5 @@
app:barrierDirection="top"
app:constraint_referenced_ids="call_screen_answer_call,call_screen_decline_call,call_screen_audio_mic_toggle,call_screen_camera_direction_toggle,call_screen_video_toggle,call_screen_answer_without_video,call_screen_speaker_toggle,call_screen_end_call" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/call_screen_raise_hand_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
app:layout_constraintBottom_toTopOf="@id/call_screen_pending_recipients"
app:layout_constraintEnd_toEndOf="parent" />
<include layout="@layout/webrtc_call_controls"/>
<androidx.constraintlayout.widget.Barrier
android:id="@+id/pip_bottom_boundary_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="call_screen_raise_hand_view,call_screen_pending_recipients,call_screen_above_controls_guideline" />
</merge>