blob: bd4d790a00b0a60783a0ce0caf646650484bfd1d [file] [log] [blame]
Matthew Nga8f24262017-12-19 11:54:24 -08001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.systemui.statusbar.phone;
18
Winson Chung661d5f92018-05-21 18:41:39 -070019import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
20import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
21import static com.android.systemui.Interpolators.ALPHA_IN;
22import static com.android.systemui.Interpolators.ALPHA_OUT;
23import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
24import static com.android.systemui.OverviewProxyService.TAG_OPS;
Matthew Ng472d3e42018-06-14 15:16:55 -070025import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
Matthew Ngc83b9892018-08-21 16:31:13 -070026import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
Winson Chung661d5f92018-05-21 18:41:39 -070027
Matthew Nga8f24262017-12-19 11:54:24 -080028import android.animation.Animator;
29import android.animation.AnimatorListenerAdapter;
30import android.animation.AnimatorSet;
31import android.animation.ObjectAnimator;
Winson Chung661d5f92018-05-21 18:41:39 -070032import android.animation.PropertyValuesHolder;
Matthew Nga8f24262017-12-19 11:54:24 -080033import android.content.Context;
Winson Chung661d5f92018-05-21 18:41:39 -070034import android.content.res.Resources;
Matthew Nga8f24262017-12-19 11:54:24 -080035import android.graphics.Canvas;
Matthew Ng2ea93b72018-03-14 19:43:18 +000036import android.graphics.Matrix;
Matthew Ng4fa3a9d2018-06-12 16:42:11 -070037import android.graphics.Paint;
38import android.graphics.RadialGradient;
Matthew Nga8f24262017-12-19 11:54:24 -080039import android.graphics.Rect;
Matthew Ng4fa3a9d2018-06-12 16:42:11 -070040import android.graphics.Shader;
Matthew Ngc83b9892018-08-21 16:31:13 -070041import android.hardware.input.InputManager;
Matthew Nga8f24262017-12-19 11:54:24 -080042import android.os.Handler;
43import android.os.RemoteException;
Matthew Ngc83b9892018-08-21 16:31:13 -070044import android.os.SystemClock;
45import android.os.SystemProperties;
Winson Chung661d5f92018-05-21 18:41:39 -070046import android.util.FloatProperty;
Matthew Nga8f24262017-12-19 11:54:24 -080047import android.util.Log;
48import android.util.Slog;
Matthew Ngc83b9892018-08-21 16:31:13 -070049import android.view.HapticFeedbackConstants;
50import android.view.InputDevice;
51import android.view.KeyCharacterMap;
52import android.view.KeyEvent;
Matthew Nga8f24262017-12-19 11:54:24 -080053import android.view.MotionEvent;
54import android.view.View;
Matthew Ngc83b9892018-08-21 16:31:13 -070055import android.view.ViewPropertyAnimator;
Matthew Nga8f24262017-12-19 11:54:24 -080056import android.view.WindowManagerGlobal;
Matthew Nga8f24262017-12-19 11:54:24 -080057import com.android.systemui.Dependency;
Matthew Ngc83b9892018-08-21 16:31:13 -070058import com.android.systemui.Interpolators;
Matthew Nga8f24262017-12-19 11:54:24 -080059import com.android.systemui.OverviewProxyService;
60import com.android.systemui.R;
61import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
62import com.android.systemui.shared.recents.IOverviewProxy;
63import com.android.systemui.shared.recents.utilities.Utilities;
Matthew Ngf29ad752018-04-26 11:24:05 -070064import com.android.systemui.shared.system.NavigationBarCompat;
Matthew Nga8f24262017-12-19 11:54:24 -080065
Matthew Nga8f24262017-12-19 11:54:24 -080066/**
Matthew Ngfba39692018-03-13 18:08:34 -070067 * Class to detect gestures on the navigation bar and implement quick scrub.
Matthew Nga8f24262017-12-19 11:54:24 -080068 */
Matthew Ngfba39692018-03-13 18:08:34 -070069public class QuickStepController implements GestureHelper {
Matthew Nga8f24262017-12-19 11:54:24 -080070
Matthew Ng2ea93b72018-03-14 19:43:18 +000071 private static final String TAG = "QuickStepController";
Winson Chung661d5f92018-05-21 18:41:39 -070072 private static final int ANIM_IN_DURATION_MS = 150;
Winson Chung4baf1242018-05-24 14:21:57 -070073 private static final int ANIM_OUT_DURATION_MS = 134;
74 private static final float TRACK_SCALE = 0.95f;
Matthew Ng4fa3a9d2018-06-12 16:42:11 -070075 private static final float GRADIENT_WIDTH = .75f;
Matthew Nga8f24262017-12-19 11:54:24 -080076
Matthew Ngc83b9892018-08-21 16:31:13 -070077 /** Experiment to swipe home button left to execute a back key press */
78 private static final String PULL_HOME_GO_BACK_PROP = "persist.quickstepcontroller.homegoesback";
79 private static final String HIDE_BACK_BUTTON_PROP = "persist.quickstepcontroller.hideback";
80 private static final long BACK_BUTTON_FADE_OUT_ALPHA = 60;
81 private static final long BACK_BUTTON_FADE_IN_ALPHA = 150;
82 private static final long BACK_GESTURE_POLL_TIMEOUT = 1000;
83
84 /** When the home-swipe-back gesture is disallowed, make it harder to pull */
85 private static final float DISALLOW_GESTURE_DAMPING_FACTOR = 0.16f;
86
87 /** When dragging the home button too far during back gesture, make it harder to pull */
88 private static final float EXCEED_DRAG_HOME_DAMPING_FACTOR = 0.33f;
89
Matthew Nga8f24262017-12-19 11:54:24 -080090 private NavigationBarView mNavigationBarView;
Matthew Nga8f24262017-12-19 11:54:24 -080091
Matthew Nga8f24262017-12-19 11:54:24 -080092 private boolean mQuickScrubActive;
Matthew Ng2ea93b72018-03-14 19:43:18 +000093 private boolean mAllowGestureDetection;
Matthew Ngc83b9892018-08-21 16:31:13 -070094 private boolean mBackGestureActive;
95 private boolean mCanPerformBack;
Matthew Ng2ea93b72018-03-14 19:43:18 +000096 private boolean mQuickStepStarted;
Matthew Ng90ef0632018-08-15 13:53:15 -070097 private boolean mNotificationsVisibleOnDown;
Matthew Nga8f24262017-12-19 11:54:24 -080098 private int mTouchDownX;
99 private int mTouchDownY;
100 private boolean mDragPositive;
101 private boolean mIsVertical;
102 private boolean mIsRTL;
Matthew Ng7090a802018-01-19 13:36:22 -0800103 private float mTrackAlpha;
Winson Chung4baf1242018-05-24 14:21:57 -0700104 private float mTrackScale = TRACK_SCALE;
Matthew Ng7090a802018-01-19 13:36:22 -0800105 private float mDarkIntensity;
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700106 private RadialGradient mHighlight;
107 private float mHighlightCenter;
Winson Chung661d5f92018-05-21 18:41:39 -0700108 private AnimatorSet mTrackAnimator;
Matthew Ngc83b9892018-08-21 16:31:13 -0700109 private ViewPropertyAnimator mHomeAnimator;
Winson Chung661d5f92018-05-21 18:41:39 -0700110 private ButtonDispatcher mHitTarget;
111 private View mCurrentNavigationBarView;
Matthew Ng20136e62018-05-30 12:20:31 -0700112 private boolean mIsInScreenPinning;
Matthew Nga8f24262017-12-19 11:54:24 -0800113
114 private final Handler mHandler = new Handler();
Matthew Nga8f24262017-12-19 11:54:24 -0800115 private final Rect mTrackRect = new Rect();
Matthew Nga8f24262017-12-19 11:54:24 -0800116 private final OverviewProxyService mOverviewEventSender;
Matthew Nga8f24262017-12-19 11:54:24 -0800117 private final int mTrackThickness;
Winson Chung661d5f92018-05-21 18:41:39 -0700118 private final int mTrackEndPadding;
Matthew Ngc83b9892018-08-21 16:31:13 -0700119 private final int mHomeBackGestureDragLimit;
Matthew Nga8f24262017-12-19 11:54:24 -0800120 private final Context mContext;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000121 private final Matrix mTransformGlobalMatrix = new Matrix();
122 private final Matrix mTransformLocalMatrix = new Matrix();
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700123 private final Paint mTrackPaint = new Paint();
Matthew Nga8f24262017-12-19 11:54:24 -0800124
Matthew Ngc83b9892018-08-21 16:31:13 -0700125 public static boolean swipeHomeGoBackGestureEnabled() {
126 return SystemProperties.getBoolean(PULL_HOME_GO_BACK_PROP, false);
127 }
128 public static boolean shouldhideBackButton() {
129 return swipeHomeGoBackGestureEnabled()
130 && SystemProperties.getBoolean(HIDE_BACK_BUTTON_PROP, false);
131 }
132
Winson Chung661d5f92018-05-21 18:41:39 -0700133 private final FloatProperty<QuickStepController> mTrackAlphaProperty =
134 new FloatProperty<QuickStepController>("TrackAlpha") {
135 @Override
136 public void setValue(QuickStepController controller, float alpha) {
137 mTrackAlpha = alpha;
138 mNavigationBarView.invalidate();
139 }
140
141 @Override
142 public Float get(QuickStepController controller) {
143 return mTrackAlpha;
144 }
Matthew Nga8f24262017-12-19 11:54:24 -0800145 };
146
Winson Chung4baf1242018-05-24 14:21:57 -0700147 private final FloatProperty<QuickStepController> mTrackScaleProperty =
148 new FloatProperty<QuickStepController>("TrackScale") {
149 @Override
150 public void setValue(QuickStepController controller, float scale) {
151 mTrackScale = scale;
152 mNavigationBarView.invalidate();
153 }
154
155 @Override
156 public Float get(QuickStepController controller) {
157 return mTrackScale;
158 }
159 };
160
Winson Chung661d5f92018-05-21 18:41:39 -0700161 private final FloatProperty<QuickStepController> mNavBarAlphaProperty =
162 new FloatProperty<QuickStepController>("NavBarAlpha") {
163 @Override
164 public void setValue(QuickStepController controller, float alpha) {
165 if (mCurrentNavigationBarView != null) {
166 mCurrentNavigationBarView.setAlpha(alpha);
167 }
Matthew Nga8f24262017-12-19 11:54:24 -0800168 }
Winson Chung661d5f92018-05-21 18:41:39 -0700169
170 @Override
171 public Float get(QuickStepController controller) {
172 if (mCurrentNavigationBarView != null) {
173 return mCurrentNavigationBarView.getAlpha();
174 }
175 return 1f;
Matthew Nga8f24262017-12-19 11:54:24 -0800176 }
177 };
178
179 private AnimatorListenerAdapter mQuickScrubEndListener = new AnimatorListenerAdapter() {
180 @Override
181 public void onAnimationEnd(Animator animation) {
Winson Chung661d5f92018-05-21 18:41:39 -0700182 resetQuickScrub();
Matthew Nga8f24262017-12-19 11:54:24 -0800183 }
184 };
185
Matthew Ngc83b9892018-08-21 16:31:13 -0700186 private final Runnable mExecuteBackRunnable = new Runnable() {
187 @Override
188 public void run() {
189 if (canPerformHomeBackGesture()) {
190 performBack();
191 mHandler.postDelayed(this, BACK_GESTURE_POLL_TIMEOUT);
192 }
193 }
194 };
195
Matthew Ng2ea93b72018-03-14 19:43:18 +0000196 public QuickStepController(Context context) {
Winson Chung661d5f92018-05-21 18:41:39 -0700197 final Resources res = context.getResources();
Matthew Nga8f24262017-12-19 11:54:24 -0800198 mContext = context;
Matthew Nga8f24262017-12-19 11:54:24 -0800199 mOverviewEventSender = Dependency.get(OverviewProxyService.class);
Winson Chung661d5f92018-05-21 18:41:39 -0700200 mTrackThickness = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_thickness);
201 mTrackEndPadding = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_edge_padding);
Matthew Ngc83b9892018-08-21 16:31:13 -0700202 mHomeBackGestureDragLimit =
203 res.getDimensionPixelSize(R.dimen.nav_home_back_gesture_drag_limit);
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700204 mTrackPaint.setAntiAlias(true);
205 mTrackPaint.setDither(true);
Matthew Nga8f24262017-12-19 11:54:24 -0800206 }
207
208 public void setComponents(NavigationBarView navigationBarView) {
209 mNavigationBarView = navigationBarView;
Matthew Ngc83b9892018-08-21 16:31:13 -0700210
211 mNavigationBarView.getBackButton().setVisibility(shouldhideBackButton()
212 ? View.GONE
213 : View.VISIBLE);
Matthew Nga8f24262017-12-19 11:54:24 -0800214 }
215
Winson Chung4faf38a2018-02-06 08:53:37 -0800216 /**
Matthew Ngfba39692018-03-13 18:08:34 -0700217 * @return true if we want to intercept touch events for quick scrub and prevent proxying the
218 * event to the overview service.
Winson Chung4faf38a2018-02-06 08:53:37 -0800219 */
Matthew Nga8f24262017-12-19 11:54:24 -0800220 @Override
221 public boolean onInterceptTouchEvent(MotionEvent event) {
Winson Chungde2a1242018-02-07 15:59:43 -0800222 return handleTouchEvent(event);
223 }
224
225 /**
Matthew Ngfba39692018-03-13 18:08:34 -0700226 * @return true if we want to handle touch events for quick scrub or if down event (that will
227 * get consumed and ignored). No events will be proxied to the overview service.
Winson Chungde2a1242018-02-07 15:59:43 -0800228 */
229 @Override
230 public boolean onTouchEvent(MotionEvent event) {
Matthew Ng2ea93b72018-03-14 19:43:18 +0000231 // The same down event was just sent on intercept and therefore can be ignored here
232 final boolean ignoreProxyDownEvent = event.getAction() == MotionEvent.ACTION_DOWN
233 && mOverviewEventSender.getProxy() != null;
234 return ignoreProxyDownEvent || handleTouchEvent(event);
Winson Chungde2a1242018-02-07 15:59:43 -0800235 }
236
237 private boolean handleTouchEvent(MotionEvent event) {
Matthew Ng472d3e42018-06-14 15:16:55 -0700238 final boolean deadZoneConsumed =
239 mNavigationBarView.getDownHitTarget() == HIT_TARGET_DEAD_ZONE;
Winson Chungd95a2252018-04-04 17:02:29 +0000240 if (mOverviewEventSender.getProxy() == null || (!mNavigationBarView.isQuickScrubEnabled()
241 && !mNavigationBarView.isQuickStepSwipeUpEnabled())) {
Matthew Ng2cbe8cd2018-08-20 11:27:40 -0700242 return deadZoneConsumed;
Matthew Ng22cf5142018-03-14 12:26:14 -0700243 }
244 mNavigationBarView.requestUnbufferedDispatch(event);
245
Matthew Ng9a223632018-03-30 16:47:22 -0700246 int action = event.getActionMasked();
247 switch (action) {
Matthew Nga8f24262017-12-19 11:54:24 -0800248 case MotionEvent.ACTION_DOWN: {
249 int x = (int) event.getX();
250 int y = (int) event.getY();
Matthew Ng20136e62018-05-30 12:20:31 -0700251 mIsInScreenPinning = mNavigationBarView.inScreenPinning();
Winson Chung661d5f92018-05-21 18:41:39 -0700252
Winson Chung0be8f082018-02-15 15:52:49 -0800253 // End any existing quickscrub animations before starting the new transition
Winson Chung661d5f92018-05-21 18:41:39 -0700254 if (mTrackAnimator != null) {
255 mTrackAnimator.end();
256 mTrackAnimator = null;
Winson Chung0be8f082018-02-15 15:52:49 -0800257 }
Winson Chung661d5f92018-05-21 18:41:39 -0700258
259 mCurrentNavigationBarView = mNavigationBarView.getCurrentView();
260 mHitTarget = mNavigationBarView.getButtonAtPosition(x, y);
261 if (mHitTarget != null) {
262 // Pre-emptively delay the touch feedback for the button that we just touched
263 mHitTarget.setDelayTouchFeedback(true);
264 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000265 mTouchDownX = x;
266 mTouchDownY = y;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000267 mTransformGlobalMatrix.set(Matrix.IDENTITY_MATRIX);
268 mTransformLocalMatrix.set(Matrix.IDENTITY_MATRIX);
269 mNavigationBarView.transformMatrixToGlobal(mTransformGlobalMatrix);
270 mNavigationBarView.transformMatrixToLocal(mTransformLocalMatrix);
271 mQuickStepStarted = false;
Matthew Ngc83b9892018-08-21 16:31:13 -0700272 mBackGestureActive = false;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000273 mAllowGestureDetection = true;
Matthew Ng90ef0632018-08-15 13:53:15 -0700274 mNotificationsVisibleOnDown = !mNavigationBarView.isNotificationsFullyCollapsed();
Matthew Ngc83b9892018-08-21 16:31:13 -0700275 mCanPerformBack = canPerformHomeBackGesture();
Matthew Nga8f24262017-12-19 11:54:24 -0800276 break;
277 }
278 case MotionEvent.ACTION_MOVE: {
Winson Chung661d5f92018-05-21 18:41:39 -0700279 if (mQuickStepStarted || !mAllowGestureDetection){
Matthew Ng2ea93b72018-03-14 19:43:18 +0000280 break;
281 }
282 int x = (int) event.getX();
283 int y = (int) event.getY();
284 int xDiff = Math.abs(x - mTouchDownX);
285 int yDiff = Math.abs(y - mTouchDownY);
Matthew Ng9a223632018-03-30 16:47:22 -0700286
Winson Chung661d5f92018-05-21 18:41:39 -0700287 boolean exceededScrubTouchSlop, exceededSwipeUpTouchSlop;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000288 int pos, touchDown, offset, trackSize;
Matthew Nge0903c92018-01-17 15:32:41 -0800289
Matthew Ng2ea93b72018-03-14 19:43:18 +0000290 if (mIsVertical) {
Matthew Ngf29ad752018-04-26 11:24:05 -0700291 exceededScrubTouchSlop =
292 yDiff > NavigationBarCompat.getQuickScrubTouchSlopPx() && yDiff > xDiff;
293 exceededSwipeUpTouchSlop =
294 xDiff > NavigationBarCompat.getQuickStepTouchSlopPx() && xDiff > yDiff;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000295 pos = y;
296 touchDown = mTouchDownY;
297 offset = pos - mTrackRect.top;
298 trackSize = mTrackRect.height();
299 } else {
Matthew Ngf29ad752018-04-26 11:24:05 -0700300 exceededScrubTouchSlop =
301 xDiff > NavigationBarCompat.getQuickScrubTouchSlopPx() && xDiff > yDiff;
302 exceededSwipeUpTouchSlop =
303 yDiff > NavigationBarCompat.getQuickStepTouchSlopPx() && yDiff > xDiff;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000304 pos = x;
305 touchDown = mTouchDownX;
306 offset = pos - mTrackRect.left;
307 trackSize = mTrackRect.width();
308 }
309 // Decide to start quickstep if dragging away from the navigation bar, otherwise in
310 // the parallel direction, decide to start quickscrub. Only one may run.
Matthew Ngc83b9892018-08-21 16:31:13 -0700311 if (!mBackGestureActive && !mQuickScrubActive && exceededSwipeUpTouchSlop) {
Matthew Ng90ef0632018-08-15 13:53:15 -0700312 if (mNavigationBarView.isQuickStepSwipeUpEnabled()
313 && !mNotificationsVisibleOnDown) {
Matthew Ng2ea93b72018-03-14 19:43:18 +0000314 startQuickStep(event);
Winson Chung0e490d922018-03-14 16:08:43 +0000315 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000316 break;
317 }
Winson Chung0e490d922018-03-14 16:08:43 +0000318
Winson Chung661d5f92018-05-21 18:41:39 -0700319 // Do not handle quick scrub if disabled
320 if (!mNavigationBarView.isQuickScrubEnabled()) {
Matthew Ng2ea93b72018-03-14 19:43:18 +0000321 break;
322 }
323
324 if (!mDragPositive) {
325 offset -= mIsVertical ? mTrackRect.height() : mTrackRect.width();
326 }
327
Matthew Ng9a223632018-03-30 16:47:22 -0700328 final boolean allowDrag = !mDragPositive
329 ? offset < 0 && pos < touchDown : offset >= 0 && pos > touchDown;
Winson Chung661d5f92018-05-21 18:41:39 -0700330 float scrubFraction = Utilities.clamp(Math.abs(offset) * 1f / trackSize, 0, 1);
Matthew Ngc83b9892018-08-21 16:31:13 -0700331 if (!mQuickScrubActive && !mBackGestureActive && exceededScrubTouchSlop) {
Matthew Ng9a223632018-03-30 16:47:22 -0700332 // Passing the drag slop then touch slop will start quick step
Matthew Ngc83b9892018-08-21 16:31:13 -0700333 if (allowDrag) {
Matthew Ngfba39692018-03-13 18:08:34 -0700334 startQuickScrub();
Matthew Ngc83b9892018-08-21 16:31:13 -0700335 } else if (swipeHomeGoBackGestureEnabled()
336 && mNavigationBarView.getDownHitTarget() == HIT_TARGET_HOME
337 && mDragPositive ? pos < touchDown : pos > touchDown) {
338 startBackGesture();
Winson Chung0e490d922018-03-14 16:08:43 +0000339 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000340 }
Matthew Ng9a223632018-03-30 16:47:22 -0700341
Winson Chung661d5f92018-05-21 18:41:39 -0700342 if (mQuickScrubActive && (mDragPositive && offset >= 0
Matthew Ng2ea93b72018-03-14 19:43:18 +0000343 || !mDragPositive && offset <= 0)) {
Winson Chung661d5f92018-05-21 18:41:39 -0700344 try {
345 mOverviewEventSender.getProxy().onQuickScrubProgress(scrubFraction);
346 if (DEBUG_OVERVIEW_PROXY) {
347 Log.d(TAG_OPS, "Quick Scrub Progress:" + scrubFraction);
Winson Chung0e490d922018-03-14 16:08:43 +0000348 }
Winson Chung661d5f92018-05-21 18:41:39 -0700349 } catch (RemoteException e) {
350 Log.e(TAG, "Failed to send progress of quick scrub.", e);
Matthew Nga8f24262017-12-19 11:54:24 -0800351 }
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700352 mHighlightCenter = x;
353 mNavigationBarView.invalidate();
Matthew Ngc83b9892018-08-21 16:31:13 -0700354 } else if (mBackGestureActive) {
355 int diff = pos - touchDown;
356 // If dragging the incorrect direction after starting back gesture or unable
357 // to execute back functionality, then move home but dampen its distance
358 if (!mCanPerformBack || (mDragPositive ? diff > 0 : diff < 0)) {
359 diff *= DISALLOW_GESTURE_DAMPING_FACTOR;
360 } if (Math.abs(diff) > mHomeBackGestureDragLimit) {
361 // Once the user drags the home button past a certain limit, the distance
362 // will lessen as the home button dampens showing that it was pulled too far
363 float distanceAfterDragLimit = (Math.abs(diff) - mHomeBackGestureDragLimit)
364 * EXCEED_DRAG_HOME_DAMPING_FACTOR;
365 diff = (int)(distanceAfterDragLimit + mHomeBackGestureDragLimit);
366 if (mDragPositive) {
367 diff *= -1;
368 }
369 }
370 moveHomeButton(diff);
Matthew Nga8f24262017-12-19 11:54:24 -0800371 }
372 break;
373 }
374 case MotionEvent.ACTION_CANCEL:
375 case MotionEvent.ACTION_UP:
Winson Chungd10ca302018-02-14 10:13:41 -0800376 endQuickScrub(true /* animate */);
Matthew Ngc83b9892018-08-21 16:31:13 -0700377 endBackGesture();
Matthew Nga8f24262017-12-19 11:54:24 -0800378 break;
379 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000380
Matthew Ng90ef0632018-08-15 13:53:15 -0700381 if (shouldProxyEvents(action)) {
Matthew Ng2ea93b72018-03-14 19:43:18 +0000382 proxyMotionEvents(event);
383 }
Matthew Ngc83b9892018-08-21 16:31:13 -0700384 return mBackGestureActive || mQuickScrubActive || mQuickStepStarted || deadZoneConsumed;
Matthew Nga8f24262017-12-19 11:54:24 -0800385 }
386
Matthew Ng90ef0632018-08-15 13:53:15 -0700387 private boolean shouldProxyEvents(int action) {
Matthew Ngc83b9892018-08-21 16:31:13 -0700388 if (!mBackGestureActive && !mQuickScrubActive && !mIsInScreenPinning) {
Matthew Ng90ef0632018-08-15 13:53:15 -0700389 // Allow down, cancel and up events, move and other events are passed if notifications
390 // are not showing and disabled gestures (such as long press) are not executed
391 switch (action) {
392 case MotionEvent.ACTION_DOWN:
393 case MotionEvent.ACTION_CANCEL:
394 case MotionEvent.ACTION_UP:
395 return true;
396 default:
397 return !mNotificationsVisibleOnDown && mAllowGestureDetection;
398 }
399 }
400 return false;
401 }
402
Matthew Nga8f24262017-12-19 11:54:24 -0800403 @Override
404 public void onDraw(Canvas canvas) {
Matthew Ngf781bbd2018-03-21 14:58:55 -0700405 if (!mNavigationBarView.isQuickScrubEnabled()) {
Matthew Ng2ea93b72018-03-14 19:43:18 +0000406 return;
407 }
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700408 mTrackPaint.setAlpha(Math.round(255f * mTrackAlpha));
Winson Chung4baf1242018-05-24 14:21:57 -0700409
410 // Scale the track, but apply the inverse scale from the nav bar
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700411 final float radius = mTrackRect.height() / 2;
Winson Chung4baf1242018-05-24 14:21:57 -0700412 canvas.save();
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700413 float translate = Utilities.clamp(mHighlightCenter, mTrackRect.left, mTrackRect.right);
414 canvas.translate(translate, 0);
Winson Chung4baf1242018-05-24 14:21:57 -0700415 canvas.scale(mTrackScale / mNavigationBarView.getScaleX(),
416 1f / mNavigationBarView.getScaleY(),
417 mTrackRect.centerX(), mTrackRect.centerY());
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700418 canvas.drawRoundRect(mTrackRect.left - translate, mTrackRect.top,
419 mTrackRect.right - translate, mTrackRect.bottom, radius, radius, mTrackPaint);
Winson Chung4baf1242018-05-24 14:21:57 -0700420 canvas.restore();
Matthew Nga8f24262017-12-19 11:54:24 -0800421 }
422
423 @Override
424 public void onLayout(boolean changed, int left, int top, int right, int bottom) {
Winson Chung661d5f92018-05-21 18:41:39 -0700425 final int paddingLeft = mNavigationBarView.getPaddingLeft();
426 final int paddingTop = mNavigationBarView.getPaddingTop();
427 final int paddingRight = mNavigationBarView.getPaddingRight();
428 final int paddingBottom = mNavigationBarView.getPaddingBottom();
429 final int width = (right - left) - paddingRight - paddingLeft;
430 final int height = (bottom - top) - paddingBottom - paddingTop;
Matthew Nga8f24262017-12-19 11:54:24 -0800431 final int x1, x2, y1, y2;
432 if (mIsVertical) {
Winson Chung661d5f92018-05-21 18:41:39 -0700433 x1 = (width - mTrackThickness) / 2 + paddingLeft;
Matthew Nga8f24262017-12-19 11:54:24 -0800434 x2 = x1 + mTrackThickness;
Winson Chung661d5f92018-05-21 18:41:39 -0700435 y1 = paddingTop + mTrackEndPadding;
436 y2 = y1 + height - 2 * mTrackEndPadding;
Matthew Nga8f24262017-12-19 11:54:24 -0800437 } else {
Winson Chung661d5f92018-05-21 18:41:39 -0700438 y1 = (height - mTrackThickness) / 2 + paddingTop;
Matthew Nga8f24262017-12-19 11:54:24 -0800439 y2 = y1 + mTrackThickness;
Winson Chung661d5f92018-05-21 18:41:39 -0700440 x1 = mNavigationBarView.getPaddingStart() + mTrackEndPadding;
441 x2 = x1 + width - 2 * mTrackEndPadding;
Matthew Nga8f24262017-12-19 11:54:24 -0800442 }
443 mTrackRect.set(x1, y1, x2, y2);
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700444 updateHighlight();
Matthew Nga8f24262017-12-19 11:54:24 -0800445 }
446
447 @Override
448 public void onDarkIntensityChange(float intensity) {
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700449 final float oldIntensity = mDarkIntensity;
Matthew Ng7090a802018-01-19 13:36:22 -0800450 mDarkIntensity = intensity;
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700451
452 // When in quick scrub, invalidate gradient if changing intensity from black to white and
453 // vice-versa
454 if (mNavigationBarView.isQuickScrubEnabled()
455 && Math.round(intensity) != Math.round(oldIntensity)) {
456 updateHighlight();
457 }
Matthew Ng7090a802018-01-19 13:36:22 -0800458 mNavigationBarView.invalidate();
Matthew Nga8f24262017-12-19 11:54:24 -0800459 }
460
461 @Override
Matthew Nga8f24262017-12-19 11:54:24 -0800462 public void setBarState(boolean isVertical, boolean isRTL) {
Winson Chungd10ca302018-02-14 10:13:41 -0800463 final boolean changed = (mIsVertical != isVertical) || (mIsRTL != isRTL);
464 if (changed) {
465 // End quickscrub if the state changes mid-transition
466 endQuickScrub(false /* animate */);
467 }
Matthew Nga8f24262017-12-19 11:54:24 -0800468 mIsVertical = isVertical;
469 mIsRTL = isRTL;
470 try {
471 int navbarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
472 mDragPositive = navbarPos == NAV_BAR_LEFT || navbarPos == NAV_BAR_BOTTOM;
473 if (isRTL) {
474 mDragPositive = !mDragPositive;
475 }
476 } catch (RemoteException e) {
477 Slog.e(TAG, "Failed to get nav bar position.", e);
478 }
479 }
480
Matthew Ng2ea93b72018-03-14 19:43:18 +0000481 @Override
482 public void onNavigationButtonLongPress(View v) {
483 mAllowGestureDetection = false;
484 mHandler.removeCallbacksAndMessages(null);
485 }
486
487 private void startQuickStep(MotionEvent event) {
Matthew Ng20136e62018-05-30 12:20:31 -0700488 if (mIsInScreenPinning) {
489 mNavigationBarView.showPinningEscapeToast();
490 mAllowGestureDetection = false;
491 return;
492 }
493
Matthew Ng2ea93b72018-03-14 19:43:18 +0000494 mQuickStepStarted = true;
495 event.transform(mTransformGlobalMatrix);
496 try {
497 mOverviewEventSender.getProxy().onQuickStep(event);
498 if (DEBUG_OVERVIEW_PROXY) {
499 Log.d(TAG_OPS, "Quick Step Start");
500 }
501 } catch (RemoteException e) {
502 Log.e(TAG, "Failed to send quick step started.", e);
503 } finally {
504 event.transform(mTransformLocalMatrix);
505 }
506 mOverviewEventSender.notifyQuickStepStarted();
Matthew Ng2ea93b72018-03-14 19:43:18 +0000507 mHandler.removeCallbacksAndMessages(null);
Matthew Ng9a223632018-03-30 16:47:22 -0700508
Winson Chung661d5f92018-05-21 18:41:39 -0700509 if (mHitTarget != null) {
510 mHitTarget.abortCurrentGesture();
511 }
512
513 if (mQuickScrubActive) {
Matthew Ng9a223632018-03-30 16:47:22 -0700514 animateEnd();
515 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000516 }
517
Matthew Nga8f24262017-12-19 11:54:24 -0800518 private void startQuickScrub() {
Matthew Ng20136e62018-05-30 12:20:31 -0700519 if (mIsInScreenPinning) {
520 mNavigationBarView.showPinningEscapeToast();
521 mAllowGestureDetection = false;
522 return;
523 }
524
Winson Chung661d5f92018-05-21 18:41:39 -0700525 if (!mQuickScrubActive) {
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700526 updateHighlight();
Matthew Nga8f24262017-12-19 11:54:24 -0800527 mQuickScrubActive = true;
Winson Chung4baf1242018-05-24 14:21:57 -0700528 ObjectAnimator trackAnimator = ObjectAnimator.ofPropertyValuesHolder(this,
529 PropertyValuesHolder.ofFloat(mTrackAlphaProperty, 1f),
530 PropertyValuesHolder.ofFloat(mTrackScaleProperty, 1f));
Winson Chung661d5f92018-05-21 18:41:39 -0700531 trackAnimator.setInterpolator(ALPHA_IN);
532 trackAnimator.setDuration(ANIM_IN_DURATION_MS);
533 ObjectAnimator navBarAnimator = ObjectAnimator.ofFloat(this, mNavBarAlphaProperty, 0f);
534 navBarAnimator.setInterpolator(ALPHA_OUT);
535 navBarAnimator.setDuration(ANIM_OUT_DURATION_MS);
536 mTrackAnimator = new AnimatorSet();
537 mTrackAnimator.playTogether(trackAnimator, navBarAnimator);
538 mTrackAnimator.start();
Matthew Ng6607c3d2018-04-26 15:23:27 -0700539
Matthew Ng472d3e42018-06-14 15:16:55 -0700540 // Disable slippery for quick scrub to not cancel outside the nav bar
541 mNavigationBarView.updateSlippery();
542
Matthew Nga8f24262017-12-19 11:54:24 -0800543 try {
544 mOverviewEventSender.getProxy().onQuickScrubStart();
Matthew Ngbd824572018-01-17 16:25:56 -0800545 if (DEBUG_OVERVIEW_PROXY) {
546 Log.d(TAG_OPS, "Quick Scrub Start");
547 }
Matthew Nga8f24262017-12-19 11:54:24 -0800548 } catch (RemoteException e) {
549 Log.e(TAG, "Failed to send start of quick scrub.", e);
550 }
Tracy Zhou27599052018-04-16 15:47:29 -0700551 mOverviewEventSender.notifyQuickScrubStarted();
Winson Chung661d5f92018-05-21 18:41:39 -0700552
553 if (mHitTarget != null) {
554 mHitTarget.abortCurrentGesture();
555 }
Matthew Nga8f24262017-12-19 11:54:24 -0800556 }
557 }
558
Winson Chungd10ca302018-02-14 10:13:41 -0800559 private void endQuickScrub(boolean animate) {
Winson Chung661d5f92018-05-21 18:41:39 -0700560 if (mQuickScrubActive) {
Matthew Nged166f92018-02-20 16:22:09 -0800561 animateEnd();
Winson Chung661d5f92018-05-21 18:41:39 -0700562 try {
563 mOverviewEventSender.getProxy().onQuickScrubEnd();
564 if (DEBUG_OVERVIEW_PROXY) {
565 Log.d(TAG_OPS, "Quick Scrub End");
Matthew Ngbd824572018-01-17 16:25:56 -0800566 }
Winson Chung661d5f92018-05-21 18:41:39 -0700567 } catch (RemoteException e) {
568 Log.e(TAG, "Failed to send end of quick scrub.", e);
Matthew Nga8f24262017-12-19 11:54:24 -0800569 }
Matthew Nged166f92018-02-20 16:22:09 -0800570 }
Winson Chung661d5f92018-05-21 18:41:39 -0700571 if (!animate) {
572 if (mTrackAnimator != null) {
573 mTrackAnimator.end();
574 mTrackAnimator = null;
575 }
Matthew Nga8f24262017-12-19 11:54:24 -0800576 }
Matthew Nga8f24262017-12-19 11:54:24 -0800577 }
578
Matthew Ngc83b9892018-08-21 16:31:13 -0700579 private void startBackGesture() {
580 if (!mBackGestureActive) {
581 mBackGestureActive = true;
582 mNavigationBarView.getHomeButton().abortCurrentGesture();
583 if (mCanPerformBack) {
584 if (!shouldhideBackButton()) {
585 mNavigationBarView.getBackButton().setAlpha(0 /* alpha */, true /* animate */,
586 BACK_BUTTON_FADE_OUT_ALPHA);
587 }
588 performBack();
589 }
590 mHandler.removeCallbacks(mExecuteBackRunnable);
591 mHandler.postDelayed(mExecuteBackRunnable, BACK_GESTURE_POLL_TIMEOUT);
592 }
593 }
594
595 private void endBackGesture() {
596 if (mBackGestureActive) {
597 mHandler.removeCallbacks(mExecuteBackRunnable);
598 mHomeAnimator = mNavigationBarView.getHomeButton().getCurrentView()
599 .animate()
600 .setDuration(BACK_BUTTON_FADE_IN_ALPHA)
601 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
602 if (mIsVertical) {
603 mHomeAnimator.translationY(0);
604 } else {
605 mHomeAnimator.translationX(0);
606 }
607 mHomeAnimator.start();
608 if (!shouldhideBackButton()) {
609 mNavigationBarView.getBackButton().setAlpha(
610 mOverviewEventSender.getBackButtonAlpha(), true /* animate */);
611 }
612 }
613 }
614
Winson Chung661d5f92018-05-21 18:41:39 -0700615 private void animateEnd() {
616 if (mTrackAnimator != null) {
617 mTrackAnimator.cancel();
618 }
619
Winson Chung4baf1242018-05-24 14:21:57 -0700620 ObjectAnimator trackAnimator = ObjectAnimator.ofPropertyValuesHolder(this,
621 PropertyValuesHolder.ofFloat(mTrackAlphaProperty, 0f),
622 PropertyValuesHolder.ofFloat(mTrackScaleProperty, TRACK_SCALE));
Winson Chung661d5f92018-05-21 18:41:39 -0700623 trackAnimator.setInterpolator(ALPHA_OUT);
624 trackAnimator.setDuration(ANIM_OUT_DURATION_MS);
625 ObjectAnimator navBarAnimator = ObjectAnimator.ofFloat(this, mNavBarAlphaProperty, 1f);
626 navBarAnimator.setInterpolator(ALPHA_IN);
627 navBarAnimator.setDuration(ANIM_IN_DURATION_MS);
628 mTrackAnimator = new AnimatorSet();
629 mTrackAnimator.playTogether(trackAnimator, navBarAnimator);
630 mTrackAnimator.addListener(mQuickScrubEndListener);
631 mTrackAnimator.start();
632 }
633
634 private void resetQuickScrub() {
635 mQuickScrubActive = false;
636 mAllowGestureDetection = false;
Matthew Ng819daaa2018-07-23 15:12:57 -0700637 if (mCurrentNavigationBarView != null) {
638 mCurrentNavigationBarView.setAlpha(1f);
639 }
Winson Chung661d5f92018-05-21 18:41:39 -0700640 mCurrentNavigationBarView = null;
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700641 updateHighlight();
642 }
643
Matthew Ngc83b9892018-08-21 16:31:13 -0700644 private void moveHomeButton(float pos) {
645 if (mHomeAnimator != null) {
646 mHomeAnimator.cancel();
647 mHomeAnimator = null;
648 }
649 final View homeButton = mNavigationBarView.getHomeButton().getCurrentView();
650 if (mIsVertical) {
651 homeButton.setTranslationY(pos);
652 } else {
653 homeButton.setTranslationX(pos);
654 }
655 }
656
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700657 private void updateHighlight() {
Matthew Ng908111d2018-06-26 15:22:56 -0700658 if (mTrackRect.isEmpty()) {
659 return;
660 }
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700661 int colorBase, colorGrad;
662 if (mDarkIntensity > 0.5f) {
663 colorBase = mContext.getColor(R.color.quick_step_track_background_background_dark);
664 colorGrad = mContext.getColor(R.color.quick_step_track_background_foreground_dark);
665 } else {
666 colorBase = mContext.getColor(R.color.quick_step_track_background_background_light);
667 colorGrad = mContext.getColor(R.color.quick_step_track_background_foreground_light);
668 }
669 mHighlight = new RadialGradient(0, mTrackRect.height() / 2,
670 mTrackRect.width() * GRADIENT_WIDTH, colorGrad, colorBase,
671 Shader.TileMode.CLAMP);
672 mTrackPaint.setShader(mHighlight);
Winson Chung661d5f92018-05-21 18:41:39 -0700673 }
674
Matthew Ngc83b9892018-08-21 16:31:13 -0700675 private boolean canPerformHomeBackGesture() {
676 return swipeHomeGoBackGestureEnabled() && mOverviewEventSender.getBackButtonAlpha() > 0;
677 }
678
679 private void performBack() {
680 sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
681 sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
682 mNavigationBarView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
683 }
684
685 private void sendEvent(int action, int code) {
686 long when = SystemClock.uptimeMillis();
687 final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
688 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
689 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
690 InputDevice.SOURCE_KEYBOARD);
691 InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
692 }
693
Matthew Ng2ea93b72018-03-14 19:43:18 +0000694 private boolean proxyMotionEvents(MotionEvent event) {
695 final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
696 event.transform(mTransformGlobalMatrix);
697 try {
698 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
699 overviewProxy.onPreMotionEvent(mNavigationBarView.getDownHitTarget());
700 }
701 overviewProxy.onMotionEvent(event);
702 if (DEBUG_OVERVIEW_PROXY) {
703 Log.d(TAG_OPS, "Send MotionEvent: " + event.toString());
704 }
705 return true;
706 } catch (RemoteException e) {
707 Log.e(TAG, "Callback failed", e);
708 } finally {
709 event.transform(mTransformLocalMatrix);
Matthew Ngdb2734c2018-02-16 16:02:20 -0800710 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000711 return false;
Matthew Ngdb2734c2018-02-16 16:02:20 -0800712 }
Matthew Nga8f24262017-12-19 11:54:24 -0800713}