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

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

Merge "Add swipe dismissal to screenshots"

parents 234a3749 40d1c0f2
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -60,6 +60,8 @@ public enum ScreenshotEvent implements UiEventLogger.UiEventEnum {
    SCREENSHOT_INTERACTION_TIMEOUT(310),
    @UiEvent(doc = "screenshot explicitly dismissed")
    SCREENSHOT_EXPLICIT_DISMISSAL(311),
    @UiEvent(doc = "screenshot swiped to dismiss")
    SCREENSHOT_SWIPE_DISMISSED(656),
    @UiEvent(doc = "screenshot reentered for new screenshot")
    SCREENSHOT_REENTERED(640);

+119 −2
Original line number Diff line number Diff line
@@ -47,7 +47,9 @@ import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MathUtils;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
@@ -394,6 +396,7 @@ 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();
            }
        });
@@ -534,8 +537,12 @@ public class ScreenshotView extends FrameLayout implements
    }

    void animateDismissal() {
        animateDismissal(createScreenshotDismissAnimation());
    }

    private void animateDismissal(Animator dismissAnimation) {
        getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
        mDismissAnimation = createScreenshotDismissAnimation();
        mDismissAnimation = dismissAnimation;
        mDismissAnimation.addListener(new AnimatorListenerAdapter() {
            private boolean mCancelled = false;

@@ -570,6 +577,8 @@ public class ScreenshotView extends FrameLayout implements
        mDismissButton.setVisibility(View.GONE);
        mScreenshotPreview.setVisibility(View.GONE);
        mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
        mScreenshotPreview.setTranslationX(0);
        mScreenshotPreview.setTranslationY(0);
        mScreenshotPreview.setContentDescription(
                mContext.getResources().getString(R.string.screenshot_preview_description));
        mScreenshotPreview.setOnClickListener(null);
@@ -586,7 +595,6 @@ public class ScreenshotView extends FrameLayout implements
        mDismissButton.setTranslationY(0);
        mActionsContainer.setTranslationY(0);
        mActionsContainerBackground.setTranslationY(0);
        mScreenshotPreview.setTranslationY(0);
        mScreenshotSelectorView.stop();
    }

@@ -650,4 +658,113 @@ public class ScreenshotView extends FrameLayout implements
        }
    }

    class SwipeDismissHandler implements OnTouchListener {

        // if distance moved on ACTION_UP is less than this, register a click
        // otherwise, run return animator
        private static final float CLICK_MOVEMENT_THRESHOLD_DP = 1;
        // distance needed to register a dismissal
        private static final float DISMISS_DISTANCE_THRESHOLD_DP = 30;

        private final GestureDetector mGestureDetector;
        private final float mDismissStartX;

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

        SwipeDismissHandler() {
            GestureDetector.OnGestureListener gestureListener = new SwipeDismissGestureListener();
            mGestureDetector = new GestureDetector(mContext, gestureListener);
            mDismissStartX = mDismissButton.getX();
        }

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
                mStartX = event.getRawX();
                mStartY = event.getRawY();
            } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
                if (isPastDismissThreshold()
                        && (mDismissAnimation == null || !mDismissAnimation.isRunning())) {
                    mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED);
                    animateDismissal(createSwipeDismissAnimation());
                    return true;
                } else if (MathUtils.dist(mStartX, mStartY, event.getRawX(), event.getRawY())
                        > dpToPx(CLICK_MOVEMENT_THRESHOLD_DP)) {
                    // if we've moved a non-negligible distance (but not past the threshold),
                    // start the return animation
                    if ((mDismissAnimation == null || !mDismissAnimation.isRunning())) {
                        createSwipeReturnAnimation().start();
                    }
                    return true;
                }
            }
            return mGestureDetector.onTouchEvent(event);
        }

        class SwipeDismissGestureListener extends GestureDetector.SimpleOnGestureListener {

            @Override
            public boolean onScroll(
                    MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) {
                mTranslationX = ev2.getRawX() - ev1.getRawX();
                mScreenshotPreview.setTranslationX(mTranslationX);
                mDismissButton.setX(mDismissStartX + mTranslationX);
                return true;
            }
        }

        private boolean isPastDismissThreshold() {
            if (mDirectionLTR) {
                return mTranslationX <= -1 * dpToPx(DISMISS_DISTANCE_THRESHOLD_DP);
            } else {
                return mTranslationX >= dpToPx(DISMISS_DISTANCE_THRESHOLD_DP);
            }
        }

        private ValueAnimator createSwipeDismissAnimation() {
            ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
            float startX = mTranslationX;
            float finalX = mDirectionLTR
                    ? -1 * (mDismissStartX + mDismissButton.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);

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

            return anim;
        }

        private ValueAnimator createSwipeReturnAnimation() {
            ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
            float startX = mTranslationX;
            float finalX = 0;
            mTranslationX = 0;

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

            return anim;
        }

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