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

Commit 06d74157 authored by Zak Cohen's avatar Zak Cohen Committed by Android (Google) Code Review
Browse files

Merge "Add visual indicator for back navigation gesture."

parents 156ac1d7 8d8d6776
Loading
Loading
Loading
Loading
+221 −2
Original line number Original line Diff line number Diff line
@@ -16,9 +16,17 @@


package com.android.systemui.statusbar.phone;
package com.android.systemui.statusbar.phone;


import android.animation.ObjectAnimator;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.content.Context;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PixelFormat;
import android.util.FloatProperty;
import android.util.MathUtils;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager;


@@ -27,6 +35,71 @@ import com.android.systemui.R;
public class NavigationBarEdgePanel extends View {
public class NavigationBarEdgePanel extends View {
    private static final String TAG = "NavigationBarEdgePanel";
    private static final String TAG = "NavigationBarEdgePanel";


    // TODO: read from resources once drawing is finalized.
    private static final boolean SHOW_PROTECTION_STROKE = true;
    private static final int PROTECTION_COLOR = 0xffc0c0c0;
    private static final int STROKE_COLOR = 0xffe5e5e5;
    private static final int PROTECTION_WIDTH_PX = 4;
    private static final int BASE_EXTENT = 32;
    private static final int ARROW_HEIGHT_DP = 32;
    private static final int POINT_EXTENT_DP = 8;
    private static final int ARROW_THICKNESS_DP = 4;
    private static final float TRACK_LENGTH_MULTIPLIER = 1.5f;
    private static final float START_POINTING_RATIO = 0.3f;
    private static final float POINTEDNESS_BEFORE_SNAP_RATIO = 0.4f;
    private static final int ANIM_DURATION_MS = 150;

    private final Paint mPaint = new Paint();
    private final Paint mProtectionPaint = new Paint();

    private final ObjectAnimator mEndAnimator;
    private final ObjectAnimator mLegAnimator;

    private final float mDensity;
    private final float mBaseExtent;
    private final float mPointExtent;
    private final float mHeight;
    private final float mStrokeThickness;
    private final boolean mIsLeftPanel;

    private float mStartY;
    private float mStartX;

    private boolean mGestureDetected;
    private boolean mArrowsPointLeft;
    private float mGestureLength;
    private float mLegProgress;
    private float mDragProgress;

    // How much the "legs" of the back arrow have proceeded from being a line to an arrow.
    private static final FloatProperty<NavigationBarEdgePanel> LEG_PROGRESS =
            new FloatProperty<NavigationBarEdgePanel>("legProgress") {
        @Override
        public void setValue(NavigationBarEdgePanel object, float value) {
            object.setLegProgress(value);
        }

        @Override
        public Float get(NavigationBarEdgePanel object) {
            return object.getLegProgress();
        }
    };

    // How far across the view the arrow should be drawn.
    private static final FloatProperty<NavigationBarEdgePanel> DRAG_PROGRESS =
            new FloatProperty<NavigationBarEdgePanel>("dragProgress") {

                @Override
                public void setValue(NavigationBarEdgePanel object, float value) {
                    object.setDragProgress(value);
                }

                @Override
                public Float get(NavigationBarEdgePanel object) {
                    return object.getDragProgress();
                }
            };

    public static NavigationBarEdgePanel create(@NonNull Context context, int width, int height,
    public static NavigationBarEdgePanel create(@NonNull Context context, int width, int height,
            int gravity) {
            int gravity) {
        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height,
        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height,
@@ -40,13 +113,43 @@ public class NavigationBarEdgePanel extends View {
        lp.setTitle(TAG + context.getDisplayId());
        lp.setTitle(TAG + context.getDisplayId());
        lp.accessibilityTitle = context.getString(R.string.nav_bar_edge_panel);
        lp.accessibilityTitle = context.getString(R.string.nav_bar_edge_panel);
        lp.windowAnimations = 0;
        lp.windowAnimations = 0;
        NavigationBarEdgePanel panel = new NavigationBarEdgePanel(context);
        NavigationBarEdgePanel panel = new NavigationBarEdgePanel(
                context, (gravity & Gravity.LEFT) != 0);
        panel.setLayoutParams(lp);
        panel.setLayoutParams(lp);
        return panel;
        return panel;
    }
    }


    private NavigationBarEdgePanel(Context context) {
    private NavigationBarEdgePanel(Context context, boolean isLeftPanel) {
        super(context);
        super(context);

        mEndAnimator = ObjectAnimator.ofFloat(this, DRAG_PROGRESS, 1f);
        mEndAnimator.setAutoCancel(true);
        mEndAnimator.setDuration(ANIM_DURATION_MS);

        mLegAnimator = ObjectAnimator.ofFloat(this, LEG_PROGRESS, 1f);
        mLegAnimator.setAutoCancel(true);
        mLegAnimator.setDuration(ANIM_DURATION_MS);

        mDensity = context.getResources().getDisplayMetrics().density;

        mBaseExtent = dp(BASE_EXTENT);
        mHeight = dp(ARROW_HEIGHT_DP);
        mPointExtent = dp(POINT_EXTENT_DP);
        mStrokeThickness = dp(ARROW_THICKNESS_DP);

        mPaint.setStrokeWidth(mStrokeThickness);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setColor(STROKE_COLOR);
        mPaint.setAntiAlias(true);

        mProtectionPaint.setStrokeWidth(mStrokeThickness + PROTECTION_WIDTH_PX);
        mProtectionPaint.setStrokeCap(Paint.Cap.ROUND);
        mProtectionPaint.setColor(PROTECTION_COLOR);
        mProtectionPaint.setAntiAlias(true);

        // Both panels arrow point the same way
        mArrowsPointLeft = getLayoutDirection() == LAYOUT_DIRECTION_LTR;
        mIsLeftPanel = isLeftPanel;
    }
    }


    public void setWindowFlag(int flags, boolean enable) {
    public void setWindowFlag(int flags, boolean enable) {
@@ -62,6 +165,58 @@ public class NavigationBarEdgePanel extends View {
        updateLayout(lp);
        updateLayout(lp);
    }
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN : {
                show(event.getX(), event.getY());
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                handleNewSwipePoint(event.getX());
                break;
            }
            // Fall through
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL: {
                hide();
                break;
            }
        }

        return false;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        float edgeOffset = mBaseExtent * mDragProgress - mStrokeThickness;
        float animatedOffset = mPointExtent * mLegProgress;
        canvas.save();
        canvas.translate(
                mIsLeftPanel ? edgeOffset : getWidth() - edgeOffset,
                mStartY - mHeight * 0.5f);

        float outsideX = mArrowsPointLeft ? animatedOffset : 0;
        float middleX = mArrowsPointLeft ? 0 : animatedOffset;

        if (SHOW_PROTECTION_STROKE) {
            canvas.drawLine(outsideX, 0, middleX, mHeight * 0.5f, mProtectionPaint);
            canvas.drawLine(middleX, mHeight * 0.5f, outsideX, mHeight, mProtectionPaint);
        }

        canvas.drawLine(outsideX, 0, middleX, mHeight * 0.5f, mPaint);
        canvas.drawLine(middleX, mHeight * 0.5f, outsideX, mHeight, mPaint);
        canvas.restore();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        // TODO: read the gesture length from the nav controller.
        mGestureLength = getWidth();
    }

    public void setDimensions(int width, int height) {
    public void setDimensions(int width, int height) {
        final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
        final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
        if (lp.width != width || lp.height != height) {
        if (lp.width != width || lp.height != height) {
@@ -71,8 +226,72 @@ public class NavigationBarEdgePanel extends View {
        }
        }
    }
    }


    private void setLegProgress(float progress) {
        mLegProgress = progress;
        invalidate();
    }

    private float getLegProgress() {
        return mLegProgress;
    }

    private void setDragProgress(float dragProgress) {
        mDragProgress = dragProgress;
        invalidate();
    }

    private float getDragProgress() {
        return mDragProgress;
    }

    private void hide() {
        animate().alpha(0f).setDuration(ANIM_DURATION_MS);
    }

    private void show(float x, float y) {
        mEndAnimator.cancel();
        mLegAnimator.cancel();
        setLegProgress(0f);
        setDragProgress(0f);
        setAlpha(1f);

        float halfHeight = mHeight * 0.5f;
        mStartY = MathUtils.constrain(y, halfHeight, getHeight() - halfHeight);
        mStartX = x;
    }

    private void handleNewSwipePoint(float x) {
        float dist = MathUtils.abs(x - mStartX);

        setDragProgress(MathUtils.constrainedMap(
                0, 1.0f,
                0, mGestureLength * TRACK_LENGTH_MULTIPLIER,
                dist));

        if (dist < mGestureLength) {
            setLegProgress(MathUtils.constrainedMap(
                    0f, POINTEDNESS_BEFORE_SNAP_RATIO,
                    mGestureLength * START_POINTING_RATIO, mGestureLength,
                    dist));

            mGestureDetected = false;
        } else {
            if (!mGestureDetected) {
                performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
                mGestureDetected = true;

                mLegAnimator.setFloatValues(1f);
                mLegAnimator.start();
            }
        }
    }

    private void updateLayout(WindowManager.LayoutParams lp) {
    private void updateLayout(WindowManager.LayoutParams lp) {
        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        wm.updateViewLayout(this, lp);
        wm.updateViewLayout(this, lp);
    }
    }

    private float dp(float dp) {
        return mDensity * dp;
    }
}
}
+3 −2
Original line number Original line Diff line number Diff line
@@ -1227,10 +1227,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
                    .getSystemService(Context.WINDOW_SERVICE);
                    .getSystemService(Context.WINDOW_SERVICE);
            int width = mPrototypeController.getEdgeSensitivityWidth();
            int width = mPrototypeController.getEdgeSensitivityWidth();
            int height = mPrototypeController.getEdgeSensitivityHeight();
            int height = mPrototypeController.getEdgeSensitivityHeight();
            // Explicitly left and right, not start and end as this is device relative.
            mLeftEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height,
            mLeftEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height,
                    Gravity.START | Gravity.BOTTOM);
                    Gravity.LEFT | Gravity.BOTTOM);
            mRightEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height,
            mRightEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height,
                    Gravity.END | Gravity.BOTTOM);
                    Gravity.RIGHT | Gravity.BOTTOM);
            mLeftEdgePanel.setOnTouchListener(mEdgePanelTouchListener);
            mLeftEdgePanel.setOnTouchListener(mEdgePanelTouchListener);
            mRightEdgePanel.setOnTouchListener(mEdgePanelTouchListener);
            mRightEdgePanel.setOnTouchListener(mEdgePanelTouchListener);
            wm.addView(mLeftEdgePanel, mLeftEdgePanel.getLayoutParams());
            wm.addView(mLeftEdgePanel, mLeftEdgePanel.getLayoutParams());