blob: 0954fd054e2d3352bb10e575865e122bd121a449 [file] [log] [blame]
Jorim Jaggidd98d412015-11-18 15:57:38 -08001/*
2 * Copyright (C) 2014 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
Jorim Jaggi213cbe62015-12-02 18:17:07 -080019import android.app.ActivityManager;
Jorim Jaggidd98d412015-11-18 15:57:38 -080020import android.content.Context;
Jorim Jaggidd98d412015-11-18 15:57:38 -080021import android.content.res.Resources;
Matthew Nga8f24262017-12-19 11:54:24 -080022import android.graphics.Canvas;
Matthew Ng8c039fd2017-11-15 11:09:44 -080023import android.graphics.Matrix;
Jorim Jaggi9ea2f7b2015-11-23 18:08:28 -080024import android.graphics.Rect;
Matthew Ng13dbf872017-10-27 11:02:14 -070025import android.os.RemoteException;
26import android.util.Log;
Jorim Jaggidd98d412015-11-18 15:57:38 -080027import android.view.MotionEvent;
28import android.view.VelocityTracker;
Jorim Jaggi298c0812016-01-14 19:40:24 -080029import android.view.View;
Jorim Jaggidd98d412015-11-18 15:57:38 -080030
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010031import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Winsonc0d70582016-01-29 10:24:39 -080032import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
Jason Monkde850bb2017-02-01 19:26:30 -050033import com.android.systemui.Dependency;
Matthew Ng13dbf872017-10-27 11:02:14 -070034import com.android.systemui.OverviewProxyService;
Jorim Jaggidd98d412015-11-18 15:57:38 -080035import com.android.systemui.R;
36import com.android.systemui.RecentsComponent;
Matthew Ng78f88d12018-01-23 12:39:55 -080037import com.android.systemui.SysUiServiceProvider;
Jason Monk67e6c802016-08-30 14:09:21 -040038import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
Winson Chung38d31c22017-11-08 14:32:32 -080039import com.android.systemui.shared.recents.IOverviewProxy;
Matthew Nga8f24262017-12-19 11:54:24 -080040import com.android.systemui.shared.recents.utilities.Utilities;
Jorim Jaggidd98d412015-11-18 15:57:38 -080041import com.android.systemui.stackdivider.Divider;
Winsone759cd52015-12-03 16:55:06 -080042import com.android.systemui.tuner.TunerService;
Jorim Jaggidd98d412015-11-18 15:57:38 -080043
Winsonc0d70582016-01-29 10:24:39 -080044import static android.view.WindowManager.DOCKED_INVALID;
45import static android.view.WindowManager.DOCKED_LEFT;
46import static android.view.WindowManager.DOCKED_TOP;
Matthew Ngbd824572018-01-17 16:25:56 -080047import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
48import static com.android.systemui.OverviewProxyService.TAG_OPS;
Jorim Jaggidd98d412015-11-18 15:57:38 -080049
50/**
51 * Class to detect gestures on the navigation bar.
52 */
Matthew Ng00000d82018-01-09 10:33:17 -080053public class NavigationBarGestureHelper implements TunerService.Tunable, GestureHelper {
Jorim Jaggidd98d412015-11-18 15:57:38 -080054
Matthew Ng13dbf872017-10-27 11:02:14 -070055 private static final String TAG = "NavBarGestureHelper";
Winsone759cd52015-12-03 16:55:06 -080056 private static final String KEY_DOCK_WINDOW_GESTURE = "overview_nav_bar_gesture";
Jorim Jaggicdb06ca2016-01-25 19:15:12 -080057 /**
58 * When dragging from the navigation bar, we drag in recents.
59 */
60 public static final int DRAG_MODE_NONE = -1;
Jorim Jaggidd98d412015-11-18 15:57:38 -080061
62 /**
63 * When dragging from the navigation bar, we drag in recents.
64 */
Jorim Jaggicdb06ca2016-01-25 19:15:12 -080065 public static final int DRAG_MODE_RECENTS = 0;
Jorim Jaggidd98d412015-11-18 15:57:38 -080066
67 /**
68 * When dragging from the navigation bar, we drag the divider.
69 */
Jorim Jaggicdb06ca2016-01-25 19:15:12 -080070 public static final int DRAG_MODE_DIVIDER = 1;
Jorim Jaggidd98d412015-11-18 15:57:38 -080071
72 private RecentsComponent mRecentsComponent;
73 private Divider mDivider;
Jorim Jaggi213cbe62015-12-02 18:17:07 -080074 private Context mContext;
Jorim Jaggi298c0812016-01-14 19:40:24 -080075 private NavigationBarView mNavigationBarView;
Jorim Jaggidd98d412015-11-18 15:57:38 -080076 private boolean mIsVertical;
Jorim Jaggidd98d412015-11-18 15:57:38 -080077
Matthew Nga8f24262017-12-19 11:54:24 -080078 private final QuickScrubController mQuickScrubController;
Jorim Jaggidd98d412015-11-18 15:57:38 -080079 private final int mScrollTouchSlop;
Matthew Ng8c039fd2017-11-15 11:09:44 -080080 private final Matrix mTransformGlobalMatrix = new Matrix();
81 private final Matrix mTransformLocalMatrix = new Matrix();
Matthew Ng78f88d12018-01-23 12:39:55 -080082 private final StatusBar mStatusBar;
Jorim Jaggidd98d412015-11-18 15:57:38 -080083 private int mTouchDownX;
84 private int mTouchDownY;
Jorim Jaggi298c0812016-01-14 19:40:24 -080085 private boolean mDownOnRecents;
Jorim Jaggidd98d412015-11-18 15:57:38 -080086 private VelocityTracker mVelocityTracker;
Matthew Ng13dbf872017-10-27 11:02:14 -070087 private OverviewProxyService mOverviewEventSender = Dependency.get(OverviewProxyService.class);
Jorim Jaggidd98d412015-11-18 15:57:38 -080088
89 private boolean mDockWindowEnabled;
90 private boolean mDockWindowTouchSlopExceeded;
91 private int mDragMode;
92
93 public NavigationBarGestureHelper(Context context) {
Jorim Jaggi213cbe62015-12-02 18:17:07 -080094 mContext = context;
Matthew Ng78f88d12018-01-23 12:39:55 -080095 mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
Jorim Jaggidd98d412015-11-18 15:57:38 -080096 Resources r = context.getResources();
97 mScrollTouchSlop = r.getDimensionPixelSize(R.dimen.navigation_bar_min_swipe_distance);
Matthew Nga8f24262017-12-19 11:54:24 -080098 mQuickScrubController = new QuickScrubController(context);
Jason Monkde850bb2017-02-01 19:26:30 -050099 Dependency.get(TunerService.class).addTunable(this, KEY_DOCK_WINDOW_GESTURE);
100 }
101
102 public void destroy() {
103 Dependency.get(TunerService.class).removeTunable(this);
Jorim Jaggidd98d412015-11-18 15:57:38 -0800104 }
105
Jorim Jaggi298c0812016-01-14 19:40:24 -0800106 public void setComponents(RecentsComponent recentsComponent, Divider divider,
107 NavigationBarView navigationBarView) {
Jorim Jaggidd98d412015-11-18 15:57:38 -0800108 mRecentsComponent = recentsComponent;
109 mDivider = divider;
Jorim Jaggi298c0812016-01-14 19:40:24 -0800110 mNavigationBarView = navigationBarView;
Matthew Nga8f24262017-12-19 11:54:24 -0800111 mQuickScrubController.setComponents(mNavigationBarView);
Jorim Jaggidd98d412015-11-18 15:57:38 -0800112 }
113
114 public void setBarState(boolean isVertical, boolean isRTL) {
115 mIsVertical = isVertical;
Matthew Nga8f24262017-12-19 11:54:24 -0800116 mQuickScrubController.setBarState(isVertical, isRTL);
Jorim Jaggidd98d412015-11-18 15:57:38 -0800117 }
118
Matthew Ng7d05e772017-11-09 14:41:07 -0800119 private boolean proxyMotionEvents(MotionEvent event) {
Matthew Ng13dbf872017-10-27 11:02:14 -0700120 final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
121 if (overviewProxy != null) {
122 mNavigationBarView.requestUnbufferedDispatch(event);
Matthew Ng8c039fd2017-11-15 11:09:44 -0800123 event.transform(mTransformGlobalMatrix);
Matthew Ng13dbf872017-10-27 11:02:14 -0700124 try {
125 overviewProxy.onMotionEvent(event);
Matthew Ngbd824572018-01-17 16:25:56 -0800126 if (DEBUG_OVERVIEW_PROXY) {
127 Log.d(TAG_OPS, "Send MotionEvent: " + event.toString());
128 }
Matthew Ng7d05e772017-11-09 14:41:07 -0800129 return true;
Matthew Ng13dbf872017-10-27 11:02:14 -0700130 } catch (RemoteException e) {
131 Log.e(TAG, "Callback failed", e);
Matthew Ng8c039fd2017-11-15 11:09:44 -0800132 } finally {
133 event.transform(mTransformLocalMatrix);
Matthew Ng13dbf872017-10-27 11:02:14 -0700134 }
135 }
Matthew Ng7d05e772017-11-09 14:41:07 -0800136 return false;
137 }
Matthew Ng13dbf872017-10-27 11:02:14 -0700138
Matthew Ng7d05e772017-11-09 14:41:07 -0800139 public boolean onInterceptTouchEvent(MotionEvent event) {
Jorim Jaggidd98d412015-11-18 15:57:38 -0800140 int action = event.getAction();
141 switch (action & MotionEvent.ACTION_MASK) {
142 case MotionEvent.ACTION_DOWN: {
143 mTouchDownX = (int) event.getX();
144 mTouchDownY = (int) event.getY();
Matthew Ng8c039fd2017-11-15 11:09:44 -0800145 mTransformGlobalMatrix.set(Matrix.IDENTITY_MATRIX);
146 mTransformLocalMatrix.set(Matrix.IDENTITY_MATRIX);
147 mNavigationBarView.transformMatrixToGlobal(mTransformGlobalMatrix);
148 mNavigationBarView.transformMatrixToLocal(mTransformLocalMatrix);
Jorim Jaggidd98d412015-11-18 15:57:38 -0800149 break;
150 }
Jorim Jaggidd98d412015-11-18 15:57:38 -0800151 }
Matthew Ng78f88d12018-01-23 12:39:55 -0800152 if (mStatusBar.isPresenterFullyCollapsed()
153 && !mQuickScrubController.onInterceptTouchEvent(event)) {
Matthew Nga8f24262017-12-19 11:54:24 -0800154 proxyMotionEvents(event);
155 return false;
156 }
157 return (mDockWindowEnabled && interceptDockWindowEvent(event));
158 }
159
160 public void onDraw(Canvas canvas) {
161 if (mOverviewEventSender.getProxy() != null) {
162 mQuickScrubController.onDraw(canvas);
163 }
164 }
165
166 public void onLayout(boolean changed, int left, int top, int right, int bottom) {
167 mQuickScrubController.onLayout(changed, left, top, right, bottom);
168 }
169
170 public void onDarkIntensityChange(float intensity) {
171 mQuickScrubController.onDarkIntensityChange(intensity);
Jorim Jaggidd98d412015-11-18 15:57:38 -0800172 }
173
174 private boolean interceptDockWindowEvent(MotionEvent event) {
175 switch (event.getActionMasked()) {
176 case MotionEvent.ACTION_DOWN:
177 handleDragActionDownEvent(event);
178 break;
179 case MotionEvent.ACTION_MOVE:
180 return handleDragActionMoveEvent(event);
181 case MotionEvent.ACTION_UP:
182 case MotionEvent.ACTION_CANCEL:
183 handleDragActionUpEvent(event);
184 break;
185 }
186 return false;
187 }
188
189 private boolean handleDockWindowEvent(MotionEvent event) {
190 switch (event.getActionMasked()) {
191 case MotionEvent.ACTION_DOWN:
192 handleDragActionDownEvent(event);
193 break;
194 case MotionEvent.ACTION_MOVE:
195 handleDragActionMoveEvent(event);
196 break;
197 case MotionEvent.ACTION_UP:
198 case MotionEvent.ACTION_CANCEL:
199 handleDragActionUpEvent(event);
200 break;
201 }
202 return true;
203 }
204
205 private void handleDragActionDownEvent(MotionEvent event) {
206 mVelocityTracker = VelocityTracker.obtain();
207 mVelocityTracker.addMovement(event);
208 mDockWindowTouchSlopExceeded = false;
209 mTouchDownX = (int) event.getX();
210 mTouchDownY = (int) event.getY();
Anthony Chenada13042016-01-19 16:57:20 -0800211
212 if (mNavigationBarView != null) {
Jason Monka2081822016-01-18 14:41:03 -0500213 View recentsButton = mNavigationBarView.getRecentsButton().getCurrentView();
Anthony Chenada13042016-01-19 16:57:20 -0800214 if (recentsButton != null) {
215 mDownOnRecents = mTouchDownX >= recentsButton.getLeft()
216 && mTouchDownX <= recentsButton.getRight()
217 && mTouchDownY >= recentsButton.getTop()
218 && mTouchDownY <= recentsButton.getBottom();
Jason Monka2081822016-01-18 14:41:03 -0500219 } else {
220 mDownOnRecents = false;
Anthony Chenada13042016-01-19 16:57:20 -0800221 }
222 }
Jorim Jaggidd98d412015-11-18 15:57:38 -0800223 }
224
225 private boolean handleDragActionMoveEvent(MotionEvent event) {
226 mVelocityTracker.addMovement(event);
227 int x = (int) event.getX();
228 int y = (int) event.getY();
229 int xDiff = Math.abs(x - mTouchDownX);
230 int yDiff = Math.abs(y - mTouchDownY);
Victor Chan9b8018c2016-01-08 10:48:35 -0800231 if (mDivider == null || mRecentsComponent == null) {
232 return false;
233 }
Jorim Jaggidd98d412015-11-18 15:57:38 -0800234 if (!mDockWindowTouchSlopExceeded) {
235 boolean touchSlopExceeded = !mIsVertical
Jorim Jaggi47cf6382015-12-10 16:32:46 -0800236 ? yDiff > mScrollTouchSlop && yDiff > xDiff
237 : xDiff > mScrollTouchSlop && xDiff > yDiff;
Jorim Jaggi298c0812016-01-14 19:40:24 -0800238 if (mDownOnRecents && touchSlopExceeded
239 && mDivider.getView().getWindowManagerProxy().getDockSide() == DOCKED_INVALID) {
Jorim Jaggi9ea2f7b2015-11-23 18:08:28 -0800240 Rect initialBounds = null;
Winsone6309aa2016-01-08 11:19:21 -0800241 int dragMode = calculateDragMode();
Matthew Ngbf155872017-10-27 15:24:39 -0700242 int createMode = ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
Winsone6309aa2016-01-08 11:19:21 -0800243 if (dragMode == DRAG_MODE_DIVIDER) {
Jorim Jaggi9ea2f7b2015-11-23 18:08:28 -0800244 initialBounds = new Rect();
245 mDivider.getView().calculateBoundsForPosition(mIsVertical
246 ? (int) event.getRawX()
247 : (int) event.getRawY(),
248 mDivider.getView().isHorizontalDivision()
249 ? DOCKED_TOP
250 : DOCKED_LEFT,
251 initialBounds);
Winsone6309aa2016-01-08 11:19:21 -0800252 } else if (dragMode == DRAG_MODE_RECENTS && mTouchDownX
Jorim Jaggi213cbe62015-12-02 18:17:07 -0800253 < mContext.getResources().getDisplayMetrics().widthPixels / 2) {
Matthew Ngbf155872017-10-27 15:24:39 -0700254 createMode = ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
Jorim Jaggi9ea2f7b2015-11-23 18:08:28 -0800255 }
Matthew Ngbf155872017-10-27 15:24:39 -0700256 boolean docked = mRecentsComponent.splitPrimaryTask(dragMode, createMode,
257 initialBounds, MetricsEvent.ACTION_WINDOW_DOCK_SWIPE);
Winsone6309aa2016-01-08 11:19:21 -0800258 if (docked) {
259 mDragMode = dragMode;
260 if (mDragMode == DRAG_MODE_DIVIDER) {
Jorim Jaggi11cc01d2016-01-22 19:39:23 -0800261 mDivider.getView().startDragging(false /* animate */, true /* touching*/);
Winsone6309aa2016-01-08 11:19:21 -0800262 }
263 mDockWindowTouchSlopExceeded = true;
Winsone6309aa2016-01-08 11:19:21 -0800264 return true;
Jorim Jaggidd98d412015-11-18 15:57:38 -0800265 }
Jorim Jaggidd98d412015-11-18 15:57:38 -0800266 }
267 } else {
268 if (mDragMode == DRAG_MODE_DIVIDER) {
Jorim Jaggidc249c42015-12-15 14:57:31 -0800269 int position = !mIsVertical ? (int) event.getRawY() : (int) event.getRawX();
Jorim Jaggie435e982015-12-30 13:54:32 +0100270 SnapTarget snapTarget = mDivider.getView().getSnapAlgorithm()
Jorim Jaggidf012d52016-01-15 22:40:13 -0800271 .calculateSnapTarget(position, 0f /* velocity */, false /* hardDismiss */);
Jorim Jaggie435e982015-12-30 13:54:32 +0100272 mDivider.getView().resizeStack(position, snapTarget.position, snapTarget);
Jorim Jaggidd98d412015-11-18 15:57:38 -0800273 } else if (mDragMode == DRAG_MODE_RECENTS) {
274 mRecentsComponent.onDraggingInRecents(event.getRawY());
275 }
276 }
277 return false;
278 }
279
280 private void handleDragActionUpEvent(MotionEvent event) {
281 mVelocityTracker.addMovement(event);
282 mVelocityTracker.computeCurrentVelocity(1000);
Victor Chan9b8018c2016-01-08 10:48:35 -0800283 if (mDockWindowTouchSlopExceeded && mDivider != null && mRecentsComponent != null) {
Jorim Jaggidd98d412015-11-18 15:57:38 -0800284 if (mDragMode == DRAG_MODE_DIVIDER) {
Jorim Jaggi9ea2f7b2015-11-23 18:08:28 -0800285 mDivider.getView().stopDragging(mIsVertical
286 ? (int) event.getRawX()
287 : (int) event.getRawY(),
288 mIsVertical
289 ? mVelocityTracker.getXVelocity()
Jorim Jaggidf012d52016-01-15 22:40:13 -0800290 : mVelocityTracker.getYVelocity(),
Jorim Jaggi29379ec2016-04-11 23:43:42 -0700291 true /* avoidDismissStart */, false /* logMetrics */);
Jorim Jaggidd98d412015-11-18 15:57:38 -0800292 } else if (mDragMode == DRAG_MODE_RECENTS) {
293 mRecentsComponent.onDraggingInRecentsEnded(mVelocityTracker.getYVelocity());
294 }
295 }
296 mVelocityTracker.recycle();
297 mVelocityTracker = null;
298 }
299
300 private int calculateDragMode() {
301 if (mIsVertical && !mDivider.getView().isHorizontalDivision()) {
302 return DRAG_MODE_DIVIDER;
303 }
304 if (!mIsVertical && mDivider.getView().isHorizontalDivision()) {
305 return DRAG_MODE_DIVIDER;
306 }
307 return DRAG_MODE_RECENTS;
308 }
309
310 public boolean onTouchEvent(MotionEvent event) {
Matthew Ng78f88d12018-01-23 12:39:55 -0800311 boolean result = mStatusBar.isPresenterFullyCollapsed()
312 && (mQuickScrubController.onTouchEvent(event) || proxyMotionEvents(event));
Jorim Jaggidd98d412015-11-18 15:57:38 -0800313 if (mDockWindowEnabled) {
314 result |= handleDockWindowEvent(event);
315 }
316 return result;
317 }
318
319 @Override
Winsone759cd52015-12-03 16:55:06 -0800320 public void onTuningChanged(String key, String newValue) {
321 switch (key) {
322 case KEY_DOCK_WINDOW_GESTURE:
Jorim Jaggi8f2a44f2016-02-03 22:18:45 -0800323 mDockWindowEnabled = newValue != null && (Integer.parseInt(newValue) != 0);
Winsone759cd52015-12-03 16:55:06 -0800324 break;
325 }
326 }
Jorim Jaggidd98d412015-11-18 15:57:38 -0800327}