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

Commit e108be85 authored by Peter Kalauskas's avatar Peter Kalauskas Committed by Android (Google) Code Review
Browse files

Merge "New back panel" into tm-qpr-dev

parents 423c67af a674977a
Loading
Loading
Loading
Loading
+44 −1
Original line number Diff line number Diff line
@@ -48,7 +48,50 @@
    <!-- The minimum display position of the arrow on the screen -->
    <dimen name="navigation_edge_arrow_min_y">64dp</dimen>
    <!-- The amount by which the arrow is shifted to avoid the finger-->
    <dimen name="navigation_edge_finger_offset">48dp</dimen>
    <dimen name="navigation_edge_finger_offset">64dp</dimen>

    <!-- The thickness of the arrow -->
    <dimen name="navigation_edge_arrow_thickness">4dp</dimen>
    <!-- The minimum delta needed to change direction / stop triggering back -->
    <dimen name="navigation_edge_minimum_x_delta_for_switch">32dp</dimen>

    <!-- entry state -->
    <dimen name="navigation_edge_entry_margin">4dp</dimen>
    <dimen name="navigation_edge_entry_background_width">8dp</dimen>
    <dimen name="navigation_edge_entry_background_height">60dp</dimen>
    <dimen name="navigation_edge_entry_edge_corners">6dp</dimen>
    <dimen name="navigation_edge_entry_far_corners">6dp</dimen>
    <dimen name="navigation_edge_entry_arrow_length">10dp</dimen>
    <dimen name="navigation_edge_entry_arrow_height">7dp</dimen>

    <!-- pre-threshold -->
    <dimen name="navigation_edge_pre_threshold_margin">4dp</dimen>
    <dimen name="navigation_edge_pre_threshold_background_width">64dp</dimen>
    <dimen name="navigation_edge_pre_threshold_background_height">60dp</dimen>
    <dimen name="navigation_edge_pre_threshold_edge_corners">22dp</dimen>
    <dimen name="navigation_edge_pre_threshold_far_corners">26dp</dimen>

    <!-- post-threshold / active -->
    <dimen name="navigation_edge_active_margin">14dp</dimen>
    <dimen name="navigation_edge_active_background_width">60dp</dimen>
    <dimen name="navigation_edge_active_background_height">60dp</dimen>
    <dimen name="navigation_edge_active_edge_corners">30dp</dimen>
    <dimen name="navigation_edge_active_far_corners">30dp</dimen>
    <dimen name="navigation_edge_active_arrow_length">8dp</dimen>
    <dimen name="navigation_edge_active_arrow_height">9dp</dimen>

    <!-- stretch @412 dp -->
    <dimen name="navigation_edge_stretch_threshold">412dp</dimen>
    <dimen name="navigation_edge_stretch_margin">18dp</dimen>
    <dimen name="navigation_edge_stretch_background_width">74dp</dimen>
    <dimen name="navigation_edge_stretch_background_height">60dp</dimen>
    <dimen name="navigation_edge_stretch_left_corners">30dp</dimen>
    <dimen name="navigation_edge_stretch_right_corners">30dp</dimen>
    <dimen name="navigation_edge_stretched_arrow_length">7dp</dimen>
    <dimen name="navigation_edge_stretched_arrow_height">10dp</dimen>

    <dimen name="navigation_edge_cancelled_arrow_length">12dp</dimen>
    <dimen name="navigation_edge_cancelled_arrow_height">0dp</dimen>

    <!-- Height of notification icons in the status bar -->
    <dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
+340 −0
Original line number Diff line number Diff line
package com.android.systemui.navigationbar.gestural

import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Path
import android.graphics.RectF
import android.view.View
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
import com.android.internal.util.LatencyTracker
import com.android.settingslib.Utils
import com.android.systemui.navigationbar.gestural.BackPanelController.DelayedOnAnimationEndListener

private const val TAG = "BackPanel"
private const val DEBUG = false

class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : View(context) {

    var arrowsPointLeft = false
        set(value) {
            if (field != value) {
                invalidate()
                field = value
            }
        }

    // Arrow color and shape
    private val arrowPath = Path()
    private val arrowPaint = Paint()

    // Arrow background color and shape
    private var arrowBackgroundRect = RectF()
    private var arrowBackgroundPaint = Paint()

    // True if the panel is currently on the left of the screen
    var isLeftPanel = false

    /**
     * Used to track back arrow latency from [android.view.MotionEvent.ACTION_DOWN] to [onDraw]
     */
    private var trackingBackArrowLatency = false

    /**
     * The length of the arrow measured horizontally. Used for animating [arrowPath]
     */
    private var arrowLength = AnimatedFloat("arrowLength", SpringForce())

    /**
     * The height of the arrow measured vertically from its center to its top (i.e. half the total
     * height). Used for animating [arrowPath]
     */
    private var arrowHeight = AnimatedFloat("arrowHeight", SpringForce())

    private val backgroundWidth = AnimatedFloat(
        name = "backgroundWidth",
        SpringForce().apply {
            stiffness = 600f
            dampingRatio = 0.65f
        })

    private val backgroundHeight = AnimatedFloat(
        name = "backgroundHeight",
        SpringForce().apply {
            stiffness = 600f
            dampingRatio = 0.65f
        })

    /**
     * Corners of the background closer to the edge of the screen (where the arrow appeared from).
     * Used for animating [arrowBackgroundRect]
     */
    private val backgroundEdgeCornerRadius = AnimatedFloat(
        name = "backgroundEdgeCornerRadius",
        SpringForce().apply {
            stiffness = 400f
            dampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY
        })

    /**
     * Corners of the background further from the edge of the screens (toward the direction the
     * arrow is being dragged). Used for animating [arrowBackgroundRect]
     */
    private val backgroundDragCornerRadius = AnimatedFloat(
        name = "backgroundDragCornerRadius",
        SpringForce().apply {
            stiffness = 2200f
            dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
        })

    /**
     * Left/right position of the background relative to the canvas. Also corresponds with the
     * background's margin relative to the screen edge. The arrow will be centered within the
     * background.
     */
    private var horizontalTranslation = AnimatedFloat("horizontalTranslation", SpringForce())

    /**
     * Canvas vertical translation. How far up/down the arrow and background appear relative to the
     * canvas.
     */
    private var verticalTranslation: AnimatedFloat =
        AnimatedFloat("verticalTranslation", SpringForce().apply {
            stiffness = SpringForce.STIFFNESS_MEDIUM
        })

    /**
     * Use for drawing debug info. Can only be set if [DEBUG]=true
     */
    var drawDebugInfo: ((canvas: Canvas) -> Unit)? = null
        set(value) {
            if (DEBUG) field = value
        }

    internal fun updateArrowPaint(arrowThickness: Float) {
        // Arrow constants
        arrowPaint.strokeWidth = arrowThickness

        arrowPaint.color =
            Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
        arrowBackgroundPaint.color = Utils.getColorAccentDefaultColor(context)
    }

    private inner class AnimatedFloat(name: String, springForce: SpringForce) {
        // The resting position when not stretched by a touch drag
        private var restingPosition = 0f

        // The current position as updated by the SpringAnimation
        var pos = 0f
            set(v) {
                if (field != v) {
                    field = v
                    invalidate()
                }
            }

        val animation: SpringAnimation

        init {
            val floatProp = object : FloatPropertyCompat<AnimatedFloat>(name) {
                override fun setValue(animatedFloat: AnimatedFloat, value: Float) {
                    animatedFloat.pos = value
                }

                override fun getValue(animatedFloat: AnimatedFloat): Float = animatedFloat.pos
            }
            animation = SpringAnimation(this, floatProp)
            animation.spring = springForce
        }

        fun snapTo(newPosition: Float) {
            animation.cancel()
            restingPosition = newPosition
            animation.spring.finalPosition = newPosition
            pos = newPosition
        }

        fun stretchTo(stretchAmount: Float) {
            animation.animateToFinalPosition(restingPosition + stretchAmount)
        }

        fun updateRestingPosition(pos: Float, animated: Boolean) {
            restingPosition = pos
            if (animated)
                animation.animateToFinalPosition(restingPosition)
            else
                snapTo(restingPosition)
        }
    }

    init {
        visibility = GONE
        arrowPaint.apply {
            style = Paint.Style.STROKE
            strokeCap = Paint.Cap.SQUARE
        }
        arrowBackgroundPaint.apply {
            style = Paint.Style.FILL
            strokeJoin = Paint.Join.ROUND
            strokeCap = Paint.Cap.ROUND
        }
    }

    private fun calculateArrowPath(dx: Float, dy: Float): Path {
        arrowPath.reset()
        arrowPath.moveTo(dx, -dy)
        arrowPath.lineTo(0f, 0f)
        arrowPath.lineTo(dx, dy)
        arrowPath.moveTo(dx, -dy)
        return arrowPath
    }

    fun addEndListener(endListener: DelayedOnAnimationEndListener): Boolean {
        return if (horizontalTranslation.animation.isRunning) {
            horizontalTranslation.animation.addEndListener(endListener)
            true
        } else {
            endListener.runNow()
            false
        }
    }

    fun setStretch(
        arrowLengthStretch: Float,
        arrowHeightStretch: Float,
        backgroundWidthStretch: Float,
        backgroundHeightStretch: Float,
        backgroundEdgeCornerRadiusStretch: Float,
        backgroundDragCornerRadiusStretch: Float,
        horizontalTranslationStretch: Float
    ) {
        arrowLength.stretchTo(arrowLengthStretch)
        arrowHeight.stretchTo(arrowHeightStretch)
        backgroundWidth.stretchTo(backgroundWidthStretch)
        backgroundHeight.stretchTo(backgroundHeightStretch)
        backgroundEdgeCornerRadius.stretchTo(backgroundEdgeCornerRadiusStretch)
        backgroundDragCornerRadius.stretchTo(backgroundDragCornerRadiusStretch)
        horizontalTranslation.stretchTo(horizontalTranslationStretch)
    }

    fun resetStretch() {
        setStretch(0f, 0f, 0f, 0f, 0f, 0f, 0f)
    }

    /**
     * Updates resting arrow and background size not accounting for stretch
     */
    internal fun updateRestingArrowDimens(
        backgroundWidth: Float,
        backgroundHeight: Float,
        backgroundEdgeCornerRadius: Float,
        backgroundDragCornerRadius: Float,
        arrowLength: Float,
        arrowHeight: Float,
        horizontalTranslation: Float,
        animate: Boolean
    ) {
        this.arrowLength.updateRestingPosition(arrowLength, animate)
        this.arrowHeight.updateRestingPosition(arrowHeight, animate)
        this.backgroundWidth.updateRestingPosition(backgroundWidth, animate)
        this.backgroundHeight.updateRestingPosition(backgroundHeight, animate)
        this.backgroundEdgeCornerRadius.updateRestingPosition(backgroundEdgeCornerRadius, animate)
        this.backgroundDragCornerRadius.updateRestingPosition(backgroundDragCornerRadius, animate)
        this.horizontalTranslation.updateRestingPosition(horizontalTranslation, animate)
    }

    fun animateVertically(yPos: Float) = verticalTranslation.stretchTo(yPos)

    fun setArrowStiffness(arrowStiffness: Float, arrowDampingRatio: Float) {
        arrowLength.animation.spring.apply {
            stiffness = arrowStiffness
            dampingRatio = arrowDampingRatio
        }
        arrowHeight.animation.spring.apply {
            stiffness = arrowStiffness
            dampingRatio = arrowDampingRatio
        }
    }

    override fun hasOverlappingRendering() = false

    override fun onDraw(canvas: Canvas) {
        var edgeCorner = backgroundEdgeCornerRadius.pos
        val farCorner = backgroundDragCornerRadius.pos
        val halfHeight = backgroundHeight.pos / 2

        canvas.save()

        if (!isLeftPanel) canvas.scale(-1f, 1f, width / 2.0f, 0f)

        canvas.translate(
            horizontalTranslation.pos,
            height * 0.5f + verticalTranslation.pos
        )

        val arrowBackground = arrowBackgroundRect.apply {
            left = 0f
            top = -halfHeight
            right = backgroundWidth.pos
            bottom = halfHeight
        }.toPathWithRoundCorners(
            topLeft = edgeCorner,
            bottomLeft = edgeCorner,
            topRight = farCorner,
            bottomRight = farCorner
        )
        canvas.drawPath(arrowBackground, arrowBackgroundPaint)

        val dx = arrowLength.pos
        val dy = arrowHeight.pos

        // How far the arrow bounding box should be from the edge of the screen. Measured from
        // either the tip or the back of the arrow, whichever is closer
        var arrowOffset = (backgroundWidth.pos - dx) / 2
        canvas.translate(
            /* dx= */ arrowOffset,
            /* dy= */ 0f /* pass 0 for the y position since the canvas was already translated */
        )

        val arrowPointsAwayFromEdge = !arrowsPointLeft.xor(isLeftPanel)
        if (arrowPointsAwayFromEdge) {
            canvas.apply {
                scale(-1f, 1f, 0f, 0f)
                translate(-dx, 0f)
            }
        }

        val arrowPath = calculateArrowPath(dx = dx, dy = dy)
        canvas.drawPath(arrowPath, arrowPaint)
        canvas.restore()

        if (trackingBackArrowLatency) {
            latencyTracker.onActionEnd(LatencyTracker.ACTION_SHOW_BACK_ARROW)
            trackingBackArrowLatency = false
        }

        if (DEBUG) drawDebugInfo?.invoke(canvas)
    }

    fun startTrackingShowBackArrowLatency() {
        latencyTracker.onActionStart(LatencyTracker.ACTION_SHOW_BACK_ARROW)
        trackingBackArrowLatency = true
    }

    private fun RectF.toPathWithRoundCorners(
        topLeft: Float = 0f,
        topRight: Float = 0f,
        bottomRight: Float = 0f,
        bottomLeft: Float = 0f
    ): Path = Path().apply {
        val corners = floatArrayOf(
            topLeft, topLeft,
            topRight, topRight,
            bottomRight, bottomRight,
            bottomLeft, bottomLeft
        )
        addRoundRect(this@toPathWithRoundCorners, corners, Path.Direction.CW)
    }
}
+735 −0

File added.

Preview size limit exceeded, changes collapsed.

+76 −21
Original line number Diff line number Diff line
@@ -62,6 +62,8 @@ import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -182,6 +184,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
    private final PluginManager mPluginManager;
    private final ProtoTracer mProtoTracer;
    private final NavigationModeController mNavigationModeController;
    private final BackPanelController.Factory mBackPanelControllerFactory;
    private final ViewConfiguration mViewConfiguration;
    private final WindowManager mWindowManager;
    private final IWindowManager mWindowManagerService;
@@ -199,6 +202,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
    private final Region mExcludeRegion = new Region();
    private final Region mUnrestrictedExcludeRegion = new Region();
    private final LatencyTracker mLatencyTracker;
    private final FeatureFlags mFeatureFlags;

    // The left side edge width where touch down is allowed
    private int mEdgeWidthLeft;
@@ -230,6 +234,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
    private boolean mIsBackGestureAllowed;
    private boolean mGestureBlockingActivityRunning;
    private boolean mIsInPipMode;
    private boolean mIsPredictiveBackAnimEnabled;

    private InputMonitor mInputMonitor;
    private InputChannelCompat.InputEventReceiver mInputEventReceiver;
@@ -298,12 +303,22 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
    };


    EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService,
            SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor,
            BroadcastDispatcher broadcastDispatcher, ProtoTracer protoTracer,
            NavigationModeController navigationModeController, ViewConfiguration viewConfiguration,
            WindowManager windowManager, IWindowManager windowManagerService,
            FalsingManager falsingManager, LatencyTracker latencyTracker) {
    EdgeBackGestureHandler(
            Context context,
            OverviewProxyService overviewProxyService,
            SysUiState sysUiState,
            PluginManager pluginManager,
            @Main Executor executor,
            BroadcastDispatcher broadcastDispatcher,
            ProtoTracer protoTracer,
            NavigationModeController navigationModeController,
            BackPanelController.Factory backPanelControllerFactory,
            ViewConfiguration viewConfiguration,
            WindowManager windowManager,
            IWindowManager windowManagerService,
            FalsingManager falsingManager,
            LatencyTracker latencyTracker,
            FeatureFlags featureFlags) {
        super(broadcastDispatcher);
        mContext = context;
        mDisplayId = context.getDisplayId();
@@ -313,11 +328,13 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
        mPluginManager = pluginManager;
        mProtoTracer = protoTracer;
        mNavigationModeController = navigationModeController;
        mBackPanelControllerFactory = backPanelControllerFactory;
        mViewConfiguration = viewConfiguration;
        mWindowManager = windowManager;
        mWindowManagerService = windowManagerService;
        mFalsingManager = falsingManager;
        mLatencyTracker = latencyTracker;
        mFeatureFlags = featureFlags;
        ComponentName recentsComponentName = ComponentName.unflattenFromString(
                context.getString(com.android.internal.R.string.config_recentsComponentName));
        if (recentsComponentName != null) {
@@ -507,8 +524,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
                    Choreographer.getInstance(), this::onInputEvent);

            // Add a nav bar panel window
            setEdgeBackPlugin(
                    new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));
            mIsPredictiveBackAnimEnabled =
                    mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_ANIM);
            resetEdgeBackPlugin();
            mPluginManager.addPluginListener(
                    this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
        }
@@ -523,7 +541,17 @@ public class EdgeBackGestureHandler extends CurrentUserTracker

    @Override
    public void onPluginDisconnected(NavigationEdgeBackPlugin plugin) {
        setEdgeBackPlugin(new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));
        resetEdgeBackPlugin();
    }

    private void resetEdgeBackPlugin() {
        if (mIsPredictiveBackAnimEnabled) {
            setEdgeBackPlugin(
                    mBackPanelControllerFactory.create(mContext, mBackAnimation));
        } else {
            setEdgeBackPlugin(
                    new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));
        }
    }

    private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {
@@ -948,8 +976,12 @@ public class EdgeBackGestureHandler extends CurrentUserTracker

    public void setBackAnimation(BackAnimation backAnimation) {
        mBackAnimation = backAnimation;
        if (mEdgeBackPlugin != null && mEdgeBackPlugin instanceof NavigationBarEdgePanel) {
        if (mEdgeBackPlugin != null) {
            if (mEdgeBackPlugin instanceof NavigationBarEdgePanel) {
                ((NavigationBarEdgePanel) mEdgeBackPlugin).setBackAnimation(backAnimation);
            } else if (mEdgeBackPlugin instanceof BackPanelController) {
                ((BackPanelController) mEdgeBackPlugin).setBackAnimation(backAnimation);
            }
        }
    }

@@ -967,20 +999,29 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
        private final BroadcastDispatcher mBroadcastDispatcher;
        private final ProtoTracer mProtoTracer;
        private final NavigationModeController mNavigationModeController;
        private final BackPanelController.Factory mBackPanelControllerFactory;
        private final ViewConfiguration mViewConfiguration;
        private final WindowManager mWindowManager;
        private final IWindowManager mWindowManagerService;
        private final FalsingManager mFalsingManager;
        private final LatencyTracker mLatencyTracker;
        private final FeatureFlags mFeatureFlags;

        @Inject
        public Factory(OverviewProxyService overviewProxyService,
                SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor,
                BroadcastDispatcher broadcastDispatcher, ProtoTracer protoTracer,
                       SysUiState sysUiState,
                       PluginManager pluginManager,
                       @Main Executor executor,
                       BroadcastDispatcher broadcastDispatcher,
                       ProtoTracer protoTracer,
                       NavigationModeController navigationModeController,
                ViewConfiguration viewConfiguration, WindowManager windowManager,
                IWindowManager windowManagerService, FalsingManager falsingManager,
                LatencyTracker latencyTracker) {
                       BackPanelController.Factory backPanelControllerFactory,
                       ViewConfiguration viewConfiguration,
                       WindowManager windowManager,
                       IWindowManager windowManagerService,
                       FalsingManager falsingManager,
                       LatencyTracker latencyTracker,
                       FeatureFlags featureFlags) {
            mOverviewProxyService = overviewProxyService;
            mSysUiState = sysUiState;
            mPluginManager = pluginManager;
@@ -988,19 +1029,33 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
            mBroadcastDispatcher = broadcastDispatcher;
            mProtoTracer = protoTracer;
            mNavigationModeController = navigationModeController;
            mBackPanelControllerFactory = backPanelControllerFactory;
            mViewConfiguration = viewConfiguration;
            mWindowManager = windowManager;
            mWindowManagerService = windowManagerService;
            mFalsingManager = falsingManager;
            mLatencyTracker = latencyTracker;
            mFeatureFlags = featureFlags;
        }

        /** Construct a {@link EdgeBackGestureHandler}. */
        public EdgeBackGestureHandler create(Context context) {
            return new EdgeBackGestureHandler(context, mOverviewProxyService, mSysUiState,
                    mPluginManager, mExecutor, mBroadcastDispatcher, mProtoTracer,
                    mNavigationModeController, mViewConfiguration, mWindowManager,
                    mWindowManagerService, mFalsingManager, mLatencyTracker);
            return new EdgeBackGestureHandler(
                    context,
                    mOverviewProxyService,
                    mSysUiState,
                    mPluginManager,
                    mExecutor,
                    mBroadcastDispatcher,
                    mProtoTracer,
                    mNavigationModeController,
                    mBackPanelControllerFactory,
                    mViewConfiguration,
                    mWindowManager,
                    mWindowManagerService,
                    mFalsingManager,
                    mLatencyTracker,
                    mFeatureFlags);
        }
    }

+139 −0

File added.

Preview size limit exceeded, changes collapsed.