Refactor QuickStepController into Gestures
The refactor allows adding new gestures easier and provides a way to
reassign triggers to the gestures.
go/navbar-prototypes-doc
Test: atest QuickStepControllerTest
Bug: 112934365
Change-Id: I5334947e2ff3f39af890e1f026089b933ff48a3c
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 3980126..c03800e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -18,187 +18,96 @@
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
-import static com.android.systemui.Interpolators.ALPHA_IN;
-import static com.android.systemui.Interpolators.ALPHA_OUT;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
+
import static com.android.systemui.recents.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
import static com.android.systemui.recents.OverviewProxyService.TAG_OPS;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.RadialGradient;
-import android.graphics.Rect;
-import android.graphics.Shader;
-import android.hardware.input.InputManager;
-import android.os.Handler;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.provider.Settings;
-import android.util.FloatProperty;
import android.util.Log;
-import android.util.Slog;
-import android.view.HapticFeedbackConstants;
-import android.view.InputDevice;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewPropertyAnimator;
-import android.view.WindowManagerGlobal;
+
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.R;
-import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
+import com.android.systemui.R;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.shared.recents.IOverviewProxy;
-import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.system.NavigationBarCompat;
+
import java.io.PrintWriter;
/**
* Class to detect gestures on the navigation bar and implement quick scrub.
+ * Note that the variables in this class horizontal and vertical represents horizontal always
+ * aligned with along the navigation bar).
*/
public class QuickStepController implements GestureHelper {
private static final String TAG = "QuickStepController";
- private static final int ANIM_IN_DURATION_MS = 150;
- private static final int ANIM_OUT_DURATION_MS = 134;
- private static final float TRACK_SCALE = 0.95f;
- private static final float GRADIENT_WIDTH = .75f;
/** Experiment to swipe home button left to execute a back key press */
- private static final String PULL_HOME_GO_BACK_PROP = "quickstepcontroller_homegoesback";
private static final String HIDE_BACK_BUTTON_PROP = "quickstepcontroller_hideback";
- private static final String BACK_AFTER_END_PROP
- = "quickstepcontroller_homegoesbackwhenend";
- private static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled";
- private static final long BACK_BUTTON_FADE_OUT_ALPHA = 60;
private static final long BACK_BUTTON_FADE_IN_ALPHA = 150;
- private static final long BACK_GESTURE_POLL_TIMEOUT = 1000;
/** When the home-swipe-back gesture is disallowed, make it harder to pull */
private static final float DISALLOW_GESTURE_DAMPING_FACTOR = 0.16f;
+ private static final int ACTION_SWIPE_UP_INDEX = 0;
+ private static final int ACTION_SWIPE_DOWN_INDEX = 1;
+ private static final int ACTION_SWIPE_LEFT_INDEX = 2;
+ private static final int ACTION_SWIPE_RIGHT_INDEX = 3;
+ private static final int MAX_GESTURES = 4;
+
private NavigationBarView mNavigationBarView;
- private boolean mQuickScrubActive;
private boolean mAllowGestureDetection;
- private boolean mBackGestureActive;
- private boolean mCanPerformBack;
- private boolean mQuickStepStarted;
private boolean mNotificationsVisibleOnDown;
private int mTouchDownX;
private int mTouchDownY;
- private boolean mDragPositive;
- private boolean mIsVertical;
+ private boolean mDragHPositive;
+ private boolean mDragVPositive;
private boolean mIsRTL;
- private float mTrackAlpha;
- private float mTrackScale = TRACK_SCALE;
+ private int mNavBarPosition;
private float mDarkIntensity;
- private RadialGradient mHighlight;
- private float mHighlightCenter;
- private AnimatorSet mTrackAnimator;
- private ViewPropertyAnimator mHomeAnimator;
+ private ViewPropertyAnimator mDragBtnAnimator;
private ButtonDispatcher mHitTarget;
- private View mCurrentNavigationBarView;
private boolean mIsInScreenPinning;
+ private boolean mGestureHorizontalDragsButton;
+ private boolean mGestureVerticalDragsButton;
+ private boolean mGestureTrackPositive;
- private final Handler mHandler = new Handler();
- private final Rect mTrackRect = new Rect();
+ private NavigationGestureAction mCurrentAction;
+ private NavigationGestureAction[] mGestureActions = new NavigationGestureAction[MAX_GESTURES];
+
private final OverviewProxyService mOverviewEventSender;
- private final int mTrackThickness;
- private final int mTrackEndPadding;
private final int mHomeBackGestureDragLimit;
private final Context mContext;
private final StatusBar mStatusBar;
private final Matrix mTransformGlobalMatrix = new Matrix();
private final Matrix mTransformLocalMatrix = new Matrix();
- private final Paint mTrackPaint = new Paint();
-
- private final FloatProperty<QuickStepController> mTrackAlphaProperty =
- new FloatProperty<QuickStepController>("TrackAlpha") {
- @Override
- public void setValue(QuickStepController controller, float alpha) {
- mTrackAlpha = alpha;
- mNavigationBarView.invalidate();
- }
-
- @Override
- public Float get(QuickStepController controller) {
- return mTrackAlpha;
- }
- };
-
- private final FloatProperty<QuickStepController> mTrackScaleProperty =
- new FloatProperty<QuickStepController>("TrackScale") {
- @Override
- public void setValue(QuickStepController controller, float scale) {
- mTrackScale = scale;
- mNavigationBarView.invalidate();
- }
-
- @Override
- public Float get(QuickStepController controller) {
- return mTrackScale;
- }
- };
-
- private final FloatProperty<QuickStepController> mNavBarAlphaProperty =
- new FloatProperty<QuickStepController>("NavBarAlpha") {
- @Override
- public void setValue(QuickStepController controller, float alpha) {
- if (mCurrentNavigationBarView != null) {
- mCurrentNavigationBarView.setAlpha(alpha);
- }
- }
-
- @Override
- public Float get(QuickStepController controller) {
- if (mCurrentNavigationBarView != null) {
- return mCurrentNavigationBarView.getAlpha();
- }
- return 1f;
- }
- };
-
- private AnimatorListenerAdapter mQuickScrubEndListener = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- resetQuickScrub();
- }
- };
-
- private final Runnable mExecuteBackRunnable = new Runnable() {
- @Override
- public void run() {
- if (canPerformHomeBackGesture()) {
- performBack();
- mHandler.postDelayed(this, BACK_GESTURE_POLL_TIMEOUT);
- }
- }
- };
public QuickStepController(Context context) {
final Resources res = context.getResources();
mContext = context;
mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
mOverviewEventSender = Dependency.get(OverviewProxyService.class);
- mTrackThickness = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_thickness);
- mTrackEndPadding = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_edge_padding);
mHomeBackGestureDragLimit =
res.getDimensionPixelSize(R.dimen.nav_home_back_gesture_drag_limit);
- mTrackPaint.setAntiAlias(true);
- mTrackPaint.setDither(true);
}
public void setComponents(NavigationBarView navigationBarView) {
@@ -210,6 +119,31 @@
}
/**
+ * Set each gesture an action. After set the gestures triggered will run the actions attached.
+ * @param swipeUpAction action after swiping up
+ * @param swipeDownAction action after swiping down
+ * @param swipeLeftAction action after swiping left
+ * @param swipeRightAction action after swiping right
+ */
+ public void setGestureActions(@Nullable NavigationGestureAction swipeUpAction,
+ @Nullable NavigationGestureAction swipeDownAction,
+ @Nullable NavigationGestureAction swipeLeftAction,
+ @Nullable NavigationGestureAction swipeRightAction) {
+ mGestureActions[ACTION_SWIPE_UP_INDEX] = swipeUpAction;
+ mGestureActions[ACTION_SWIPE_DOWN_INDEX] = swipeDownAction;
+ mGestureActions[ACTION_SWIPE_LEFT_INDEX] = swipeLeftAction;
+ mGestureActions[ACTION_SWIPE_RIGHT_INDEX] = swipeRightAction;
+
+ // Set the current state to all actions
+ for (NavigationGestureAction action: mGestureActions) {
+ if (action != null) {
+ action.setBarState(true, mNavBarPosition, mDragHPositive, mDragVPositive);
+ action.onDarkIntensityChange(mDarkIntensity);
+ }
+ }
+ }
+
+ /**
* @return true if we want to intercept touch events for quick scrub and prevent proxying the
* event to the overview service.
*/
@@ -242,8 +176,10 @@
private boolean handleTouchEvent(MotionEvent event) {
final boolean deadZoneConsumed =
mNavigationBarView.getDownHitTarget() == HIT_TARGET_DEAD_ZONE;
- if (mOverviewEventSender.getProxy() == null || (!mNavigationBarView.isQuickScrubEnabled()
- && !mNavigationBarView.isQuickStepSwipeUpEnabled())) {
+
+ // Requires proxy and an active gesture or able to perform any gesture to continue
+ if (mOverviewEventSender.getProxy() == null
+ || (mCurrentAction == null && !canPerformAnyAction())) {
return deadZoneConsumed;
}
mNavigationBarView.requestUnbufferedDispatch(event);
@@ -255,33 +191,45 @@
int y = (int) event.getY();
mIsInScreenPinning = mNavigationBarView.inScreenPinning();
- // End any existing quickscrub animations before starting the new transition
- if (mTrackAnimator != null) {
- mTrackAnimator.end();
- mTrackAnimator = null;
+ for (NavigationGestureAction gestureAction: mGestureActions) {
+ if (gestureAction != null) {
+ gestureAction.reset();
+ }
}
- mCurrentNavigationBarView = mNavigationBarView.getCurrentView();
- mHitTarget = mNavigationBarView.getButtonAtPosition(x, y);
+ // Valid buttons to drag over
+ switch (mNavigationBarView.getDownHitTarget()) {
+ case HIT_TARGET_BACK:
+ mHitTarget = mNavigationBarView.getBackButton();
+ break;
+ case HIT_TARGET_HOME:
+ mHitTarget = mNavigationBarView.getHomeButton();
+ break;
+ case HIT_TARGET_OVERVIEW:
+ mHitTarget = mNavigationBarView.getRecentsButton();
+ break;
+ default:
+ mHitTarget = null;
+ break;
+ }
if (mHitTarget != null) {
// Pre-emptively delay the touch feedback for the button that we just touched
mHitTarget.setDelayTouchFeedback(true);
}
mTouchDownX = x;
mTouchDownY = y;
+ mGestureHorizontalDragsButton = false;
+ mGestureVerticalDragsButton = false;
mTransformGlobalMatrix.set(Matrix.IDENTITY_MATRIX);
mTransformLocalMatrix.set(Matrix.IDENTITY_MATRIX);
mNavigationBarView.transformMatrixToGlobal(mTransformGlobalMatrix);
mNavigationBarView.transformMatrixToLocal(mTransformLocalMatrix);
- mQuickStepStarted = false;
- mBackGestureActive = false;
mAllowGestureDetection = true;
mNotificationsVisibleOnDown = !mNavigationBarView.isNotificationsFullyCollapsed();
- mCanPerformBack = canPerformHomeBackGesture();
break;
}
case MotionEvent.ACTION_MOVE: {
- if (mQuickStepStarted || !mAllowGestureDetection){
+ if (!mAllowGestureDetection) {
break;
}
int x = (int) event.getX();
@@ -289,108 +237,132 @@
int xDiff = Math.abs(x - mTouchDownX);
int yDiff = Math.abs(y - mTouchDownY);
- boolean exceededScrubTouchSlop, exceededSwipeUpTouchSlop;
- int pos, touchDown, offset, trackSize;
+ boolean exceededSwipeHorizontalTouchSlop, exceededSwipeVerticalTouchSlop;
+ int posH, touchDownH, posV, touchDownV;
- if (mIsVertical) {
- exceededScrubTouchSlop =
+ if (isNavBarVertical()) {
+ exceededSwipeHorizontalTouchSlop =
yDiff > NavigationBarCompat.getQuickScrubTouchSlopPx() && yDiff > xDiff;
- exceededSwipeUpTouchSlop =
+ exceededSwipeVerticalTouchSlop =
xDiff > NavigationBarCompat.getQuickStepTouchSlopPx() && xDiff > yDiff;
- pos = y;
- touchDown = mTouchDownY;
- offset = pos - mTrackRect.top;
- trackSize = mTrackRect.height();
+ posH = y;
+ touchDownH = mTouchDownY;
+ posV = x;
+ touchDownV = mTouchDownX;
} else {
- exceededScrubTouchSlop =
+ exceededSwipeHorizontalTouchSlop =
xDiff > NavigationBarCompat.getQuickScrubTouchSlopPx() && xDiff > yDiff;
- exceededSwipeUpTouchSlop =
+ exceededSwipeVerticalTouchSlop =
yDiff > NavigationBarCompat.getQuickStepTouchSlopPx() && yDiff > xDiff;
- pos = x;
- touchDown = mTouchDownX;
- offset = pos - mTrackRect.left;
- trackSize = mTrackRect.width();
- }
- // Decide to start quickstep if dragging away from the navigation bar, otherwise in
- // the parallel direction, decide to start quickscrub. Only one may run.
- if (!mBackGestureActive && !mQuickScrubActive && exceededSwipeUpTouchSlop) {
- if (mNavigationBarView.isQuickStepSwipeUpEnabled()
- && !mNotificationsVisibleOnDown) {
- startQuickStep(event);
- }
- break;
+ posH = x;
+ touchDownH = mTouchDownX;
+ posV = y;
+ touchDownV = mTouchDownY;
}
- // Do not handle quick scrub if disabled
- if (!mNavigationBarView.isQuickScrubEnabled()) {
- break;
- }
-
- if (!mDragPositive) {
- offset -= mIsVertical ? mTrackRect.height() : mTrackRect.width();
- }
-
- final boolean allowDrag = !mDragPositive
- ? offset < 0 && pos < touchDown : offset >= 0 && pos > touchDown;
- float scrubFraction = Utilities.clamp(Math.abs(offset) * 1f / trackSize, 0, 1);
- if (!mQuickScrubActive && !mBackGestureActive && exceededScrubTouchSlop) {
- // Passing the drag slop then touch slop will start quick step
- if (allowDrag) {
- startQuickScrub();
- } else if (swipeHomeGoBackGestureEnabled(mContext)
- && mNavigationBarView.getDownHitTarget() == HIT_TARGET_HOME
- && mDragPositive ? pos < touchDown : pos > touchDown) {
- startBackGesture();
- }
- }
-
- if (mQuickScrubActive && (mDragPositive && offset >= 0
- || !mDragPositive && offset <= 0)) {
- try {
- mOverviewEventSender.getProxy().onQuickScrubProgress(scrubFraction);
- if (DEBUG_OVERVIEW_PROXY) {
- Log.d(TAG_OPS, "Quick Scrub Progress:" + scrubFraction);
+ if (mCurrentAction != null) {
+ // Gesture started, provide positions to the current action
+ mCurrentAction.onGestureMove(x, y);
+ } else {
+ // Detect gesture and try to execute an action, only one can run at a time
+ if (exceededSwipeVerticalTouchSlop) {
+ if (mDragVPositive ? (posV < touchDownV) : (posV > touchDownV)) {
+ // Swiping up gesture
+ tryToStartGesture(mGestureActions[ACTION_SWIPE_UP_INDEX],
+ false /* alignedWithNavBar */, false /* positiveDirection */,
+ event);
+ } else {
+ // Swiping down gesture
+ tryToStartGesture(mGestureActions[ACTION_SWIPE_DOWN_INDEX],
+ false /* alignedWithNavBar */, true /* positiveDirection */,
+ event);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to send progress of quick scrub.", e);
- }
- mHighlightCenter = x;
- mNavigationBarView.invalidate();
- } else if (mBackGestureActive) {
- int diff = pos - touchDown;
- // If dragging the incorrect direction after starting back gesture or unable
- // to execute back functionality, then move home but dampen its distance
- if (!mCanPerformBack || (mDragPositive ? diff > 0 : diff < 0)) {
- diff *= DISALLOW_GESTURE_DAMPING_FACTOR;
- } if (Math.abs(diff) > mHomeBackGestureDragLimit) {
- // Once the user drags the home button past a certain limit, the distance
- // will lessen as the home button dampens showing that it was pulled too far
- float distanceAfterDragLimit = (Math.abs(diff) - mHomeBackGestureDragLimit)
- * DISALLOW_GESTURE_DAMPING_FACTOR;
- diff = (int)(distanceAfterDragLimit + mHomeBackGestureDragLimit);
- if (mDragPositive) {
- diff *= -1;
+ } else if (exceededSwipeHorizontalTouchSlop) {
+ if (mDragHPositive ? (posH < touchDownH) : (posH > touchDownH)) {
+ // Swiping left (ltr) gesture
+ tryToStartGesture(mGestureActions[ACTION_SWIPE_LEFT_INDEX],
+ true /* alignedWithNavBar */, false /* positiveDirection */,
+ event);
+ } else {
+ // Swiping right (ltr) gesture
+ tryToStartGesture(mGestureActions[ACTION_SWIPE_RIGHT_INDEX],
+ true /* alignedWithNavBar */, true /* positiveDirection */,
+ event);
}
}
- moveHomeButton(diff);
}
+
+ handleDragHitTarget(mGestureHorizontalDragsButton ? posH : posV,
+ mGestureHorizontalDragsButton ? touchDownH : touchDownV);
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
- endQuickScrub(true /* animate */);
- endBackGesture();
+ if (mCurrentAction != null) {
+ mCurrentAction.endGesture();
+ mCurrentAction = null;
+ }
+
+ // Return the hit target back to its original position
+ if (mHitTarget != null) {
+ final View button = mHitTarget.getCurrentView();
+ if (mGestureHorizontalDragsButton || mGestureVerticalDragsButton) {
+ mDragBtnAnimator = button.animate().setDuration(BACK_BUTTON_FADE_IN_ALPHA)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ if (mGestureVerticalDragsButton ^ isNavBarVertical()) {
+ mDragBtnAnimator.translationY(0);
+ } else {
+ mDragBtnAnimator.translationX(0);
+ }
+ mDragBtnAnimator.start();
+ }
+ }
break;
}
if (shouldProxyEvents(action)) {
proxyMotionEvents(event);
}
- return mBackGestureActive || mQuickScrubActive || mQuickStepStarted || deadZoneConsumed;
+ return mCurrentAction != null || deadZoneConsumed;
+ }
+
+ private void handleDragHitTarget(int position, int touchDown) {
+ // Drag the hit target if gesture action requires it
+ if (mHitTarget != null && (mGestureVerticalDragsButton || mGestureHorizontalDragsButton)) {
+ final View button = mHitTarget.getCurrentView();
+ if (mDragBtnAnimator != null) {
+ mDragBtnAnimator.cancel();
+ mDragBtnAnimator = null;
+ }
+
+ int diff = position - touchDown;
+ // If dragging the incorrect direction after starting gesture or unable to
+ // execute tried action, then move the button but dampen its distance
+ if (mCurrentAction == null || (mGestureTrackPositive ? diff < 0 : diff > 0)) {
+ diff *= DISALLOW_GESTURE_DAMPING_FACTOR;
+ } else if (Math.abs(diff) > mHomeBackGestureDragLimit) {
+ // Once the user drags the button past a certain limit, the distance will
+ // lessen as the button dampens that it was pulled too far
+ float distanceAfterDragLimit = (Math.abs(diff) - mHomeBackGestureDragLimit)
+ * DISALLOW_GESTURE_DAMPING_FACTOR;
+ diff = (int) (distanceAfterDragLimit + mHomeBackGestureDragLimit);
+ if (!mGestureTrackPositive) {
+ diff *= -1;
+ }
+ }
+ if (mGestureVerticalDragsButton ^ isNavBarVertical()) {
+ button.setTranslationY(diff);
+ } else {
+ button.setTranslationX(diff);
+ }
+ }
}
private boolean shouldProxyEvents(int action) {
- if (!mBackGestureActive && !mQuickScrubActive && !mIsInScreenPinning) {
+ final boolean actionValid = (mCurrentAction == null
+ || (mGestureActions[ACTION_SWIPE_UP_INDEX] != null
+ && mGestureActions[ACTION_SWIPE_UP_INDEX].isActive()));
+ if (actionValid && !mIsInScreenPinning) {
// Allow down, cancel and up events, move and other events are passed if notifications
// are not showing and disabled gestures (such as long press) are not executed
switch (action) {
@@ -407,46 +379,18 @@
@Override
public void onDraw(Canvas canvas) {
- if (!mNavigationBarView.isQuickScrubEnabled()) {
- return;
+ if (mCurrentAction != null) {
+ mCurrentAction.onDraw(canvas);
}
- mTrackPaint.setAlpha(Math.round(255f * mTrackAlpha));
-
- // Scale the track, but apply the inverse scale from the nav bar
- final float radius = mTrackRect.height() / 2;
- canvas.save();
- float translate = Utilities.clamp(mHighlightCenter, mTrackRect.left, mTrackRect.right);
- canvas.translate(translate, 0);
- canvas.scale(mTrackScale / mNavigationBarView.getScaleX(),
- 1f / mNavigationBarView.getScaleY(),
- mTrackRect.centerX(), mTrackRect.centerY());
- canvas.drawRoundRect(mTrackRect.left - translate, mTrackRect.top,
- mTrackRect.right - translate, mTrackRect.bottom, radius, radius, mTrackPaint);
- canvas.restore();
}
@Override
public void onLayout(boolean changed, int left, int top, int right, int bottom) {
- final int paddingLeft = mNavigationBarView.getPaddingLeft();
- final int paddingTop = mNavigationBarView.getPaddingTop();
- final int paddingRight = mNavigationBarView.getPaddingRight();
- final int paddingBottom = mNavigationBarView.getPaddingBottom();
- final int width = (right - left) - paddingRight - paddingLeft;
- final int height = (bottom - top) - paddingBottom - paddingTop;
- final int x1, x2, y1, y2;
- if (mIsVertical) {
- x1 = (width - mTrackThickness) / 2 + paddingLeft;
- x2 = x1 + mTrackThickness;
- y1 = paddingTop + mTrackEndPadding;
- y2 = y1 + height - 2 * mTrackEndPadding;
- } else {
- y1 = (height - mTrackThickness) / 2 + paddingTop;
- y2 = y1 + mTrackThickness;
- x1 = mNavigationBarView.getPaddingStart() + mTrackEndPadding;
- x2 = x1 + width - 2 * mTrackEndPadding;
+ for (NavigationGestureAction action: mGestureActions) {
+ if (action != null) {
+ action.onLayout(changed, left, top, right, bottom);
+ }
}
- mTrackRect.set(x1, y1, x2, y2);
- updateHighlight();
}
@Override
@@ -456,119 +400,104 @@
// When in quick scrub, invalidate gradient if changing intensity from black to white and
// vice-versa
- if (mNavigationBarView.isQuickScrubEnabled()
+ if (mCurrentAction != null && mNavigationBarView.isQuickScrubEnabled()
&& Math.round(intensity) != Math.round(oldIntensity)) {
- updateHighlight();
+ mCurrentAction.onDarkIntensityChange(mDarkIntensity);
}
mNavigationBarView.invalidate();
}
@Override
- public void setBarState(boolean isVertical, boolean isRTL) {
- final boolean changed = (mIsVertical != isVertical) || (mIsRTL != isRTL);
- if (changed) {
- // End quickscrub if the state changes mid-transition
- endQuickScrub(false /* animate */);
- }
- mIsVertical = isVertical;
+ public void setBarState(boolean isRTL, int navBarPosition) {
+ final boolean changed = (mIsRTL != isRTL) || (mNavBarPosition != navBarPosition);
mIsRTL = isRTL;
- try {
- int navbarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
- mDragPositive = navbarPos == NAV_BAR_LEFT || navbarPos == NAV_BAR_BOTTOM;
- if (isRTL) {
- mDragPositive = !mDragPositive;
+ mNavBarPosition = navBarPosition;
+
+ // Determine the drag directions depending on location of nav bar
+ switch (navBarPosition) {
+ case NAV_BAR_LEFT:
+ mDragHPositive = !isRTL;
+ mDragVPositive = false;
+ break;
+ case NAV_BAR_RIGHT:
+ mDragHPositive = isRTL;
+ mDragVPositive = true;
+ break;
+ case NAV_BAR_BOTTOM:
+ mDragHPositive = !isRTL;
+ mDragVPositive = true;
+ break;
+ }
+
+ for (NavigationGestureAction action: mGestureActions) {
+ if (action != null) {
+ action.setBarState(changed, mNavBarPosition, mDragHPositive, mDragVPositive);
}
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to get nav bar position.", e);
}
}
@Override
public void onNavigationButtonLongPress(View v) {
mAllowGestureDetection = false;
- mHandler.removeCallbacksAndMessages(null);
}
@Override
public void dump(PrintWriter pw) {
pw.println("QuickStepController {");
- pw.print(" "); pw.println("mQuickScrubActive=" + mQuickScrubActive);
- pw.print(" "); pw.println("mQuickStepStarted=" + mQuickStepStarted);
pw.print(" "); pw.println("mAllowGestureDetection=" + mAllowGestureDetection);
- pw.print(" "); pw.println("mBackGestureActive=" + mBackGestureActive);
- pw.print(" "); pw.println("mCanPerformBack=" + mCanPerformBack);
pw.print(" "); pw.println("mNotificationsVisibleOnDown=" + mNotificationsVisibleOnDown);
- pw.print(" "); pw.println("mIsVertical=" + mIsVertical);
+ pw.print(" "); pw.println("mNavBarPosition=" + mNavBarPosition);
pw.print(" "); pw.println("mIsRTL=" + mIsRTL);
pw.print(" "); pw.println("mIsInScreenPinning=" + mIsInScreenPinning);
pw.println("}");
}
- private void startQuickStep(MotionEvent event) {
- if (mIsInScreenPinning) {
- mNavigationBarView.showPinningEscapeToast();
- mAllowGestureDetection = false;
- return;
- }
-
- mQuickStepStarted = true;
- event.transform(mTransformGlobalMatrix);
- try {
- mOverviewEventSender.getProxy().onQuickStep(event);
- if (DEBUG_OVERVIEW_PROXY) {
- Log.d(TAG_OPS, "Quick Step Start");
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to send quick step started.", e);
- } finally {
- event.transform(mTransformLocalMatrix);
- }
- mOverviewEventSender.notifyQuickStepStarted();
- mHandler.removeCallbacksAndMessages(null);
-
- if (mHitTarget != null) {
- mHitTarget.abortCurrentGesture();
- }
-
- if (mQuickScrubActive) {
- animateEnd();
- }
+ public NavigationGestureAction getCurrentAction() {
+ return mCurrentAction;
}
- private void startQuickScrub() {
+ private void tryToStartGesture(NavigationGestureAction action, boolean alignedWithNavBar,
+ boolean positiveDirection, MotionEvent event) {
+ if (action == null) {
+ return;
+ }
if (mIsInScreenPinning) {
mNavigationBarView.showPinningEscapeToast();
mAllowGestureDetection = false;
return;
}
- if (!mQuickScrubActive) {
- updateHighlight();
- mQuickScrubActive = true;
- ObjectAnimator trackAnimator = ObjectAnimator.ofPropertyValuesHolder(this,
- PropertyValuesHolder.ofFloat(mTrackAlphaProperty, 1f),
- PropertyValuesHolder.ofFloat(mTrackScaleProperty, 1f));
- trackAnimator.setInterpolator(ALPHA_IN);
- trackAnimator.setDuration(ANIM_IN_DURATION_MS);
- ObjectAnimator navBarAnimator = ObjectAnimator.ofFloat(this, mNavBarAlphaProperty, 0f);
- navBarAnimator.setInterpolator(ALPHA_OUT);
- navBarAnimator.setDuration(ANIM_OUT_DURATION_MS);
- mTrackAnimator = new AnimatorSet();
- mTrackAnimator.playTogether(trackAnimator, navBarAnimator);
- mTrackAnimator.start();
-
- // Disable slippery for quick scrub to not cancel outside the nav bar
- mNavigationBarView.updateSlippery();
-
- try {
- mOverviewEventSender.getProxy().onQuickScrubStart();
- if (DEBUG_OVERVIEW_PROXY) {
- Log.d(TAG_OPS, "Quick Scrub Start");
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to send start of quick scrub.", e);
+ // Start new action from gesture if is able to start and depending on notifications
+ // visibility and starting touch down target. If the action is enabled, then also check if
+ // can perform the action so that if action requires the button to be dragged, then the
+ // gesture will have a large dampening factor and prevent action from running.
+ final boolean validHitTarget = action.requiresTouchDownHitTarget() == HIT_TARGET_NONE
+ || action.requiresTouchDownHitTarget() == mNavigationBarView.getDownHitTarget();
+ if (mCurrentAction == null && validHitTarget && action.isEnabled()
+ && (!mNotificationsVisibleOnDown || action.canRunWhenNotificationsShowing())) {
+ if (action.canPerformAction()) {
+ mCurrentAction = action;
+ event.transform(mTransformGlobalMatrix);
+ action.startGesture(event);
+ event.transform(mTransformLocalMatrix);
}
- mOverviewEventSender.notifyQuickScrubStarted();
+
+ // Handle direction of the hit target drag from the axis that started the gesture
+ if (action.requiresDragWithHitTarget()) {
+ if (alignedWithNavBar) {
+ mGestureHorizontalDragsButton = true;
+ mGestureVerticalDragsButton = false;
+ if (positiveDirection) {
+ mGestureTrackPositive = mDragHPositive;
+ }
+ } else {
+ mGestureVerticalDragsButton = true;
+ mGestureHorizontalDragsButton = false;
+ if (positiveDirection) {
+ mGestureTrackPositive = mDragVPositive;
+ }
+ }
+ }
if (mHitTarget != null) {
mHitTarget.abortCurrentGesture();
@@ -576,148 +505,13 @@
}
}
- private void endQuickScrub(boolean animate) {
- if (mQuickScrubActive) {
- animateEnd();
- try {
- mOverviewEventSender.getProxy().onQuickScrubEnd();
- if (DEBUG_OVERVIEW_PROXY) {
- Log.d(TAG_OPS, "Quick Scrub End");
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to send end of quick scrub.", e);
+ private boolean canPerformAnyAction() {
+ for (NavigationGestureAction action: mGestureActions) {
+ if (action != null && action.isEnabled()) {
+ return true;
}
}
- if (!animate) {
- if (mTrackAnimator != null) {
- mTrackAnimator.end();
- mTrackAnimator = null;
- }
- }
- }
-
- private void startBackGesture() {
- if (!mBackGestureActive) {
- mBackGestureActive = true;
- mNavigationBarView.getHomeButton().abortCurrentGesture();
- final boolean runBackMidGesture = !shouldExecuteBackOnUp(mContext);
- if (mCanPerformBack) {
- if (!shouldhideBackButton(mContext)) {
- mNavigationBarView.getBackButton().setAlpha(0 /* alpha */, true /* animate */,
- BACK_BUTTON_FADE_OUT_ALPHA);
- }
- if (runBackMidGesture) {
- performBack();
- }
- }
- mHandler.removeCallbacks(mExecuteBackRunnable);
- if (runBackMidGesture) {
- mHandler.postDelayed(mExecuteBackRunnable, BACK_GESTURE_POLL_TIMEOUT);
- }
- }
- }
-
- private void endBackGesture() {
- if (mBackGestureActive) {
- mHandler.removeCallbacks(mExecuteBackRunnable);
- mHomeAnimator = mNavigationBarView.getHomeButton().getCurrentView()
- .animate()
- .setDuration(BACK_BUTTON_FADE_IN_ALPHA)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- if (mIsVertical) {
- mHomeAnimator.translationY(0);
- } else {
- mHomeAnimator.translationX(0);
- }
- mHomeAnimator.start();
- if (!shouldhideBackButton(mContext)) {
- mNavigationBarView.getBackButton().setAlpha(
- mOverviewEventSender.getBackButtonAlpha(), true /* animate */);
- }
- if (shouldExecuteBackOnUp(mContext)) {
- performBack();
- }
- }
- }
-
- private void animateEnd() {
- if (mTrackAnimator != null) {
- mTrackAnimator.cancel();
- }
-
- ObjectAnimator trackAnimator = ObjectAnimator.ofPropertyValuesHolder(this,
- PropertyValuesHolder.ofFloat(mTrackAlphaProperty, 0f),
- PropertyValuesHolder.ofFloat(mTrackScaleProperty, TRACK_SCALE));
- trackAnimator.setInterpolator(ALPHA_OUT);
- trackAnimator.setDuration(ANIM_OUT_DURATION_MS);
- ObjectAnimator navBarAnimator = ObjectAnimator.ofFloat(this, mNavBarAlphaProperty, 1f);
- navBarAnimator.setInterpolator(ALPHA_IN);
- navBarAnimator.setDuration(ANIM_IN_DURATION_MS);
- mTrackAnimator = new AnimatorSet();
- mTrackAnimator.playTogether(trackAnimator, navBarAnimator);
- mTrackAnimator.addListener(mQuickScrubEndListener);
- mTrackAnimator.start();
- }
-
- private void resetQuickScrub() {
- mQuickScrubActive = false;
- mAllowGestureDetection = false;
- if (mCurrentNavigationBarView != null) {
- mCurrentNavigationBarView.setAlpha(1f);
- }
- mCurrentNavigationBarView = null;
- updateHighlight();
- }
-
- private void moveHomeButton(float pos) {
- if (mHomeAnimator != null) {
- mHomeAnimator.cancel();
- mHomeAnimator = null;
- }
- final View homeButton = mNavigationBarView.getHomeButton().getCurrentView();
- if (mIsVertical) {
- homeButton.setTranslationY(pos);
- } else {
- homeButton.setTranslationX(pos);
- }
- }
-
- private void updateHighlight() {
- if (mTrackRect.isEmpty()) {
- return;
- }
- int colorBase, colorGrad;
- if (mDarkIntensity > 0.5f) {
- colorBase = mContext.getColor(R.color.quick_step_track_background_background_dark);
- colorGrad = mContext.getColor(R.color.quick_step_track_background_foreground_dark);
- } else {
- colorBase = mContext.getColor(R.color.quick_step_track_background_background_light);
- colorGrad = mContext.getColor(R.color.quick_step_track_background_foreground_light);
- }
- mHighlight = new RadialGradient(0, mTrackRect.height() / 2,
- mTrackRect.width() * GRADIENT_WIDTH, colorGrad, colorBase,
- Shader.TileMode.CLAMP);
- mTrackPaint.setShader(mHighlight);
- }
-
- private boolean canPerformHomeBackGesture() {
- return swipeHomeGoBackGestureEnabled(mContext)
- && mOverviewEventSender.getBackButtonAlpha() > 0;
- }
-
- private void performBack() {
- sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
- sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
- mNavigationBarView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
- }
-
- private void sendEvent(int action, int code) {
- long when = SystemClock.uptimeMillis();
- final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
- 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
- KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
- InputDevice.SOURCE_KEYBOARD);
- InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+ return false;
}
private boolean proxyMotionEvents(MotionEvent event) {
@@ -740,22 +534,15 @@
return false;
}
- private static boolean getBoolGlobalSetting(Context context, String key) {
+ protected boolean isNavBarVertical() {
+ return mNavBarPosition == NAV_BAR_LEFT || mNavBarPosition == NAV_BAR_RIGHT;
+ }
+
+ static boolean getBoolGlobalSetting(Context context, String key) {
return Settings.Global.getInt(context.getContentResolver(), key, 0) != 0;
}
- public static boolean swipeHomeGoBackGestureEnabled(Context context) {
- return !getBoolGlobalSetting(context, NAVBAR_EXPERIMENTS_DISABLED)
- && getBoolGlobalSetting(context, PULL_HOME_GO_BACK_PROP);
- }
-
public static boolean shouldhideBackButton(Context context) {
- return swipeHomeGoBackGestureEnabled(context)
- && getBoolGlobalSetting(context, HIDE_BACK_BUTTON_PROP);
- }
-
- public static boolean shouldExecuteBackOnUp(Context context) {
- return !getBoolGlobalSetting(context, NAVBAR_EXPERIMENTS_DISABLED)
- && getBoolGlobalSetting(context, BACK_AFTER_END_PROP);
+ return getBoolGlobalSetting(context, HIDE_BACK_BUTTON_PROP);
}
}