Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 312fe058 authored by Miranda Kephart's avatar Miranda Kephart Committed by Android (Google) Code Review
Browse files

Merge "Screenshot swipe-dismiss polish"

parents ec0354cc 2ecc1be9
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -39,5 +39,6 @@
        android:layout_height="match_parent"
        android:visibility="gone"
        android:pointerIcon="crosshair"/>
    <include layout="@layout/global_screenshot_static"/>
    <include layout="@layout/global_screenshot_static"
             android:id="@+id/global_screenshot_static"/>
</com.android.systemui.screenshot.ScreenshotView>
+32 −11
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ import javax.inject.Inject;
 */
public class ScreenshotController {
    private static final String TAG = logTag(ScreenshotController.class);

    /**
     * POD used in the AsyncTask which saves an image in the background.
     */
@@ -350,10 +351,20 @@ public class ScreenshotController {
        // Inflate the screenshot layout
        mScreenshotView = (ScreenshotView)
                LayoutInflater.from(mContext).inflate(R.layout.global_screenshot, null);
        mScreenshotView.init(mUiEventLogger, this::resetScreenshotView);
        mScreenshotView.init(mUiEventLogger, new ScreenshotView.ScreenshotViewCallback() {
            @Override
            public void onUserInteraction() {
                resetTimeout();
            }

            @Override
            public void onDismiss() {
                resetScreenshotView();
            }
        });

        // TODO(159460485): Remove this when focus is handled properly in the system
        mScreenshotView.setOnTouchListener((v, event) -> {
        mDecorView.setOnTouchListener((v, event) -> {
            if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
                if (DEBUG_INPUT) {
                    Log.d(TAG, "onTouch: ACTION_OUTSIDE");
@@ -580,11 +591,8 @@ public class ScreenshotController {
        mSaveInBgTask.execute();
    }

    /**
     * Sets up the action shade and its entrance animation, once we get the screenshot URI.
     */
    private void showUiOnActionsReady(ScreenshotController.SavedImageData imageData) {
        logSuccessOnActionsReady(imageData);
    private void resetTimeout() {
        mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);

        AccessibilityManager accessibilityManager = (AccessibilityManager)
                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
@@ -592,12 +600,25 @@ public class ScreenshotController {
                SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS,
                AccessibilityManager.FLAG_CONTENT_CONTROLS);

        if (DEBUG_UI) {
            Log.d(TAG, "Showing UI actions, dismiss timeout: " + timeoutMs + " ms");
        }
        mScreenshotHandler.sendMessageDelayed(
                mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
                timeoutMs);
        if (DEBUG_UI) {
            Log.d(TAG, "dismiss timeout: " + timeoutMs + " ms");
        }

    }

    /**
     * Sets up the action shade and its entrance animation, once we get the screenshot URI.
     */
    private void showUiOnActionsReady(ScreenshotController.SavedImageData imageData) {
        logSuccessOnActionsReady(imageData);
        if (DEBUG_UI) {
            Log.d(TAG, "Showing UI actions");
        }

        resetTimeout();

        if (imageData.uri != null) {
            mScreenshotHandler.post(() -> {
+92 −29
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ import android.util.MathUtils;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
@@ -83,6 +84,12 @@ import java.util.function.Consumer;
public class ScreenshotView extends FrameLayout implements
        ViewTreeObserver.OnComputeInternalInsetsListener {

    interface ScreenshotViewCallback {
        void onUserInteraction();

        void onDismiss();
    }

    private static final String TAG = logTag(ScreenshotView.class);

    private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133;
@@ -99,6 +106,7 @@ public class ScreenshotView extends FrameLayout implements
    private static final long SCREENSHOT_DISMISS_ALPHA_OFFSET_MS = 50; // delay before starting fade
    private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f;
    private static final float ROUNDED_CORNER_RADIUS = .05f;
    private static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe

    private final Interpolator mAccelerateInterpolator = new AccelerateInterpolator();

@@ -115,6 +123,7 @@ public class ScreenshotView extends FrameLayout implements
    private boolean mDirectionLTR;

    private ScreenshotSelectorView mScreenshotSelectorView;
    private View mScreenshotStatic;
    private ImageView mScreenshotPreview;
    private ImageView mScreenshotFlash;
    private ImageView mActionsContainerBackground;
@@ -127,7 +136,7 @@ public class ScreenshotView extends FrameLayout implements
    private ScreenshotActionChip mScrollChip;

    private UiEventLogger mUiEventLogger;
    private Runnable mOnDismissRunnable;
    private ScreenshotViewCallback mCallbacks;
    private Animator mDismissAnimation;

    private final ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>();
@@ -195,8 +204,10 @@ public class ScreenshotView extends FrameLayout implements

        final Rect tmpRect = new Rect();
        mScreenshotPreview.getBoundsOnScreen(tmpRect);
        tmpRect.inset((int) dpToPx(-SWIPE_PADDING_DP), (int) dpToPx(-SWIPE_PADDING_DP));
        touchRegion.op(tmpRect, Region.Op.UNION);
        mActionsContainer.getBoundsOnScreen(tmpRect);
        mActionsContainerBackground.getBoundsOnScreen(tmpRect);
        tmpRect.inset((int) dpToPx(-SWIPE_PADDING_DP), (int) dpToPx(-SWIPE_PADDING_DP));
        touchRegion.op(tmpRect, Region.Op.UNION);
        mDismissButton.getBoundsOnScreen(tmpRect);
        touchRegion.op(tmpRect, Region.Op.UNION);
@@ -215,7 +226,9 @@ public class ScreenshotView extends FrameLayout implements

    @Override // View
    protected void onFinishInflate() {
        mScreenshotStatic = requireNonNull(findViewById(R.id.global_screenshot_static));
        mScreenshotPreview = requireNonNull(findViewById(R.id.global_screenshot_preview));

        mActionsContainerBackground = requireNonNull(findViewById(
                R.id.global_screenshot_actions_container_background));
        mActionsContainer = requireNonNull(findViewById(R.id.global_screenshot_actions_container));
@@ -238,6 +251,16 @@ public class ScreenshotView extends FrameLayout implements
            }
        });

        int swipePaddingPx = (int) dpToPx(SWIPE_PADDING_DP);
        TouchDelegate previewDelegate = new TouchDelegate(
                new Rect(swipePaddingPx, swipePaddingPx, swipePaddingPx, swipePaddingPx),
                mScreenshotPreview);
        mScreenshotPreview.setTouchDelegate(previewDelegate);
        TouchDelegate actionsDelegate = new TouchDelegate(
                new Rect(swipePaddingPx, swipePaddingPx, swipePaddingPx, swipePaddingPx),
                mActionsContainerBackground);
        mActionsContainerBackground.setTouchDelegate(actionsDelegate);

        setFocusable(true);
        mScreenshotSelectorView.setFocusable(true);
        mScreenshotSelectorView.setFocusableInTouchMode(true);
@@ -272,9 +295,9 @@ public class ScreenshotView extends FrameLayout implements
     * Note: must be called before any other (non-constructor) method or null pointer exceptions
     * may occur.
     */
    void init(UiEventLogger uiEventLogger, Runnable onDismissRunnable) {
    void init(UiEventLogger uiEventLogger, ScreenshotViewCallback callbacks) {
        mUiEventLogger = uiEventLogger;
        mOnDismissRunnable = onDismissRunnable;
        mCallbacks = callbacks;
    }

    void takePartialScreenshot(Consumer<Rect> onPartialScreenshotSelected) {
@@ -414,8 +437,14 @@ public class ScreenshotView extends FrameLayout implements
                mScreenshotPreview.setX(finalPos.x - bounds.width() * cornerScale / 2f);
                mScreenshotPreview.setY(finalPos.y - bounds.height() * cornerScale / 2f);
                requestLayout();
                mScreenshotPreview.setOnTouchListener(new SwipeDismissHandler());

                createScreenshotActionsShadeAnimation().start();

                SwipeDismissHandler swipeDismissHandler = new SwipeDismissHandler();
                mScreenshotPreview.setOnTouchListener(swipeDismissHandler);
                mActionsContainer.setOnTouchListener(swipeDismissHandler);
                mActionsContainerBackground.setOnTouchListener(swipeDismissHandler);
                mBackgroundProtection.setOnTouchListener(swipeDismissHandler);
            }
        });

@@ -544,6 +573,7 @@ public class ScreenshotView extends FrameLayout implements
                            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED);
                            animateDismissal();
                        });
                actionChip.setAlpha(1);
                mActionsView.addView(actionChip);
                mSmartChips.add(actionChip);
            }
@@ -583,7 +613,7 @@ public class ScreenshotView extends FrameLayout implements
                    if (DEBUG_ANIM) {
                        Log.d(TAG, "after dismiss animation, calling onDismissRunnable.run()");
                    }
                    mOnDismissRunnable.run();
                    mCallbacks.onDismiss();
                }
            }
        });
@@ -617,7 +647,7 @@ public class ScreenshotView extends FrameLayout implements
        mDismissButton.setVisibility(View.GONE);
        mScreenshotPreview.setVisibility(View.GONE);
        mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
        mScreenshotPreview.setTranslationX(0);
        mScreenshotStatic.setTranslationX(0);
        mScreenshotPreview.setTranslationY(0);
        mScreenshotPreview.setContentDescription(
                mContext.getResources().getString(R.string.screenshot_preview_description));
@@ -697,6 +727,10 @@ public class ScreenshotView extends FrameLayout implements
        }
    }

    private float dpToPx(float dp) {
        return dp * mDisplayMetrics.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT;
    }

    class SwipeDismissHandler implements OnTouchListener {

        // if distance moved on ACTION_UP is less than this, register a click
@@ -707,11 +741,17 @@ public class ScreenshotView extends FrameLayout implements

        private final GestureDetector mGestureDetector;
        private final float mDismissStartX;
        private final Rect mActionsRect = new Rect();

        private float mStartX;
        private float mStartY;
        private float mTranslationX = 0;

        // tracks whether mStartX has been set for this interaction
        private boolean mInteractionStarted = false;
        // tracks whether we're dragging the UI (as opposed to scrolling the actions bar)
        private boolean mIsDragging = false;

        SwipeDismissHandler() {
            GestureDetector.OnGestureListener gestureListener = new SwipeDismissGestureListener();
            mGestureDetector = new GestureDetector(mContext, gestureListener);
@@ -720,10 +760,16 @@ public class ScreenshotView extends FrameLayout implements

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            boolean gestureResult = mGestureDetector.onTouchEvent(event);
            mCallbacks.onUserInteraction();
            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
                mInteractionStarted = true;
                mStartX = event.getRawX();
                mStartY = event.getRawY();
                return true;
            } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
                mInteractionStarted = false;
                mIsDragging = false;
                if (isPastDismissThreshold()
                        && (mDismissAnimation == null || !mDismissAnimation.isRunning())) {
                    if (DEBUG_INPUT) {
@@ -745,18 +791,45 @@ public class ScreenshotView extends FrameLayout implements
                    return true;
                }
            }
            return mGestureDetector.onTouchEvent(event);
            return gestureResult;
        }

        class SwipeDismissGestureListener extends GestureDetector.SimpleOnGestureListener {

            /**
             * This is somewhat complicated to handle because we want to allow scrolling the actions
             * bar (if it extends off the screen) as well as dismissing the UI horizontally by
             * dragging the actions bar. In addition, we don't get the initial ACTION_DOWN because
             * it is consumed by the action bar view.
             *
             * So, we use a gated system: first, keep track of the pointer location as we move;
             * next, check whether the actions bar can scroll in the direction we're moving in. If
             * it can, let the actions bar handle the event; otherwise, we've gone as far as we can
             * and can start dragging the UI instead.
             */
            @Override
            public boolean onScroll(
                    MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) {
                mTranslationX = ev2.getRawX() - ev1.getRawX();
                mScreenshotPreview.setTranslationX(mTranslationX);
                mDismissButton.setX(mDismissStartX + mTranslationX);
                mActionsContainer.getBoundsOnScreen(mActionsRect);
                if (!mInteractionStarted) {
                    if (mActionsRect.contains((int) ev2.getRawX(), (int) ev2.getRawY())) {
                        mStartX = ev2.getRawX();
                        mInteractionStarted = true;
                    }
                } else {
                    float distance = ev2.getRawX() - mStartX;
                    if ((mIsDragging && distance * mTranslationX > 0)
                            || !mActionsRect.contains((int) ev2.getRawX(), (int) ev2.getRawY())
                            || !mActionsContainer.canScrollHorizontally(-1 * (int) distance)) {
                        mIsDragging = true;
                        mTranslationX = distance;
                        mScreenshotStatic.setTranslationX(mTranslationX);
                        return true;
                    } else {
                        mStartX = ev2.getRawX();
                    }
                }
                return false;
            }
        }

@@ -772,23 +845,18 @@ public class ScreenshotView extends FrameLayout implements
            ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
            float startX = mTranslationX;
            float finalX = mDirectionLTR
                    ? -1 * (mDismissStartX + mDismissButton.getWidth())
                    ? -1 * (mActionsContainerBackground.getX()
                    + mActionsContainerBackground.getWidth())
                    : mDisplayMetrics.widthPixels;

            anim.addUpdateListener(animation -> {
                float translation = MathUtils.lerp(startX, finalX, animation.getAnimatedFraction());
                mScreenshotPreview.setTranslationX(translation);
                mDismissButton.setX(mDismissStartX + translation);

                float yDelta = MathUtils.lerp(0, mDismissDeltaY, animation.getAnimatedFraction());

                mActionsContainer.setTranslationY(yDelta);
                mActionsContainerBackground.setTranslationY(yDelta);
                float translation = MathUtils.lerp(startX, finalX,
                        animation.getAnimatedFraction());
                mScreenshotStatic.setTranslationX(translation);

                setAlpha(1 - animation.getAnimatedFraction());
            });
            anim.setDuration(400);

            return anim;
        }

@@ -801,15 +869,10 @@ public class ScreenshotView extends FrameLayout implements
            anim.addUpdateListener(animation -> {
                float translation = MathUtils.lerp(
                        startX, finalX, animation.getAnimatedFraction());
                mScreenshotPreview.setTranslationX(translation);
                mDismissButton.setX(mDismissStartX + translation);
                mScreenshotStatic.setTranslationX(translation);
            });

            return anim;
        }

        private float dpToPx(float dp) {
            return dp * mDisplayMetrics.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT;
        }
    }
}