blob: bce52a294e85be35afde0e2ff4b0c47cc1f148fe [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;
Winson Chung0d3815d2018-09-25 18:11:54 -070061import com.android.systemui.SysUiServiceProvider;
Matthew Nga8f24262017-12-19 11:54:24 -080062import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
63import com.android.systemui.shared.recents.IOverviewProxy;
64import com.android.systemui.shared.recents.utilities.Utilities;
Matthew Ngf29ad752018-04-26 11:24:05 -070065import com.android.systemui.shared.system.NavigationBarCompat;
Winson Chung0d3815d2018-09-25 18:11:54 -070066import java.io.PrintWriter;
Matthew Nga8f24262017-12-19 11:54:24 -080067
Matthew Nga8f24262017-12-19 11:54:24 -080068/**
Matthew Ngfba39692018-03-13 18:08:34 -070069 * Class to detect gestures on the navigation bar and implement quick scrub.
Matthew Nga8f24262017-12-19 11:54:24 -080070 */
Matthew Ngfba39692018-03-13 18:08:34 -070071public class QuickStepController implements GestureHelper {
Matthew Nga8f24262017-12-19 11:54:24 -080072
Matthew Ng2ea93b72018-03-14 19:43:18 +000073 private static final String TAG = "QuickStepController";
Winson Chung661d5f92018-05-21 18:41:39 -070074 private static final int ANIM_IN_DURATION_MS = 150;
Winson Chung4baf1242018-05-24 14:21:57 -070075 private static final int ANIM_OUT_DURATION_MS = 134;
76 private static final float TRACK_SCALE = 0.95f;
Matthew Ng4fa3a9d2018-06-12 16:42:11 -070077 private static final float GRADIENT_WIDTH = .75f;
Matthew Nga8f24262017-12-19 11:54:24 -080078
Matthew Ngc83b9892018-08-21 16:31:13 -070079 /** Experiment to swipe home button left to execute a back key press */
80 private static final String PULL_HOME_GO_BACK_PROP = "persist.quickstepcontroller.homegoesback";
81 private static final String HIDE_BACK_BUTTON_PROP = "persist.quickstepcontroller.hideback";
Matthew Ngc414e282018-09-05 15:50:10 -070082 private static final String BACK_AFTER_END_PROP
83 = "persist.quickstepcontroller.homegoesbackwhenend";
Matthew Ngc83b9892018-08-21 16:31:13 -070084 private static final long BACK_BUTTON_FADE_OUT_ALPHA = 60;
85 private static final long BACK_BUTTON_FADE_IN_ALPHA = 150;
86 private static final long BACK_GESTURE_POLL_TIMEOUT = 1000;
87
88 /** When the home-swipe-back gesture is disallowed, make it harder to pull */
89 private static final float DISALLOW_GESTURE_DAMPING_FACTOR = 0.16f;
90
Matthew Nga8f24262017-12-19 11:54:24 -080091 private NavigationBarView mNavigationBarView;
Matthew Nga8f24262017-12-19 11:54:24 -080092
Matthew Nga8f24262017-12-19 11:54:24 -080093 private boolean mQuickScrubActive;
Matthew Ng2ea93b72018-03-14 19:43:18 +000094 private boolean mAllowGestureDetection;
Matthew Ngc83b9892018-08-21 16:31:13 -070095 private boolean mBackGestureActive;
96 private boolean mCanPerformBack;
Matthew Ng2ea93b72018-03-14 19:43:18 +000097 private boolean mQuickStepStarted;
Matthew Ng90ef0632018-08-15 13:53:15 -070098 private boolean mNotificationsVisibleOnDown;
Matthew Nga8f24262017-12-19 11:54:24 -080099 private int mTouchDownX;
100 private int mTouchDownY;
101 private boolean mDragPositive;
102 private boolean mIsVertical;
103 private boolean mIsRTL;
Matthew Ng7090a802018-01-19 13:36:22 -0800104 private float mTrackAlpha;
Winson Chung4baf1242018-05-24 14:21:57 -0700105 private float mTrackScale = TRACK_SCALE;
Matthew Ng7090a802018-01-19 13:36:22 -0800106 private float mDarkIntensity;
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700107 private RadialGradient mHighlight;
108 private float mHighlightCenter;
Winson Chung661d5f92018-05-21 18:41:39 -0700109 private AnimatorSet mTrackAnimator;
Matthew Ngc83b9892018-08-21 16:31:13 -0700110 private ViewPropertyAnimator mHomeAnimator;
Winson Chung661d5f92018-05-21 18:41:39 -0700111 private ButtonDispatcher mHitTarget;
112 private View mCurrentNavigationBarView;
Matthew Ng20136e62018-05-30 12:20:31 -0700113 private boolean mIsInScreenPinning;
Matthew Nga8f24262017-12-19 11:54:24 -0800114
115 private final Handler mHandler = new Handler();
Matthew Nga8f24262017-12-19 11:54:24 -0800116 private final Rect mTrackRect = new Rect();
Matthew Nga8f24262017-12-19 11:54:24 -0800117 private final OverviewProxyService mOverviewEventSender;
Matthew Nga8f24262017-12-19 11:54:24 -0800118 private final int mTrackThickness;
Winson Chung661d5f92018-05-21 18:41:39 -0700119 private final int mTrackEndPadding;
Matthew Ngc83b9892018-08-21 16:31:13 -0700120 private final int mHomeBackGestureDragLimit;
Matthew Nga8f24262017-12-19 11:54:24 -0800121 private final Context mContext;
Winson Chung0d3815d2018-09-25 18:11:54 -0700122 private final StatusBar mStatusBar;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000123 private final Matrix mTransformGlobalMatrix = new Matrix();
124 private final Matrix mTransformLocalMatrix = new Matrix();
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700125 private final Paint mTrackPaint = new Paint();
Matthew Nga8f24262017-12-19 11:54:24 -0800126
Matthew Ngc83b9892018-08-21 16:31:13 -0700127 public static boolean swipeHomeGoBackGestureEnabled() {
128 return SystemProperties.getBoolean(PULL_HOME_GO_BACK_PROP, false);
129 }
130 public static boolean shouldhideBackButton() {
131 return swipeHomeGoBackGestureEnabled()
132 && SystemProperties.getBoolean(HIDE_BACK_BUTTON_PROP, false);
133 }
134
Winson Chung661d5f92018-05-21 18:41:39 -0700135 private final FloatProperty<QuickStepController> mTrackAlphaProperty =
136 new FloatProperty<QuickStepController>("TrackAlpha") {
137 @Override
138 public void setValue(QuickStepController controller, float alpha) {
139 mTrackAlpha = alpha;
140 mNavigationBarView.invalidate();
141 }
142
143 @Override
144 public Float get(QuickStepController controller) {
145 return mTrackAlpha;
146 }
Matthew Nga8f24262017-12-19 11:54:24 -0800147 };
148
Winson Chung4baf1242018-05-24 14:21:57 -0700149 private final FloatProperty<QuickStepController> mTrackScaleProperty =
150 new FloatProperty<QuickStepController>("TrackScale") {
151 @Override
152 public void setValue(QuickStepController controller, float scale) {
153 mTrackScale = scale;
154 mNavigationBarView.invalidate();
155 }
156
157 @Override
158 public Float get(QuickStepController controller) {
159 return mTrackScale;
160 }
161 };
162
Winson Chung661d5f92018-05-21 18:41:39 -0700163 private final FloatProperty<QuickStepController> mNavBarAlphaProperty =
164 new FloatProperty<QuickStepController>("NavBarAlpha") {
165 @Override
166 public void setValue(QuickStepController controller, float alpha) {
167 if (mCurrentNavigationBarView != null) {
168 mCurrentNavigationBarView.setAlpha(alpha);
169 }
Matthew Nga8f24262017-12-19 11:54:24 -0800170 }
Winson Chung661d5f92018-05-21 18:41:39 -0700171
172 @Override
173 public Float get(QuickStepController controller) {
174 if (mCurrentNavigationBarView != null) {
175 return mCurrentNavigationBarView.getAlpha();
176 }
177 return 1f;
Matthew Nga8f24262017-12-19 11:54:24 -0800178 }
179 };
180
181 private AnimatorListenerAdapter mQuickScrubEndListener = new AnimatorListenerAdapter() {
182 @Override
183 public void onAnimationEnd(Animator animation) {
Winson Chung661d5f92018-05-21 18:41:39 -0700184 resetQuickScrub();
Matthew Nga8f24262017-12-19 11:54:24 -0800185 }
186 };
187
Matthew Ngc83b9892018-08-21 16:31:13 -0700188 private final Runnable mExecuteBackRunnable = new Runnable() {
189 @Override
190 public void run() {
191 if (canPerformHomeBackGesture()) {
192 performBack();
193 mHandler.postDelayed(this, BACK_GESTURE_POLL_TIMEOUT);
194 }
195 }
196 };
197
Matthew Ng2ea93b72018-03-14 19:43:18 +0000198 public QuickStepController(Context context) {
Winson Chung661d5f92018-05-21 18:41:39 -0700199 final Resources res = context.getResources();
Matthew Nga8f24262017-12-19 11:54:24 -0800200 mContext = context;
Winson Chung0d3815d2018-09-25 18:11:54 -0700201 mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
Matthew Nga8f24262017-12-19 11:54:24 -0800202 mOverviewEventSender = Dependency.get(OverviewProxyService.class);
Winson Chung661d5f92018-05-21 18:41:39 -0700203 mTrackThickness = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_thickness);
204 mTrackEndPadding = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_edge_padding);
Matthew Ngc83b9892018-08-21 16:31:13 -0700205 mHomeBackGestureDragLimit =
206 res.getDimensionPixelSize(R.dimen.nav_home_back_gesture_drag_limit);
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700207 mTrackPaint.setAntiAlias(true);
208 mTrackPaint.setDither(true);
Matthew Nga8f24262017-12-19 11:54:24 -0800209 }
210
211 public void setComponents(NavigationBarView navigationBarView) {
212 mNavigationBarView = navigationBarView;
Matthew Ngc83b9892018-08-21 16:31:13 -0700213
214 mNavigationBarView.getBackButton().setVisibility(shouldhideBackButton()
215 ? View.GONE
216 : View.VISIBLE);
Matthew Nga8f24262017-12-19 11:54:24 -0800217 }
218
Winson Chung4faf38a2018-02-06 08:53:37 -0800219 /**
Matthew Ngfba39692018-03-13 18:08:34 -0700220 * @return true if we want to intercept touch events for quick scrub and prevent proxying the
221 * event to the overview service.
Winson Chung4faf38a2018-02-06 08:53:37 -0800222 */
Matthew Nga8f24262017-12-19 11:54:24 -0800223 @Override
224 public boolean onInterceptTouchEvent(MotionEvent event) {
Winson Chung0d3815d2018-09-25 18:11:54 -0700225 if (mStatusBar.isKeyguardShowing()) {
226 // Disallow any handling when the keyguard is showing
227 return false;
228 }
Winson Chungde2a1242018-02-07 15:59:43 -0800229 return handleTouchEvent(event);
230 }
231
232 /**
Matthew Ngfba39692018-03-13 18:08:34 -0700233 * @return true if we want to handle touch events for quick scrub or if down event (that will
234 * get consumed and ignored). No events will be proxied to the overview service.
Winson Chungde2a1242018-02-07 15:59:43 -0800235 */
236 @Override
237 public boolean onTouchEvent(MotionEvent event) {
Winson Chung0d3815d2018-09-25 18:11:54 -0700238 if (mStatusBar.isKeyguardShowing()) {
239 // Disallow any handling when the keyguard is showing
240 return false;
241 }
242
Matthew Ng2ea93b72018-03-14 19:43:18 +0000243 // The same down event was just sent on intercept and therefore can be ignored here
244 final boolean ignoreProxyDownEvent = event.getAction() == MotionEvent.ACTION_DOWN
245 && mOverviewEventSender.getProxy() != null;
246 return ignoreProxyDownEvent || handleTouchEvent(event);
Winson Chungde2a1242018-02-07 15:59:43 -0800247 }
248
249 private boolean handleTouchEvent(MotionEvent event) {
Matthew Ng472d3e42018-06-14 15:16:55 -0700250 final boolean deadZoneConsumed =
251 mNavigationBarView.getDownHitTarget() == HIT_TARGET_DEAD_ZONE;
Winson Chungd95a2252018-04-04 17:02:29 +0000252 if (mOverviewEventSender.getProxy() == null || (!mNavigationBarView.isQuickScrubEnabled()
253 && !mNavigationBarView.isQuickStepSwipeUpEnabled())) {
Matthew Ng2cbe8cd2018-08-20 11:27:40 -0700254 return deadZoneConsumed;
Matthew Ng22cf5142018-03-14 12:26:14 -0700255 }
256 mNavigationBarView.requestUnbufferedDispatch(event);
257
Matthew Ng9a223632018-03-30 16:47:22 -0700258 int action = event.getActionMasked();
259 switch (action) {
Matthew Nga8f24262017-12-19 11:54:24 -0800260 case MotionEvent.ACTION_DOWN: {
261 int x = (int) event.getX();
262 int y = (int) event.getY();
Matthew Ng20136e62018-05-30 12:20:31 -0700263 mIsInScreenPinning = mNavigationBarView.inScreenPinning();
Winson Chung661d5f92018-05-21 18:41:39 -0700264
Winson Chung0be8f082018-02-15 15:52:49 -0800265 // End any existing quickscrub animations before starting the new transition
Winson Chung661d5f92018-05-21 18:41:39 -0700266 if (mTrackAnimator != null) {
267 mTrackAnimator.end();
268 mTrackAnimator = null;
Winson Chung0be8f082018-02-15 15:52:49 -0800269 }
Winson Chung661d5f92018-05-21 18:41:39 -0700270
271 mCurrentNavigationBarView = mNavigationBarView.getCurrentView();
272 mHitTarget = mNavigationBarView.getButtonAtPosition(x, y);
273 if (mHitTarget != null) {
274 // Pre-emptively delay the touch feedback for the button that we just touched
275 mHitTarget.setDelayTouchFeedback(true);
276 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000277 mTouchDownX = x;
278 mTouchDownY = y;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000279 mTransformGlobalMatrix.set(Matrix.IDENTITY_MATRIX);
280 mTransformLocalMatrix.set(Matrix.IDENTITY_MATRIX);
281 mNavigationBarView.transformMatrixToGlobal(mTransformGlobalMatrix);
282 mNavigationBarView.transformMatrixToLocal(mTransformLocalMatrix);
283 mQuickStepStarted = false;
Matthew Ngc83b9892018-08-21 16:31:13 -0700284 mBackGestureActive = false;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000285 mAllowGestureDetection = true;
Matthew Ng90ef0632018-08-15 13:53:15 -0700286 mNotificationsVisibleOnDown = !mNavigationBarView.isNotificationsFullyCollapsed();
Matthew Ngc83b9892018-08-21 16:31:13 -0700287 mCanPerformBack = canPerformHomeBackGesture();
Matthew Nga8f24262017-12-19 11:54:24 -0800288 break;
289 }
290 case MotionEvent.ACTION_MOVE: {
Winson Chung661d5f92018-05-21 18:41:39 -0700291 if (mQuickStepStarted || !mAllowGestureDetection){
Matthew Ng2ea93b72018-03-14 19:43:18 +0000292 break;
293 }
294 int x = (int) event.getX();
295 int y = (int) event.getY();
296 int xDiff = Math.abs(x - mTouchDownX);
297 int yDiff = Math.abs(y - mTouchDownY);
Matthew Ng9a223632018-03-30 16:47:22 -0700298
Winson Chung661d5f92018-05-21 18:41:39 -0700299 boolean exceededScrubTouchSlop, exceededSwipeUpTouchSlop;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000300 int pos, touchDown, offset, trackSize;
Matthew Nge0903c92018-01-17 15:32:41 -0800301
Matthew Ng2ea93b72018-03-14 19:43:18 +0000302 if (mIsVertical) {
Matthew Ngf29ad752018-04-26 11:24:05 -0700303 exceededScrubTouchSlop =
304 yDiff > NavigationBarCompat.getQuickScrubTouchSlopPx() && yDiff > xDiff;
305 exceededSwipeUpTouchSlop =
306 xDiff > NavigationBarCompat.getQuickStepTouchSlopPx() && xDiff > yDiff;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000307 pos = y;
308 touchDown = mTouchDownY;
309 offset = pos - mTrackRect.top;
310 trackSize = mTrackRect.height();
311 } else {
Matthew Ngf29ad752018-04-26 11:24:05 -0700312 exceededScrubTouchSlop =
313 xDiff > NavigationBarCompat.getQuickScrubTouchSlopPx() && xDiff > yDiff;
314 exceededSwipeUpTouchSlop =
315 yDiff > NavigationBarCompat.getQuickStepTouchSlopPx() && yDiff > xDiff;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000316 pos = x;
317 touchDown = mTouchDownX;
318 offset = pos - mTrackRect.left;
319 trackSize = mTrackRect.width();
320 }
321 // Decide to start quickstep if dragging away from the navigation bar, otherwise in
322 // the parallel direction, decide to start quickscrub. Only one may run.
Matthew Ngc83b9892018-08-21 16:31:13 -0700323 if (!mBackGestureActive && !mQuickScrubActive && exceededSwipeUpTouchSlop) {
Matthew Ng90ef0632018-08-15 13:53:15 -0700324 if (mNavigationBarView.isQuickStepSwipeUpEnabled()
325 && !mNotificationsVisibleOnDown) {
Matthew Ng2ea93b72018-03-14 19:43:18 +0000326 startQuickStep(event);
Winson Chung0e490d922018-03-14 16:08:43 +0000327 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000328 break;
329 }
Winson Chung0e490d922018-03-14 16:08:43 +0000330
Winson Chung661d5f92018-05-21 18:41:39 -0700331 // Do not handle quick scrub if disabled
332 if (!mNavigationBarView.isQuickScrubEnabled()) {
Matthew Ng2ea93b72018-03-14 19:43:18 +0000333 break;
334 }
335
336 if (!mDragPositive) {
337 offset -= mIsVertical ? mTrackRect.height() : mTrackRect.width();
338 }
339
Matthew Ng9a223632018-03-30 16:47:22 -0700340 final boolean allowDrag = !mDragPositive
341 ? offset < 0 && pos < touchDown : offset >= 0 && pos > touchDown;
Winson Chung661d5f92018-05-21 18:41:39 -0700342 float scrubFraction = Utilities.clamp(Math.abs(offset) * 1f / trackSize, 0, 1);
Matthew Ngc83b9892018-08-21 16:31:13 -0700343 if (!mQuickScrubActive && !mBackGestureActive && exceededScrubTouchSlop) {
Matthew Ng9a223632018-03-30 16:47:22 -0700344 // Passing the drag slop then touch slop will start quick step
Matthew Ngc83b9892018-08-21 16:31:13 -0700345 if (allowDrag) {
Matthew Ngfba39692018-03-13 18:08:34 -0700346 startQuickScrub();
Matthew Ngc83b9892018-08-21 16:31:13 -0700347 } else if (swipeHomeGoBackGestureEnabled()
348 && mNavigationBarView.getDownHitTarget() == HIT_TARGET_HOME
349 && mDragPositive ? pos < touchDown : pos > touchDown) {
350 startBackGesture();
Winson Chung0e490d922018-03-14 16:08:43 +0000351 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000352 }
Matthew Ng9a223632018-03-30 16:47:22 -0700353
Winson Chung661d5f92018-05-21 18:41:39 -0700354 if (mQuickScrubActive && (mDragPositive && offset >= 0
Matthew Ng2ea93b72018-03-14 19:43:18 +0000355 || !mDragPositive && offset <= 0)) {
Winson Chung661d5f92018-05-21 18:41:39 -0700356 try {
357 mOverviewEventSender.getProxy().onQuickScrubProgress(scrubFraction);
358 if (DEBUG_OVERVIEW_PROXY) {
359 Log.d(TAG_OPS, "Quick Scrub Progress:" + scrubFraction);
Winson Chung0e490d922018-03-14 16:08:43 +0000360 }
Winson Chung661d5f92018-05-21 18:41:39 -0700361 } catch (RemoteException e) {
362 Log.e(TAG, "Failed to send progress of quick scrub.", e);
Matthew Nga8f24262017-12-19 11:54:24 -0800363 }
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700364 mHighlightCenter = x;
365 mNavigationBarView.invalidate();
Matthew Ngc83b9892018-08-21 16:31:13 -0700366 } else if (mBackGestureActive) {
367 int diff = pos - touchDown;
368 // If dragging the incorrect direction after starting back gesture or unable
369 // to execute back functionality, then move home but dampen its distance
370 if (!mCanPerformBack || (mDragPositive ? diff > 0 : diff < 0)) {
371 diff *= DISALLOW_GESTURE_DAMPING_FACTOR;
372 } if (Math.abs(diff) > mHomeBackGestureDragLimit) {
373 // Once the user drags the home button past a certain limit, the distance
374 // will lessen as the home button dampens showing that it was pulled too far
375 float distanceAfterDragLimit = (Math.abs(diff) - mHomeBackGestureDragLimit)
Matthew Ngc414e282018-09-05 15:50:10 -0700376 * DISALLOW_GESTURE_DAMPING_FACTOR;
Matthew Ngc83b9892018-08-21 16:31:13 -0700377 diff = (int)(distanceAfterDragLimit + mHomeBackGestureDragLimit);
378 if (mDragPositive) {
379 diff *= -1;
380 }
381 }
382 moveHomeButton(diff);
Matthew Nga8f24262017-12-19 11:54:24 -0800383 }
384 break;
385 }
386 case MotionEvent.ACTION_CANCEL:
387 case MotionEvent.ACTION_UP:
Winson Chungd10ca302018-02-14 10:13:41 -0800388 endQuickScrub(true /* animate */);
Matthew Ngc83b9892018-08-21 16:31:13 -0700389 endBackGesture();
Matthew Nga8f24262017-12-19 11:54:24 -0800390 break;
391 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000392
Matthew Ng90ef0632018-08-15 13:53:15 -0700393 if (shouldProxyEvents(action)) {
Matthew Ng2ea93b72018-03-14 19:43:18 +0000394 proxyMotionEvents(event);
395 }
Matthew Ngc83b9892018-08-21 16:31:13 -0700396 return mBackGestureActive || mQuickScrubActive || mQuickStepStarted || deadZoneConsumed;
Matthew Nga8f24262017-12-19 11:54:24 -0800397 }
398
Matthew Ng90ef0632018-08-15 13:53:15 -0700399 private boolean shouldProxyEvents(int action) {
Matthew Ngc83b9892018-08-21 16:31:13 -0700400 if (!mBackGestureActive && !mQuickScrubActive && !mIsInScreenPinning) {
Matthew Ng90ef0632018-08-15 13:53:15 -0700401 // Allow down, cancel and up events, move and other events are passed if notifications
402 // are not showing and disabled gestures (such as long press) are not executed
403 switch (action) {
404 case MotionEvent.ACTION_DOWN:
405 case MotionEvent.ACTION_CANCEL:
406 case MotionEvent.ACTION_UP:
407 return true;
408 default:
409 return !mNotificationsVisibleOnDown && mAllowGestureDetection;
410 }
411 }
412 return false;
413 }
414
Matthew Nga8f24262017-12-19 11:54:24 -0800415 @Override
416 public void onDraw(Canvas canvas) {
Matthew Ngf781bbd2018-03-21 14:58:55 -0700417 if (!mNavigationBarView.isQuickScrubEnabled()) {
Matthew Ng2ea93b72018-03-14 19:43:18 +0000418 return;
419 }
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700420 mTrackPaint.setAlpha(Math.round(255f * mTrackAlpha));
Winson Chung4baf1242018-05-24 14:21:57 -0700421
422 // Scale the track, but apply the inverse scale from the nav bar
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700423 final float radius = mTrackRect.height() / 2;
Winson Chung4baf1242018-05-24 14:21:57 -0700424 canvas.save();
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700425 float translate = Utilities.clamp(mHighlightCenter, mTrackRect.left, mTrackRect.right);
426 canvas.translate(translate, 0);
Winson Chung4baf1242018-05-24 14:21:57 -0700427 canvas.scale(mTrackScale / mNavigationBarView.getScaleX(),
428 1f / mNavigationBarView.getScaleY(),
429 mTrackRect.centerX(), mTrackRect.centerY());
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700430 canvas.drawRoundRect(mTrackRect.left - translate, mTrackRect.top,
431 mTrackRect.right - translate, mTrackRect.bottom, radius, radius, mTrackPaint);
Winson Chung4baf1242018-05-24 14:21:57 -0700432 canvas.restore();
Matthew Nga8f24262017-12-19 11:54:24 -0800433 }
434
435 @Override
436 public void onLayout(boolean changed, int left, int top, int right, int bottom) {
Winson Chung661d5f92018-05-21 18:41:39 -0700437 final int paddingLeft = mNavigationBarView.getPaddingLeft();
438 final int paddingTop = mNavigationBarView.getPaddingTop();
439 final int paddingRight = mNavigationBarView.getPaddingRight();
440 final int paddingBottom = mNavigationBarView.getPaddingBottom();
441 final int width = (right - left) - paddingRight - paddingLeft;
442 final int height = (bottom - top) - paddingBottom - paddingTop;
Matthew Nga8f24262017-12-19 11:54:24 -0800443 final int x1, x2, y1, y2;
444 if (mIsVertical) {
Winson Chung661d5f92018-05-21 18:41:39 -0700445 x1 = (width - mTrackThickness) / 2 + paddingLeft;
Matthew Nga8f24262017-12-19 11:54:24 -0800446 x2 = x1 + mTrackThickness;
Winson Chung661d5f92018-05-21 18:41:39 -0700447 y1 = paddingTop + mTrackEndPadding;
448 y2 = y1 + height - 2 * mTrackEndPadding;
Matthew Nga8f24262017-12-19 11:54:24 -0800449 } else {
Winson Chung661d5f92018-05-21 18:41:39 -0700450 y1 = (height - mTrackThickness) / 2 + paddingTop;
Matthew Nga8f24262017-12-19 11:54:24 -0800451 y2 = y1 + mTrackThickness;
Winson Chung661d5f92018-05-21 18:41:39 -0700452 x1 = mNavigationBarView.getPaddingStart() + mTrackEndPadding;
453 x2 = x1 + width - 2 * mTrackEndPadding;
Matthew Nga8f24262017-12-19 11:54:24 -0800454 }
455 mTrackRect.set(x1, y1, x2, y2);
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700456 updateHighlight();
Matthew Nga8f24262017-12-19 11:54:24 -0800457 }
458
459 @Override
460 public void onDarkIntensityChange(float intensity) {
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700461 final float oldIntensity = mDarkIntensity;
Matthew Ng7090a802018-01-19 13:36:22 -0800462 mDarkIntensity = intensity;
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700463
464 // When in quick scrub, invalidate gradient if changing intensity from black to white and
465 // vice-versa
466 if (mNavigationBarView.isQuickScrubEnabled()
467 && Math.round(intensity) != Math.round(oldIntensity)) {
468 updateHighlight();
469 }
Matthew Ng7090a802018-01-19 13:36:22 -0800470 mNavigationBarView.invalidate();
Matthew Nga8f24262017-12-19 11:54:24 -0800471 }
472
473 @Override
Matthew Nga8f24262017-12-19 11:54:24 -0800474 public void setBarState(boolean isVertical, boolean isRTL) {
Winson Chungd10ca302018-02-14 10:13:41 -0800475 final boolean changed = (mIsVertical != isVertical) || (mIsRTL != isRTL);
476 if (changed) {
477 // End quickscrub if the state changes mid-transition
478 endQuickScrub(false /* animate */);
479 }
Matthew Nga8f24262017-12-19 11:54:24 -0800480 mIsVertical = isVertical;
481 mIsRTL = isRTL;
482 try {
483 int navbarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
484 mDragPositive = navbarPos == NAV_BAR_LEFT || navbarPos == NAV_BAR_BOTTOM;
485 if (isRTL) {
486 mDragPositive = !mDragPositive;
487 }
488 } catch (RemoteException e) {
489 Slog.e(TAG, "Failed to get nav bar position.", e);
490 }
491 }
492
Matthew Ng2ea93b72018-03-14 19:43:18 +0000493 @Override
494 public void onNavigationButtonLongPress(View v) {
495 mAllowGestureDetection = false;
496 mHandler.removeCallbacksAndMessages(null);
497 }
498
Winson Chung0d3815d2018-09-25 18:11:54 -0700499 @Override
500 public void dump(PrintWriter pw) {
501 pw.println("QuickStepController {");
502 pw.print(" "); pw.println("mQuickScrubActive=" + mQuickScrubActive);
503 pw.print(" "); pw.println("mQuickStepStarted=" + mQuickStepStarted);
504 pw.print(" "); pw.println("mAllowGestureDetection=" + mAllowGestureDetection);
505 pw.print(" "); pw.println("mBackGestureActive=" + mBackGestureActive);
506 pw.print(" "); pw.println("mCanPerformBack=" + mCanPerformBack);
507 pw.print(" "); pw.println("mNotificationsVisibleOnDown=" + mNotificationsVisibleOnDown);
508 pw.print(" "); pw.println("mIsVertical=" + mIsVertical);
509 pw.print(" "); pw.println("mIsRTL=" + mIsRTL);
510 pw.print(" "); pw.println("mIsInScreenPinning=" + mIsInScreenPinning);
511 pw.println("}");
512 }
513
Matthew Ng2ea93b72018-03-14 19:43:18 +0000514 private void startQuickStep(MotionEvent event) {
Matthew Ng20136e62018-05-30 12:20:31 -0700515 if (mIsInScreenPinning) {
516 mNavigationBarView.showPinningEscapeToast();
517 mAllowGestureDetection = false;
518 return;
519 }
520
Matthew Ng2ea93b72018-03-14 19:43:18 +0000521 mQuickStepStarted = true;
522 event.transform(mTransformGlobalMatrix);
523 try {
524 mOverviewEventSender.getProxy().onQuickStep(event);
525 if (DEBUG_OVERVIEW_PROXY) {
526 Log.d(TAG_OPS, "Quick Step Start");
527 }
528 } catch (RemoteException e) {
529 Log.e(TAG, "Failed to send quick step started.", e);
530 } finally {
531 event.transform(mTransformLocalMatrix);
532 }
533 mOverviewEventSender.notifyQuickStepStarted();
Matthew Ng2ea93b72018-03-14 19:43:18 +0000534 mHandler.removeCallbacksAndMessages(null);
Matthew Ng9a223632018-03-30 16:47:22 -0700535
Winson Chung661d5f92018-05-21 18:41:39 -0700536 if (mHitTarget != null) {
537 mHitTarget.abortCurrentGesture();
538 }
539
540 if (mQuickScrubActive) {
Matthew Ng9a223632018-03-30 16:47:22 -0700541 animateEnd();
542 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000543 }
544
Matthew Nga8f24262017-12-19 11:54:24 -0800545 private void startQuickScrub() {
Matthew Ng20136e62018-05-30 12:20:31 -0700546 if (mIsInScreenPinning) {
547 mNavigationBarView.showPinningEscapeToast();
548 mAllowGestureDetection = false;
549 return;
550 }
551
Winson Chung661d5f92018-05-21 18:41:39 -0700552 if (!mQuickScrubActive) {
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700553 updateHighlight();
Matthew Nga8f24262017-12-19 11:54:24 -0800554 mQuickScrubActive = true;
Winson Chung4baf1242018-05-24 14:21:57 -0700555 ObjectAnimator trackAnimator = ObjectAnimator.ofPropertyValuesHolder(this,
556 PropertyValuesHolder.ofFloat(mTrackAlphaProperty, 1f),
557 PropertyValuesHolder.ofFloat(mTrackScaleProperty, 1f));
Winson Chung661d5f92018-05-21 18:41:39 -0700558 trackAnimator.setInterpolator(ALPHA_IN);
559 trackAnimator.setDuration(ANIM_IN_DURATION_MS);
560 ObjectAnimator navBarAnimator = ObjectAnimator.ofFloat(this, mNavBarAlphaProperty, 0f);
561 navBarAnimator.setInterpolator(ALPHA_OUT);
562 navBarAnimator.setDuration(ANIM_OUT_DURATION_MS);
563 mTrackAnimator = new AnimatorSet();
564 mTrackAnimator.playTogether(trackAnimator, navBarAnimator);
565 mTrackAnimator.start();
Matthew Ng6607c3d2018-04-26 15:23:27 -0700566
Matthew Ng472d3e42018-06-14 15:16:55 -0700567 // Disable slippery for quick scrub to not cancel outside the nav bar
568 mNavigationBarView.updateSlippery();
569
Matthew Nga8f24262017-12-19 11:54:24 -0800570 try {
571 mOverviewEventSender.getProxy().onQuickScrubStart();
Matthew Ngbd824572018-01-17 16:25:56 -0800572 if (DEBUG_OVERVIEW_PROXY) {
573 Log.d(TAG_OPS, "Quick Scrub Start");
574 }
Matthew Nga8f24262017-12-19 11:54:24 -0800575 } catch (RemoteException e) {
576 Log.e(TAG, "Failed to send start of quick scrub.", e);
577 }
Tracy Zhou27599052018-04-16 15:47:29 -0700578 mOverviewEventSender.notifyQuickScrubStarted();
Winson Chung661d5f92018-05-21 18:41:39 -0700579
580 if (mHitTarget != null) {
581 mHitTarget.abortCurrentGesture();
582 }
Matthew Nga8f24262017-12-19 11:54:24 -0800583 }
584 }
585
Winson Chungd10ca302018-02-14 10:13:41 -0800586 private void endQuickScrub(boolean animate) {
Winson Chung661d5f92018-05-21 18:41:39 -0700587 if (mQuickScrubActive) {
Matthew Nged166f92018-02-20 16:22:09 -0800588 animateEnd();
Winson Chung661d5f92018-05-21 18:41:39 -0700589 try {
590 mOverviewEventSender.getProxy().onQuickScrubEnd();
591 if (DEBUG_OVERVIEW_PROXY) {
592 Log.d(TAG_OPS, "Quick Scrub End");
Matthew Ngbd824572018-01-17 16:25:56 -0800593 }
Winson Chung661d5f92018-05-21 18:41:39 -0700594 } catch (RemoteException e) {
595 Log.e(TAG, "Failed to send end of quick scrub.", e);
Matthew Nga8f24262017-12-19 11:54:24 -0800596 }
Matthew Nged166f92018-02-20 16:22:09 -0800597 }
Winson Chung661d5f92018-05-21 18:41:39 -0700598 if (!animate) {
599 if (mTrackAnimator != null) {
600 mTrackAnimator.end();
601 mTrackAnimator = null;
602 }
Matthew Nga8f24262017-12-19 11:54:24 -0800603 }
Matthew Nga8f24262017-12-19 11:54:24 -0800604 }
605
Matthew Ngc83b9892018-08-21 16:31:13 -0700606 private void startBackGesture() {
607 if (!mBackGestureActive) {
608 mBackGestureActive = true;
609 mNavigationBarView.getHomeButton().abortCurrentGesture();
Matthew Ngc414e282018-09-05 15:50:10 -0700610 final boolean runBackMidGesture
611 = !SystemProperties.getBoolean(BACK_AFTER_END_PROP, false);
Matthew Ngc83b9892018-08-21 16:31:13 -0700612 if (mCanPerformBack) {
613 if (!shouldhideBackButton()) {
614 mNavigationBarView.getBackButton().setAlpha(0 /* alpha */, true /* animate */,
615 BACK_BUTTON_FADE_OUT_ALPHA);
616 }
Matthew Ngc414e282018-09-05 15:50:10 -0700617 if (runBackMidGesture) {
618 performBack();
619 }
Matthew Ngc83b9892018-08-21 16:31:13 -0700620 }
621 mHandler.removeCallbacks(mExecuteBackRunnable);
Matthew Ngc414e282018-09-05 15:50:10 -0700622 if (runBackMidGesture) {
623 mHandler.postDelayed(mExecuteBackRunnable, BACK_GESTURE_POLL_TIMEOUT);
624 }
Matthew Ngc83b9892018-08-21 16:31:13 -0700625 }
626 }
627
628 private void endBackGesture() {
629 if (mBackGestureActive) {
630 mHandler.removeCallbacks(mExecuteBackRunnable);
631 mHomeAnimator = mNavigationBarView.getHomeButton().getCurrentView()
632 .animate()
633 .setDuration(BACK_BUTTON_FADE_IN_ALPHA)
634 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
635 if (mIsVertical) {
636 mHomeAnimator.translationY(0);
637 } else {
638 mHomeAnimator.translationX(0);
639 }
640 mHomeAnimator.start();
641 if (!shouldhideBackButton()) {
642 mNavigationBarView.getBackButton().setAlpha(
643 mOverviewEventSender.getBackButtonAlpha(), true /* animate */);
644 }
Matthew Ngc414e282018-09-05 15:50:10 -0700645 if (SystemProperties.getBoolean(BACK_AFTER_END_PROP, false)) {
646 performBack();
647 }
Matthew Ngc83b9892018-08-21 16:31:13 -0700648 }
649 }
650
Winson Chung661d5f92018-05-21 18:41:39 -0700651 private void animateEnd() {
652 if (mTrackAnimator != null) {
653 mTrackAnimator.cancel();
654 }
655
Winson Chung4baf1242018-05-24 14:21:57 -0700656 ObjectAnimator trackAnimator = ObjectAnimator.ofPropertyValuesHolder(this,
657 PropertyValuesHolder.ofFloat(mTrackAlphaProperty, 0f),
658 PropertyValuesHolder.ofFloat(mTrackScaleProperty, TRACK_SCALE));
Winson Chung661d5f92018-05-21 18:41:39 -0700659 trackAnimator.setInterpolator(ALPHA_OUT);
660 trackAnimator.setDuration(ANIM_OUT_DURATION_MS);
661 ObjectAnimator navBarAnimator = ObjectAnimator.ofFloat(this, mNavBarAlphaProperty, 1f);
662 navBarAnimator.setInterpolator(ALPHA_IN);
663 navBarAnimator.setDuration(ANIM_IN_DURATION_MS);
664 mTrackAnimator = new AnimatorSet();
665 mTrackAnimator.playTogether(trackAnimator, navBarAnimator);
666 mTrackAnimator.addListener(mQuickScrubEndListener);
667 mTrackAnimator.start();
668 }
669
670 private void resetQuickScrub() {
671 mQuickScrubActive = false;
672 mAllowGestureDetection = false;
Matthew Ng819daaa2018-07-23 15:12:57 -0700673 if (mCurrentNavigationBarView != null) {
674 mCurrentNavigationBarView.setAlpha(1f);
675 }
Winson Chung661d5f92018-05-21 18:41:39 -0700676 mCurrentNavigationBarView = null;
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700677 updateHighlight();
678 }
679
Matthew Ngc83b9892018-08-21 16:31:13 -0700680 private void moveHomeButton(float pos) {
681 if (mHomeAnimator != null) {
682 mHomeAnimator.cancel();
683 mHomeAnimator = null;
684 }
685 final View homeButton = mNavigationBarView.getHomeButton().getCurrentView();
686 if (mIsVertical) {
687 homeButton.setTranslationY(pos);
688 } else {
689 homeButton.setTranslationX(pos);
690 }
691 }
692
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700693 private void updateHighlight() {
Matthew Ng908111d2018-06-26 15:22:56 -0700694 if (mTrackRect.isEmpty()) {
695 return;
696 }
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700697 int colorBase, colorGrad;
698 if (mDarkIntensity > 0.5f) {
699 colorBase = mContext.getColor(R.color.quick_step_track_background_background_dark);
700 colorGrad = mContext.getColor(R.color.quick_step_track_background_foreground_dark);
701 } else {
702 colorBase = mContext.getColor(R.color.quick_step_track_background_background_light);
703 colorGrad = mContext.getColor(R.color.quick_step_track_background_foreground_light);
704 }
705 mHighlight = new RadialGradient(0, mTrackRect.height() / 2,
706 mTrackRect.width() * GRADIENT_WIDTH, colorGrad, colorBase,
707 Shader.TileMode.CLAMP);
708 mTrackPaint.setShader(mHighlight);
Winson Chung661d5f92018-05-21 18:41:39 -0700709 }
710
Matthew Ngc83b9892018-08-21 16:31:13 -0700711 private boolean canPerformHomeBackGesture() {
712 return swipeHomeGoBackGestureEnabled() && mOverviewEventSender.getBackButtonAlpha() > 0;
713 }
714
715 private void performBack() {
716 sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
717 sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
718 mNavigationBarView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
719 }
720
721 private void sendEvent(int action, int code) {
722 long when = SystemClock.uptimeMillis();
723 final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
724 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
725 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
726 InputDevice.SOURCE_KEYBOARD);
727 InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
728 }
729
Matthew Ng2ea93b72018-03-14 19:43:18 +0000730 private boolean proxyMotionEvents(MotionEvent event) {
731 final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
732 event.transform(mTransformGlobalMatrix);
733 try {
734 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
735 overviewProxy.onPreMotionEvent(mNavigationBarView.getDownHitTarget());
736 }
737 overviewProxy.onMotionEvent(event);
738 if (DEBUG_OVERVIEW_PROXY) {
739 Log.d(TAG_OPS, "Send MotionEvent: " + event.toString());
740 }
741 return true;
742 } catch (RemoteException e) {
743 Log.e(TAG, "Callback failed", e);
744 } finally {
745 event.transform(mTransformLocalMatrix);
Matthew Ngdb2734c2018-02-16 16:02:20 -0800746 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000747 return false;
Matthew Ngdb2734c2018-02-16 16:02:20 -0800748 }
Matthew Nga8f24262017-12-19 11:54:24 -0800749}