summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/res/layout/clipboard_overlay.xml4
-rw-r--r--packages/SystemUI/res/layout/screenshot_static.xml14
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java163
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java354
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java237
7 files changed, 416 insertions, 459 deletions
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index c58c00114262..27823009459c 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -26,7 +26,7 @@
android:layout_width="match_parent"
android:layout_gravity="bottom"
android:src="@drawable/overlay_actions_background_protection"/>
- <com.android.systemui.clipboardoverlay.DraggableConstraintLayout
+ <com.android.systemui.screenshot.DraggableConstraintLayout
android:id="@+id/clipboard_ui"
android:theme="@style/FloatingOverlay"
android:layout_width="match_parent"
@@ -146,5 +146,5 @@
android:layout_margin="@dimen/overlay_dismiss_button_margin"
android:src="@drawable/overlay_cancel"/>
</FrameLayout>
- </com.android.systemui.clipboardoverlay.DraggableConstraintLayout>
+ </com.android.systemui.screenshot.DraggableConstraintLayout>
</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml
index 813bb6018801..8de80844d784 100644
--- a/packages/SystemUI/res/layout/screenshot_static.xml
+++ b/packages/SystemUI/res/layout/screenshot_static.xml
@@ -14,25 +14,25 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<androidx.constraintlayout.widget.ConstraintLayout
+<com.android.systemui.screenshot.DraggableConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
- android:id="@+id/screenshot_actions_container_background"
+ android:id="@+id/actions_container_background"
android:visibility="gone"
android:layout_height="0dp"
android:layout_width="0dp"
android:elevation="1dp"
android:background="@drawable/action_chip_container_background"
android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
- app:layout_constraintBottom_toBottomOf="@+id/screenshot_actions_container"
+ app:layout_constraintBottom_toBottomOf="@+id/actions_container"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="@+id/screenshot_actions_container"
- app:layout_constraintEnd_toEndOf="@+id/screenshot_actions_container"/>
+ app:layout_constraintTop_toTopOf="@+id/actions_container"
+ app:layout_constraintEnd_toEndOf="@+id/actions_container"/>
<HorizontalScrollView
- android:id="@+id/screenshot_actions_container"
+ android:id="@+id/actions_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal"
@@ -130,4 +130,4 @@
app:layout_constraintStart_toStartOf="@id/screenshot_preview"
app:layout_constraintTop_toTopOf="@id/screenshot_preview"
android:elevation="@dimen/overlay_preview_elevation"/>
-</androidx.constraintlayout.widget.ConstraintLayout>
+</com.android.systemui.screenshot.DraggableConstraintLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 56e4d7ec1882..0f937d163f5c 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -73,6 +73,7 @@ import android.widget.TextView;
import com.android.internal.policy.PhoneWindow;
import com.android.systemui.R;
+import com.android.systemui.screenshot.DraggableConstraintLayout;
import com.android.systemui.screenshot.FloatingWindowUtil;
import com.android.systemui.screenshot.OverlayActionChip;
import com.android.systemui.screenshot.TimeoutHandler;
@@ -166,8 +167,28 @@ public class ClipboardOverlayController {
mRemoteCopyChip = requireNonNull(mView.findViewById(R.id.remote_copy_chip));
mDismissButton = requireNonNull(mView.findViewById(R.id.dismiss_button));
- mView.setOnDismissEndCallback(this::hideImmediate);
- mView.setOnInteractionCallback(mTimeoutHandler::resetTimeout);
+ mView.setCallbacks(new DraggableConstraintLayout.SwipeDismissCallbacks() {
+ @Override
+ public void onInteraction() {
+ mTimeoutHandler.resetTimeout();
+ }
+
+ @Override
+ public void onSwipeDismissInitiated(Animator animator) {
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ mContainer.animate().alpha(0).start();
+ }
+ });
+ }
+
+ @Override
+ public void onDismissComplete() {
+ hideImmediate();
+ }
+ });
mDismissButton.setOnClickListener(view -> animateOut());
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java
deleted file mode 100644
index 57dc1620a410..000000000000
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.clipboardoverlay;
-
-import android.animation.Animator;
-import android.content.Context;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.util.AttributeSet;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewTreeObserver;
-
-import androidx.constraintlayout.widget.ConstraintLayout;
-
-import com.android.systemui.R;
-import com.android.systemui.screenshot.SwipeDismissHandler;
-
-import java.util.function.Consumer;
-
-/**
- * ConstraintLayout that is draggable when touched in a specific region
- */
-public class DraggableConstraintLayout extends ConstraintLayout
- implements ViewTreeObserver.OnComputeInternalInsetsListener {
- private final SwipeDismissHandler mSwipeDismissHandler;
- private final GestureDetector mSwipeDetector;
- private Consumer<Animator> mOnDismissInitiated;
- private Runnable mOnDismissComplete;
- private Runnable mOnInteraction;
-
- public DraggableConstraintLayout(Context context) {
- this(context, null);
- }
-
- public DraggableConstraintLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public DraggableConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- mSwipeDismissHandler = new SwipeDismissHandler(mContext, this,
- new SwipeDismissHandler.SwipeDismissCallbacks() {
- @Override
- public void onInteraction() {
- if (mOnInteraction != null) {
- mOnInteraction.run();
- }
- }
-
- @Override
- public void onSwipeDismissInitiated(Animator animator) {
- if (mOnDismissInitiated != null) {
- mOnDismissInitiated.accept(animator);
- }
- }
-
- @Override
- public void onDismissComplete() {
- if (mOnDismissComplete != null) {
- mOnDismissComplete.run();
- }
- }
- });
- setOnTouchListener(mSwipeDismissHandler);
-
- mSwipeDetector = new GestureDetector(mContext,
- new GestureDetector.SimpleOnGestureListener() {
- final Rect mActionsRect = new Rect();
-
- @Override
- public boolean onScroll(
- MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) {
- View actionsContainer = findViewById(R.id.actions_container);
- actionsContainer.getBoundsOnScreen(mActionsRect);
- // return true if we aren't in the actions bar, or if we are but it isn't
- // scrollable in the direction of movement
- return !mActionsRect.contains((int) ev2.getRawX(), (int) ev2.getRawY())
- || !actionsContainer.canScrollHorizontally((int) distanceX);
- }
- });
- mSwipeDetector.setIsLongpressEnabled(false);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mSwipeDismissHandler.onTouch(this, ev);
- }
-
- return mSwipeDetector.onTouchEvent(ev);
- }
-
- /**
- * Dismiss the view, with animation controlled by SwipeDismissHandler
- */
- public void dismiss() {
- mSwipeDismissHandler.dismiss();
- }
-
- /**
- * Set the callback to be run after view is dismissed (before animation; receives animator as
- * input)
- */
- public void setOnDismissStartCallback(Consumer<Animator> callback) {
- mOnDismissInitiated = callback;
- }
-
- /**
- * Set the callback to be run after view is dismissed
- */
- public void setOnDismissEndCallback(Runnable callback) {
- mOnDismissComplete = callback;
- }
-
- /**
- * Set the callback to be run when the view is interacted with (e.g. tapped)
- */
- public void setOnInteractionCallback(Runnable callback) {
- mOnInteraction = callback;
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- getViewTreeObserver().addOnComputeInternalInsetsListener(this);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
- }
-
- @Override
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
- // Only child views are touchable.
- Region r = new Region();
- Rect rect = new Rect();
- for (int i = 0; i < getChildCount(); i++) {
- getChildAt(i).getGlobalVisibleRect(rect);
- r.op(rect, Region.Op.UNION);
- }
- inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- inoutInfo.touchableRegion.set(r);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java b/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java
new file mode 100644
index 000000000000..0b987677eac9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.systemui.R;
+
+/**
+ * ConstraintLayout that is draggable when touched in a specific region
+ */
+public class DraggableConstraintLayout extends ConstraintLayout
+ implements ViewTreeObserver.OnComputeInternalInsetsListener {
+
+ private final SwipeDismissHandler mSwipeDismissHandler;
+ private final GestureDetector mSwipeDetector;
+ private View mActionsContainer;
+ private SwipeDismissCallbacks mCallbacks;
+
+ /**
+ * Stores the callbacks when the view is interacted with or dismissed.
+ */
+ public interface SwipeDismissCallbacks {
+ /**
+ * Run when the view is interacted with (touched)
+ */
+ default void onInteraction() {
+
+ }
+
+ /**
+ * Run when the view is dismissed (the distance threshold is met), pre-dismissal animation
+ */
+ default void onSwipeDismissInitiated(Animator animator) {
+
+ }
+
+ /**
+ * Run when the view is dismissed (the distance threshold is met), post-dismissal animation
+ */
+ default void onDismissComplete() {
+
+ }
+ }
+
+ public DraggableConstraintLayout(Context context) {
+ this(context, null);
+ }
+
+ public DraggableConstraintLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DraggableConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ mSwipeDismissHandler = new SwipeDismissHandler(mContext, this);
+ setOnTouchListener(mSwipeDismissHandler);
+
+ mSwipeDetector = new GestureDetector(mContext,
+ new GestureDetector.SimpleOnGestureListener() {
+ final Rect mActionsRect = new Rect();
+
+ @Override
+ public boolean onScroll(
+ MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) {
+ mActionsContainer.getBoundsOnScreen(mActionsRect);
+ // return true if we aren't in the actions bar, or if we are but it isn't
+ // scrollable in the direction of movement
+ return !mActionsRect.contains((int) ev2.getRawX(), (int) ev2.getRawY())
+ || !mActionsContainer.canScrollHorizontally((int) distanceX);
+ }
+ });
+ mSwipeDetector.setIsLongpressEnabled(false);
+ }
+
+ public void setCallbacks(SwipeDismissCallbacks callbacks) {
+ mCallbacks = callbacks;
+ }
+
+ @Override // View
+ protected void onFinishInflate() {
+ mActionsContainer = findViewById(R.id.actions_container_background);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mSwipeDismissHandler.onTouch(this, ev);
+ }
+ return mSwipeDetector.onTouchEvent(ev);
+ }
+
+ public int getVisibleRight() {
+ return mActionsContainer.getRight();
+ }
+
+ /**
+ * Cancel current dismissal animation, if any
+ */
+ public void cancelDismissal() {
+ mSwipeDismissHandler.cancel();
+ }
+
+ /**
+ * Return whether the view is currently dismissing
+ */
+ public boolean isDismissing() {
+ return mSwipeDismissHandler.isDismissing();
+ }
+
+ /**
+ * Dismiss the view, with animation controlled by SwipeDismissHandler
+ */
+ public void dismiss() {
+ mSwipeDismissHandler.dismiss();
+ }
+
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+ }
+
+ @Override
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
+ // Only child views are touchable.
+ Region r = new Region();
+ Rect rect = new Rect();
+ for (int i = 0; i < getChildCount(); i++) {
+ getChildAt(i).getGlobalVisibleRect(rect);
+ r.op(rect, Region.Op.UNION);
+ }
+ inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ inoutInfo.touchableRegion.set(r);
+ }
+
+ /**
+ * Allows a view to be swipe-dismissed, or returned to its location if distance threshold is not
+ * met
+ */
+ private class SwipeDismissHandler implements OnTouchListener {
+ private static final String TAG = "SwipeDismissHandler";
+
+ // distance needed to register a dismissal
+ private static final float DISMISS_DISTANCE_THRESHOLD_DP = 20;
+
+ private final DraggableConstraintLayout mView;
+ private final GestureDetector mGestureDetector;
+ private final DisplayMetrics mDisplayMetrics;
+ private ValueAnimator mDismissAnimation;
+
+ private float mStartX;
+ // Keeps track of the most recent direction (between the last two move events).
+ // -1 for left; +1 for right.
+ private int mDirectionX;
+ private float mPreviousX;
+
+ SwipeDismissHandler(Context context, DraggableConstraintLayout view) {
+ mView = view;
+ GestureDetector.OnGestureListener gestureListener = new SwipeDismissGestureListener();
+ mGestureDetector = new GestureDetector(context, gestureListener);
+ mDisplayMetrics = new DisplayMetrics();
+ context.getDisplay().getRealMetrics(mDisplayMetrics);
+ mCallbacks = new SwipeDismissCallbacks() {
+ }; // default to unimplemented callbacks
+ }
+
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ boolean gestureResult = mGestureDetector.onTouchEvent(event);
+ mCallbacks.onInteraction();
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mStartX = event.getRawX();
+ mPreviousX = mStartX;
+ return true;
+ } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
+ return true;
+ }
+ if (isPastDismissThreshold()) {
+ dismiss();
+ } else {
+ // if we've moved, but not past the threshold, start the return animation
+ if (DEBUG_DISMISS) {
+ Log.d(TAG, "swipe gesture abandoned");
+ }
+ createSwipeReturnAnimation().start();
+ }
+ return true;
+ }
+ return gestureResult;
+ }
+
+ class SwipeDismissGestureListener extends GestureDetector.SimpleOnGestureListener {
+ @Override
+ public boolean onScroll(
+ MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) {
+ mView.setTranslationX(ev2.getRawX() - mStartX);
+ mDirectionX = (ev2.getRawX() < mPreviousX) ? -1 : 1;
+ mPreviousX = ev2.getRawX();
+ return true;
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ float velocityY) {
+ if (mView.getTranslationX() * velocityX > 0
+ && (mDismissAnimation == null || !mDismissAnimation.isRunning())) {
+ ValueAnimator dismissAnimator =
+ createSwipeDismissAnimation(velocityX / (float) 1000);
+ mCallbacks.onSwipeDismissInitiated(dismissAnimator);
+ dismiss(dismissAnimator);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private boolean isPastDismissThreshold() {
+ float translationX = mView.getTranslationX();
+ // Determines whether the absolute translation from the start is in the same direction
+ // as the current movement. For example, if the user moves most of the way to the right,
+ // but then starts dragging back left, we do not dismiss even though the absolute
+ // distance is greater than the threshold.
+ if (translationX * mDirectionX > 0) {
+ return Math.abs(translationX) >= FloatingWindowUtil.dpToPx(mDisplayMetrics,
+ DISMISS_DISTANCE_THRESHOLD_DP);
+ }
+ return false;
+ }
+
+ boolean isDismissing() {
+ return (mDismissAnimation != null && mDismissAnimation.isRunning());
+ }
+
+ void cancel() {
+ if (isDismissing()) {
+ if (DEBUG_ANIM) {
+ Log.d(TAG, "cancelling dismiss animation");
+ }
+ mDismissAnimation.cancel();
+ }
+ }
+
+ void dismiss() {
+ ValueAnimator anim = createSwipeDismissAnimation(3);
+ mCallbacks.onSwipeDismissInitiated(anim);
+ dismiss(anim);
+ }
+
+ private void dismiss(ValueAnimator animator) {
+ mDismissAnimation = animator;
+ mDismissAnimation.addListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ if (!mCancelled) {
+ mCallbacks.onDismissComplete();
+ }
+ }
+ });
+ mDismissAnimation.start();
+ }
+
+ private ValueAnimator createSwipeDismissAnimation(float velocity) {
+ // velocity is measured in pixels per millisecond
+ velocity = Math.min(3, Math.max(1, velocity));
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+ float startX = mView.getTranslationX();
+ // make sure the UI gets all the way off the screen in the direction of movement
+ // (the actions container background is guaranteed to be both the leftmost and
+ // rightmost UI element in LTR and RTL)
+ float finalX;
+ int layoutDir =
+ mView.getContext().getResources().getConfiguration().getLayoutDirection();
+ if (startX > 0 || (startX == 0 && layoutDir == LAYOUT_DIRECTION_RTL)) {
+ finalX = mDisplayMetrics.widthPixels;
+ } else {
+ finalX = -1 * mActionsContainer.getRight();
+ }
+ float distance = Math.abs(finalX - startX);
+
+ anim.addUpdateListener(animation -> {
+ float translation = MathUtils.lerp(startX, finalX, animation.getAnimatedFraction());
+ mView.setTranslationX(translation);
+ mView.setAlpha(1 - animation.getAnimatedFraction());
+ });
+ anim.setDuration((long) (distance / Math.abs(velocity)));
+ return anim;
+ }
+
+ private ValueAnimator createSwipeReturnAnimation() {
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+ float startX = mView.getTranslationX();
+ float finalX = 0;
+
+ anim.addUpdateListener(animation -> {
+ float translation = MathUtils.lerp(
+ startX, finalX, animation.getAnimatedFraction());
+ mView.setTranslationX(translation);
+ });
+
+ return anim;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 6d729b92e7b8..6af6e36a75f7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -141,7 +141,7 @@ public class ScreenshotView extends FrameLayout implements
private ScreenshotSelectorView mScreenshotSelectorView;
private ImageView mScrollingScrim;
- private View mScreenshotStatic;
+ private DraggableConstraintLayout mScreenshotStatic;
private ImageView mScreenshotPreview;
private View mScreenshotPreviewBorder;
private ImageView mScrollablePreview;
@@ -159,7 +159,6 @@ public class ScreenshotView extends FrameLayout implements
private UiEventLogger mUiEventLogger;
private ScreenshotViewCallback mCallbacks;
private boolean mPendingSharedTransition;
- private SwipeDismissHandler mSwipeDismissHandler;
private InputMonitorCompat mInputMonitor;
private InputChannelCompat.InputEventReceiver mInputEventReceiver;
private boolean mShowScrollablePreview;
@@ -332,19 +331,6 @@ public class ScreenshotView extends FrameLayout implements
}
}
- @Override // ViewGroup
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- // scrolling scrim should not be swipeable; return early if we're on the scrim
- if (!getSwipeRegion().contains((int) ev.getRawX(), (int) ev.getRawY())) {
- return false;
- }
- // always pass through the down event so the swipe handler knows the initial state
- if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mSwipeDismissHandler.onTouch(this, ev);
- }
- return mSwipeDetector.onTouchEvent(ev);
- }
-
@Override // View
protected void onFinishInflate() {
mScrollingScrim = requireNonNull(findViewById(R.id.screenshot_scrolling_scrim));
@@ -356,8 +342,8 @@ public class ScreenshotView extends FrameLayout implements
mScreenshotPreview.setClipToOutline(true);
mActionsContainerBackground = requireNonNull(findViewById(
- R.id.screenshot_actions_container_background));
- mActionsContainer = requireNonNull(findViewById(R.id.screenshot_actions_container));
+ R.id.actions_container_background));
+ mActionsContainer = requireNonNull(findViewById(R.id.actions_container));
mActionsView = requireNonNull(findViewById(R.id.screenshot_actions));
mBackgroundProtection = requireNonNull(
findViewById(R.id.screenshot_actions_background));
@@ -395,35 +381,34 @@ public class ScreenshotView extends FrameLayout implements
setFocusableInTouchMode(true);
requestFocus();
- mSwipeDismissHandler = new SwipeDismissHandler(mContext, mScreenshotStatic,
- new SwipeDismissHandler.SwipeDismissCallbacks() {
- @Override
- public void onInteraction() {
- mCallbacks.onUserInteraction();
- }
-
- @Override
- public void onSwipeDismissInitiated(Animator anim) {
- if (DEBUG_DISMISS) {
- Log.d(ScreenshotView.TAG, "dismiss triggered via swipe gesture");
- }
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED, 0,
- mPackageName);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- super.onAnimationStart(animation);
- mBackgroundProtection.animate()
- .alpha(0).setDuration(anim.getDuration()).start();
- }
- });
- }
+ mScreenshotStatic.setCallbacks(new DraggableConstraintLayout.SwipeDismissCallbacks() {
+ @Override
+ public void onInteraction() {
+ mCallbacks.onUserInteraction();
+ }
+ @Override
+ public void onSwipeDismissInitiated(Animator animator) {
+ if (DEBUG_DISMISS) {
+ Log.d(ScreenshotView.TAG, "dismiss triggered via swipe gesture");
+ }
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED, 0,
+ mPackageName);
+ animator.addListener(new AnimatorListenerAdapter() {
@Override
- public void onDismissComplete() {
- mCallbacks.onDismiss();
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ mBackgroundProtection.animate()
+ .alpha(0).setDuration(animation.getDuration()).start();
}
});
+ }
+
+ @Override
+ public void onDismissComplete() {
+ mCallbacks.onDismiss();
+ }
+ });
}
View getScreenshotPreview() {
@@ -648,8 +633,6 @@ public class ScreenshotView extends FrameLayout implements
requestLayout();
createScreenshotActionsShadeAnimation().start();
-
- setOnTouchListener(mSwipeDismissHandler);
}
});
@@ -958,7 +941,7 @@ public class ScreenshotView extends FrameLayout implements
}
boolean isDismissing() {
- return mSwipeDismissHandler.isDismissing();
+ return mScreenshotStatic.isDismissing();
}
boolean isPendingSharedTransition() {
@@ -966,15 +949,14 @@ public class ScreenshotView extends FrameLayout implements
}
void animateDismissal() {
- mSwipeDismissHandler.dismiss();
+ mScreenshotStatic.dismiss();
}
void reset() {
if (DEBUG_UI) {
Log.d(TAG, "reset screenshot view");
}
-
- mSwipeDismissHandler.cancel();
+ mScreenshotStatic.cancelDismissal();
if (DEBUG_WINDOW) {
Log.d(TAG, "removing OnComputeInternalInsetsListener");
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java b/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java
deleted file mode 100644
index 24b1249c3f98..000000000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.screenshot;
-
-import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM;
-import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.MathUtils;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.View;
-
-/**
- * Allows a view to be swipe-dismissed, or returned to its location if distance threshold is not met
- */
-public class SwipeDismissHandler implements View.OnTouchListener {
- private static final String TAG = "SwipeDismissHandler";
-
- // distance needed to register a dismissal
- private static final float DISMISS_DISTANCE_THRESHOLD_DP = 20;
-
- /**
- * Stores the callbacks when the view is interacted with or dismissed.
- */
- public interface SwipeDismissCallbacks {
- /**
- * Run when the view is interacted with (touched)
- */
- void onInteraction();
-
- /**
- * Run when the view is dismissed (the distance threshold is met), pre-dismissal animation
- */
- void onSwipeDismissInitiated(Animator animator);
-
- /**
- * Run when the view is dismissed (the distance threshold is met), post-dismissal animation
- */
- void onDismissComplete();
- }
-
- private final View mView;
- private final SwipeDismissCallbacks mCallbacks;
- private final GestureDetector mGestureDetector;
- private DisplayMetrics mDisplayMetrics;
- private ValueAnimator mDismissAnimation;
-
-
- private float mStartX;
- // Keeps track of the most recent direction (between the last two move events).
- // -1 for left; +1 for right.
- private int mDirectionX;
- private float mPreviousX;
-
- public SwipeDismissHandler(Context context, View view, SwipeDismissCallbacks callbacks) {
- mView = view;
- mCallbacks = callbacks;
- GestureDetector.OnGestureListener gestureListener = new SwipeDismissGestureListener();
- mGestureDetector = new GestureDetector(context, gestureListener);
- mDisplayMetrics = new DisplayMetrics();
- context.getDisplay().getRealMetrics(mDisplayMetrics);
- }
-
- @Override
- public boolean onTouch(View view, MotionEvent event) {
- boolean gestureResult = mGestureDetector.onTouchEvent(event);
- mCallbacks.onInteraction();
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mStartX = event.getRawX();
- mPreviousX = mStartX;
- return true;
- } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
- return true;
- }
- if (isPastDismissThreshold()) {
- ValueAnimator dismissAnimator = createSwipeDismissAnimation(1);
- mCallbacks.onSwipeDismissInitiated(dismissAnimator);
- dismiss(dismissAnimator);
- } else {
- // if we've moved, but not past the threshold, start the return animation
- if (DEBUG_DISMISS) {
- Log.d(TAG, "swipe gesture abandoned");
- }
- createSwipeReturnAnimation().start();
- }
- return true;
- }
- return gestureResult;
- }
-
- class SwipeDismissGestureListener extends GestureDetector.SimpleOnGestureListener {
- @Override
- public boolean onScroll(
- MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) {
- mView.setTranslationX(ev2.getRawX() - mStartX);
- mDirectionX = (ev2.getRawX() < mPreviousX) ? -1 : 1;
- mPreviousX = ev2.getRawX();
- return true;
- }
-
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
- float velocityY) {
- if (mView.getTranslationX() * velocityX > 0
- && (mDismissAnimation == null || !mDismissAnimation.isRunning())) {
- ValueAnimator dismissAnimator =
- createSwipeDismissAnimation(velocityX / (float) 1000);
- mCallbacks.onSwipeDismissInitiated(dismissAnimator);
- dismiss(dismissAnimator);
- return true;
- }
- return false;
- }
- }
-
- private boolean isPastDismissThreshold() {
- float translationX = mView.getTranslationX();
- // Determines whether the absolute translation from the start is in the same direction
- // as the current movement. For example, if the user moves most of the way to the right,
- // but then starts dragging back left, we do not dismiss even though the absolute
- // distance is greater than the threshold.
- if (translationX * mDirectionX > 0) {
- return Math.abs(translationX) >= FloatingWindowUtil.dpToPx(mDisplayMetrics,
- DISMISS_DISTANCE_THRESHOLD_DP);
- }
- return false;
- }
-
- /**
- * Return whether the view is currently being dismissed
- */
- public boolean isDismissing() {
- return (mDismissAnimation != null && mDismissAnimation.isRunning());
- }
-
- /**
- * Cancel the currently-running dismissal animation, if any.
- */
- public void cancel() {
- if (isDismissing()) {
- if (DEBUG_ANIM) {
- Log.d(TAG, "cancelling dismiss animation");
- }
- mDismissAnimation.cancel();
- }
- }
-
- /**
- * Start dismissal animation (will run onDismiss callback when animation complete)
- */
- public void dismiss() {
- dismiss(createSwipeDismissAnimation(1));
- }
-
- private void dismiss(ValueAnimator animator) {
- mDismissAnimation = animator;
- mDismissAnimation.addListener(new AnimatorListenerAdapter() {
- private boolean mCancelled;
-
- @Override
- public void onAnimationCancel(Animator animation) {
- super.onAnimationCancel(animation);
- mCancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- if (!mCancelled) {
- mCallbacks.onDismissComplete();
- }
- }
- });
- mDismissAnimation.start();
- }
-
- private ValueAnimator createSwipeDismissAnimation(float velocity) {
- // velocity is measured in pixels per millisecond
- velocity = Math.min(3, Math.max(1, velocity));
- ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
- float startX = mView.getTranslationX();
- // make sure the UI gets all the way off the screen in the direction of movement
- // (the actions container background is guaranteed to be both the leftmost and
- // rightmost UI element in LTR and RTL)
- float finalX;
- int layoutDir = mView.getContext().getResources().getConfiguration().getLayoutDirection();
- if (startX > 0 || (startX == 0 && layoutDir == View.LAYOUT_DIRECTION_RTL)) {
- finalX = mDisplayMetrics.widthPixels;
- } else {
- finalX = -1 * mView.getRight();
- }
- float distance = Math.abs(finalX - startX);
-
- anim.addUpdateListener(animation -> {
- float translation = MathUtils.lerp(startX, finalX, animation.getAnimatedFraction());
- mView.setTranslationX(translation);
- mView.setAlpha(1 - animation.getAnimatedFraction());
- });
- anim.setDuration((long) (distance / Math.abs(velocity)));
- return anim;
- }
-
- private ValueAnimator createSwipeReturnAnimation() {
- ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
- float startX = mView.getTranslationX();
- float finalX = 0;
-
- anim.addUpdateListener(animation -> {
- float translation = MathUtils.lerp(
- startX, finalX, animation.getAnimatedFraction());
- mView.setTranslationX(translation);
- });
-
- return anim;
- }
-}