blob: 73ab5274a0ab10a37ac49a8ecd7d0e18134ecbdf [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;
Tiger Huang44c04552018-11-29 20:06:45 +080020import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
Winson Chung661d5f92018-05-21 18:41:39 -070021import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
Matthew Ng86a436e2018-10-26 16:00:53 -070022import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
23
Winson Chung2dbcf092018-10-24 13:00:41 -070024import static com.android.systemui.recents.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
25import static com.android.systemui.recents.OverviewProxyService.TAG_OPS;
Matthew Ng86a436e2018-10-26 16:00:53 -070026import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
Matthew Ng472d3e42018-06-14 15:16:55 -070027import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
Matthew Ngc83b9892018-08-21 16:31:13 -070028import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
Matthew Ng86a436e2018-10-26 16:00:53 -070029import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
30import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW;
Matthew Ngb831fb42019-01-30 11:20:48 -080031import static com.android.systemui.statusbar.phone.NavigationBarView.WINDOW_TARGET_BOTTOM;
32import static com.android.systemui.statusbar.phone.NavigationPrototypeController.EDGE_SENSITIVITY_WIDTH_SETTING;
Winson Chung661d5f92018-05-21 18:41:39 -070033
Matthew Ng86a436e2018-10-26 16:00:53 -070034import android.annotation.Nullable;
Matthew Nga8f24262017-12-19 11:54:24 -080035import android.content.Context;
Matthew Ngb831fb42019-01-30 11:20:48 -080036import android.content.res.Resources;
Matthew Nga8f24262017-12-19 11:54:24 -080037import android.graphics.Canvas;
Matthew Ng2ea93b72018-03-14 19:43:18 +000038import android.graphics.Matrix;
Matthew Ng1cf6eac2018-11-27 15:06:55 -080039import android.graphics.Rect;
Matthew Ng11891c92019-01-15 12:12:37 -080040import android.hardware.input.InputManager;
Matthew Nga8f24262017-12-19 11:54:24 -080041import android.os.RemoteException;
Matthew Ng11891c92019-01-15 12:12:37 -080042import android.os.SystemClock;
Matthew Ngb687c8a2018-10-02 11:31:38 -070043import android.provider.Settings;
Matthew Nga8f24262017-12-19 11:54:24 -080044import android.util.Log;
Matthew Ng11891c92019-01-15 12:12:37 -080045import android.view.InputDevice;
Matthew Nga8f24262017-12-19 11:54:24 -080046import android.view.MotionEvent;
47import android.view.View;
Matthew Ngb831fb42019-01-30 11:20:48 -080048import android.view.ViewConfiguration;
Matthew Ngc83b9892018-08-21 16:31:13 -070049import android.view.ViewPropertyAnimator;
Matthew Ng86a436e2018-10-26 16:00:53 -070050
Matthew Nga8f24262017-12-19 11:54:24 -080051import com.android.systemui.Dependency;
Matthew Ngc83b9892018-08-21 16:31:13 -070052import com.android.systemui.Interpolators;
Matthew Ng86a436e2018-10-26 16:00:53 -070053import com.android.systemui.R;
Matthew Ng86a436e2018-10-26 16:00:53 -070054import com.android.systemui.SysUiServiceProvider;
Gus Prevasab336792018-11-14 13:52:20 -050055import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
56import com.android.systemui.recents.OverviewProxyService;
Matthew Nga8f24262017-12-19 11:54:24 -080057import com.android.systemui.shared.recents.IOverviewProxy;
Matthew Ngf1f8a432018-11-08 18:03:29 -080058import com.android.systemui.shared.recents.utilities.Utilities;
Sunny Goyalaac6c882019-02-11 11:57:58 -080059import com.android.systemui.shared.system.InputChannelCompat.InputEventDispatcher;
Matthew Ngf29ad752018-04-26 11:24:05 -070060import com.android.systemui.shared.system.NavigationBarCompat;
Matthew Ng86a436e2018-10-26 16:00:53 -070061
Winson Chung0d3815d2018-09-25 18:11:54 -070062import java.io.PrintWriter;
Matthew Nga8f24262017-12-19 11:54:24 -080063
Matthew Nga8f24262017-12-19 11:54:24 -080064/**
Matthew Ngfba39692018-03-13 18:08:34 -070065 * Class to detect gestures on the navigation bar and implement quick scrub.
Matthew Ng86a436e2018-10-26 16:00:53 -070066 * Note that the variables in this class horizontal and vertical represents horizontal always
Matthew Ngf1f8a432018-11-08 18:03:29 -080067 * aligned with along the navigation bar.
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";
Matthew Nga8f24262017-12-19 11:54:24 -080072
Matthew Ngc83b9892018-08-21 16:31:13 -070073 /** Experiment to swipe home button left to execute a back key press */
Matthew Ngb687c8a2018-10-02 11:31:38 -070074 private static final String HIDE_BACK_BUTTON_PROP = "quickstepcontroller_hideback";
Matthew Ng11891c92019-01-15 12:12:37 -080075 private static final String ENABLE_CLICK_THROUGH_NAV_PROP = "quickstepcontroller_clickthrough";
Matthew Ngb831fb42019-01-30 11:20:48 -080076 private static final String GESTURE_REGION_THRESHOLD_SETTING = "gesture_region_threshold";
Matthew Ngc83b9892018-08-21 16:31:13 -070077 private static final long BACK_BUTTON_FADE_IN_ALPHA = 150;
Matthew Ng11891c92019-01-15 12:12:37 -080078 private static final long CLICK_THROUGH_TAP_DELAY = 70;
79 private static final long CLICK_THROUGH_TAP_RESET_DELAY = 100;
Matthew Ngc83b9892018-08-21 16:31:13 -070080
81 /** When the home-swipe-back gesture is disallowed, make it harder to pull */
Matthew Ngf1f8a432018-11-08 18:03:29 -080082 private static final float HORIZONTAL_GESTURE_DAMPING = 0.3f;
83 private static final float VERTICAL_GESTURE_DAMPING = 0.15f;
84 private static final float HORIZONTAL_DISABLED_GESTURE_DAMPING = 0.16f;
85 private static final float VERTICAL_DISABLED_GESTURE_DAMPING = 0.06f;
Matthew Ngc83b9892018-08-21 16:31:13 -070086
Matthew Ng86a436e2018-10-26 16:00:53 -070087 private static final int ACTION_SWIPE_UP_INDEX = 0;
88 private static final int ACTION_SWIPE_DOWN_INDEX = 1;
89 private static final int ACTION_SWIPE_LEFT_INDEX = 2;
90 private static final int ACTION_SWIPE_RIGHT_INDEX = 3;
Matthew Ngb9c84282018-12-06 17:15:27 -080091 private static final int ACTION_SWIPE_LEFT_FROM_EDGE_INDEX = 4;
92 private static final int ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX = 5;
93 private static final int MAX_GESTURES = 6;
Matthew Ng86a436e2018-10-26 16:00:53 -070094
Matthew Nga8f24262017-12-19 11:54:24 -080095 private NavigationBarView mNavigationBarView;
Matthew Nga8f24262017-12-19 11:54:24 -080096
Matthew Ng2ea93b72018-03-14 19:43:18 +000097 private boolean mAllowGestureDetection;
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;
Matthew Ng86a436e2018-10-26 16:00:53 -0700101 private boolean mDragHPositive;
102 private boolean mDragVPositive;
Matthew Nga8f24262017-12-19 11:54:24 -0800103 private boolean mIsRTL;
Matthew Ng86a436e2018-10-26 16:00:53 -0700104 private int mNavBarPosition;
Matthew Ng7090a802018-01-19 13:36:22 -0800105 private float mDarkIntensity;
Matthew Ng86a436e2018-10-26 16:00:53 -0700106 private ViewPropertyAnimator mDragBtnAnimator;
Winson Chung661d5f92018-05-21 18:41:39 -0700107 private ButtonDispatcher mHitTarget;
Matthew Ng20136e62018-05-30 12:20:31 -0700108 private boolean mIsInScreenPinning;
Matthew Ng86a436e2018-10-26 16:00:53 -0700109 private boolean mGestureHorizontalDragsButton;
110 private boolean mGestureVerticalDragsButton;
Matthew Ngf1f8a432018-11-08 18:03:29 -0800111 private float mMaxDragLimit;
112 private float mMinDragLimit;
113 private float mDragDampeningFactor;
Matthew Ng11891c92019-01-15 12:12:37 -0800114 private boolean mClickThroughPressed;
115 private float mClickThroughPressX;
116 private float mClickThroughPressY;
Matthew Ngb831fb42019-01-30 11:20:48 -0800117 private int mGestureRegionThreshold;
Matthew Nga8f24262017-12-19 11:54:24 -0800118
Matthew Ng86a436e2018-10-26 16:00:53 -0700119 private NavigationGestureAction mCurrentAction;
120 private NavigationGestureAction[] mGestureActions = new NavigationGestureAction[MAX_GESTURES];
121
Matthew Ng1cf6eac2018-11-27 15:06:55 -0800122 private final Rect mLastLayoutRect = new Rect();
Matthew Nga8f24262017-12-19 11:54:24 -0800123 private final OverviewProxyService mOverviewEventSender;
Matthew Nga8f24262017-12-19 11:54:24 -0800124 private final Context mContext;
Winson Chung0d3815d2018-09-25 18:11:54 -0700125 private final StatusBar mStatusBar;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000126 private final Matrix mTransformGlobalMatrix = new Matrix();
127 private final Matrix mTransformLocalMatrix = new Matrix();
Matthew Ngc83b9892018-08-21 16:31:13 -0700128
Matthew Ng2ea93b72018-03-14 19:43:18 +0000129 public QuickStepController(Context context) {
Matthew Nga8f24262017-12-19 11:54:24 -0800130 mContext = context;
Winson Chung0d3815d2018-09-25 18:11:54 -0700131 mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
Matthew Nga8f24262017-12-19 11:54:24 -0800132 mOverviewEventSender = Dependency.get(OverviewProxyService.class);
Matthew Nga8f24262017-12-19 11:54:24 -0800133 }
134
Matthew Ng11891c92019-01-15 12:12:37 -0800135 private final Runnable mClickThroughSendTap = new Runnable() {
136 @Override
137 public void run() {
138 sendTap(mClickThroughPressX, mClickThroughPressY);
139 mNavigationBarView.postDelayed(mClickThroughResetTap, CLICK_THROUGH_TAP_RESET_DELAY);
140 }
141 };
142
143 private final Runnable mClickThroughResetTap = () -> {
Matthew Ngb831fb42019-01-30 11:20:48 -0800144 mNavigationBarView.setWindowTouchable(true);
Matthew Ng11891c92019-01-15 12:12:37 -0800145 mClickThroughPressed = false;
146 };
147
Matthew Nga8f24262017-12-19 11:54:24 -0800148 public void setComponents(NavigationBarView navigationBarView) {
149 mNavigationBarView = navigationBarView;
Matthew Ngc83b9892018-08-21 16:31:13 -0700150
Matthew Ngb687c8a2018-10-02 11:31:38 -0700151 mNavigationBarView.getBackButton().setVisibility(shouldhideBackButton(mContext)
Matthew Ngc83b9892018-08-21 16:31:13 -0700152 ? View.GONE
153 : View.VISIBLE);
Matthew Nga8f24262017-12-19 11:54:24 -0800154 }
155
Winson Chung4faf38a2018-02-06 08:53:37 -0800156 /**
Matthew Ng86a436e2018-10-26 16:00:53 -0700157 * Set each gesture an action. After set the gestures triggered will run the actions attached.
158 * @param swipeUpAction action after swiping up
159 * @param swipeDownAction action after swiping down
160 * @param swipeLeftAction action after swiping left
161 * @param swipeRightAction action after swiping right
Matthew Ngb9c84282018-12-06 17:15:27 -0800162 * @param swipeLeftFromEdgeAction action swiping left starting from the right side
163 * @param swipeRightFromEdgeAction action swiping right starting from the left side
Matthew Ng86a436e2018-10-26 16:00:53 -0700164 */
165 public void setGestureActions(@Nullable NavigationGestureAction swipeUpAction,
166 @Nullable NavigationGestureAction swipeDownAction,
167 @Nullable NavigationGestureAction swipeLeftAction,
Matthew Ngb9c84282018-12-06 17:15:27 -0800168 @Nullable NavigationGestureAction swipeRightAction,
169 @Nullable NavigationGestureAction swipeLeftFromEdgeAction,
170 @Nullable NavigationGestureAction swipeRightFromEdgeAction) {
Matthew Ng86a436e2018-10-26 16:00:53 -0700171 mGestureActions[ACTION_SWIPE_UP_INDEX] = swipeUpAction;
172 mGestureActions[ACTION_SWIPE_DOWN_INDEX] = swipeDownAction;
173 mGestureActions[ACTION_SWIPE_LEFT_INDEX] = swipeLeftAction;
174 mGestureActions[ACTION_SWIPE_RIGHT_INDEX] = swipeRightAction;
Matthew Ngb9c84282018-12-06 17:15:27 -0800175 mGestureActions[ACTION_SWIPE_LEFT_FROM_EDGE_INDEX] = swipeLeftFromEdgeAction;
176 mGestureActions[ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX] = swipeRightFromEdgeAction;
Matthew Ng86a436e2018-10-26 16:00:53 -0700177
178 // Set the current state to all actions
179 for (NavigationGestureAction action: mGestureActions) {
180 if (action != null) {
181 action.setBarState(true, mNavBarPosition, mDragHPositive, mDragVPositive);
182 action.onDarkIntensityChange(mDarkIntensity);
Matthew Ng1cf6eac2018-11-27 15:06:55 -0800183 action.onLayout(true /* changed */, mLastLayoutRect.left, mLastLayoutRect.top,
184 mLastLayoutRect.right, mLastLayoutRect.bottom);
Matthew Ng86a436e2018-10-26 16:00:53 -0700185 }
186 }
187 }
188
189 /**
Matthew Ngfba39692018-03-13 18:08:34 -0700190 * @return true if we want to intercept touch events for quick scrub and prevent proxying the
191 * event to the overview service.
Winson Chung4faf38a2018-02-06 08:53:37 -0800192 */
Matthew Nga8f24262017-12-19 11:54:24 -0800193 @Override
194 public boolean onInterceptTouchEvent(MotionEvent event) {
Winson Chung0d3815d2018-09-25 18:11:54 -0700195 if (mStatusBar.isKeyguardShowing()) {
196 // Disallow any handling when the keyguard is showing
197 return false;
198 }
Winson Chungde2a1242018-02-07 15:59:43 -0800199 return handleTouchEvent(event);
200 }
201
202 /**
Matthew Ngfba39692018-03-13 18:08:34 -0700203 * @return true if we want to handle touch events for quick scrub or if down event (that will
204 * get consumed and ignored). No events will be proxied to the overview service.
Winson Chungde2a1242018-02-07 15:59:43 -0800205 */
206 @Override
207 public boolean onTouchEvent(MotionEvent event) {
Winson Chung0d3815d2018-09-25 18:11:54 -0700208 if (mStatusBar.isKeyguardShowing()) {
209 // Disallow any handling when the keyguard is showing
210 return false;
211 }
212
Matthew Ng2ea93b72018-03-14 19:43:18 +0000213 // The same down event was just sent on intercept and therefore can be ignored here
214 final boolean ignoreProxyDownEvent = event.getAction() == MotionEvent.ACTION_DOWN
Matthew Ngb831fb42019-01-30 11:20:48 -0800215 && mOverviewEventSender.getProxy() != null
216 && mNavigationBarView.getWindowTarget() == WINDOW_TARGET_BOTTOM;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000217 return ignoreProxyDownEvent || handleTouchEvent(event);
Winson Chungde2a1242018-02-07 15:59:43 -0800218 }
219
220 private boolean handleTouchEvent(MotionEvent event) {
Matthew Ng472d3e42018-06-14 15:16:55 -0700221 final boolean deadZoneConsumed =
222 mNavigationBarView.getDownHitTarget() == HIT_TARGET_DEAD_ZONE;
Matthew Ng86a436e2018-10-26 16:00:53 -0700223
224 // Requires proxy and an active gesture or able to perform any gesture to continue
225 if (mOverviewEventSender.getProxy() == null
Matthew Ngd04676d2018-12-04 14:54:48 -0800226 || !mOverviewEventSender.shouldShowSwipeUpUI()
Matthew Ng86a436e2018-10-26 16:00:53 -0700227 || (mCurrentAction == null && !canPerformAnyAction())) {
Matthew Ng2cbe8cd2018-08-20 11:27:40 -0700228 return deadZoneConsumed;
Matthew Ng22cf5142018-03-14 12:26:14 -0700229 }
230 mNavigationBarView.requestUnbufferedDispatch(event);
231
Matthew Ng9a223632018-03-30 16:47:22 -0700232 int action = event.getActionMasked();
233 switch (action) {
Matthew Nga8f24262017-12-19 11:54:24 -0800234 case MotionEvent.ACTION_DOWN: {
235 int x = (int) event.getX();
236 int y = (int) event.getY();
Matthew Ng20136e62018-05-30 12:20:31 -0700237 mIsInScreenPinning = mNavigationBarView.inScreenPinning();
Winson Chung661d5f92018-05-21 18:41:39 -0700238
Matthew Ng86a436e2018-10-26 16:00:53 -0700239 for (NavigationGestureAction gestureAction: mGestureActions) {
240 if (gestureAction != null) {
241 gestureAction.reset();
242 }
Winson Chung0be8f082018-02-15 15:52:49 -0800243 }
Winson Chung661d5f92018-05-21 18:41:39 -0700244
Matthew Ng86a436e2018-10-26 16:00:53 -0700245 // Valid buttons to drag over
246 switch (mNavigationBarView.getDownHitTarget()) {
247 case HIT_TARGET_BACK:
248 mHitTarget = mNavigationBarView.getBackButton();
249 break;
250 case HIT_TARGET_HOME:
251 mHitTarget = mNavigationBarView.getHomeButton();
252 break;
253 case HIT_TARGET_OVERVIEW:
254 mHitTarget = mNavigationBarView.getRecentsButton();
255 break;
256 default:
257 mHitTarget = null;
258 break;
259 }
Winson Chung661d5f92018-05-21 18:41:39 -0700260 if (mHitTarget != null) {
261 // Pre-emptively delay the touch feedback for the button that we just touched
262 mHitTarget.setDelayTouchFeedback(true);
263 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000264 mTouchDownX = x;
265 mTouchDownY = y;
Matthew Ng86a436e2018-10-26 16:00:53 -0700266 mGestureHorizontalDragsButton = false;
267 mGestureVerticalDragsButton = false;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000268 mTransformGlobalMatrix.set(Matrix.IDENTITY_MATRIX);
269 mTransformLocalMatrix.set(Matrix.IDENTITY_MATRIX);
270 mNavigationBarView.transformMatrixToGlobal(mTransformGlobalMatrix);
271 mNavigationBarView.transformMatrixToLocal(mTransformLocalMatrix);
Matthew Ng2ea93b72018-03-14 19:43:18 +0000272 mAllowGestureDetection = true;
Matthew Ng90ef0632018-08-15 13:53:15 -0700273 mNotificationsVisibleOnDown = !mNavigationBarView.isNotificationsFullyCollapsed();
Matthew Ngb831fb42019-01-30 11:20:48 -0800274 final int defaultRegionThreshold = mContext.getResources()
275 .getDimensionPixelOffset(R.dimen.navigation_bar_default_edge_width);
276 mGestureRegionThreshold = convertDpToPixel(getIntGlobalSetting(mContext,
277 EDGE_SENSITIVITY_WIDTH_SETTING, defaultRegionThreshold));
Matthew Nga8f24262017-12-19 11:54:24 -0800278 break;
279 }
280 case MotionEvent.ACTION_MOVE: {
Matthew Ngb831fb42019-01-30 11:20:48 -0800281 if (!mAllowGestureDetection
282 || mNavigationBarView.getWindowTarget() != WINDOW_TARGET_BOTTOM) {
Matthew Ng2ea93b72018-03-14 19:43:18 +0000283 break;
284 }
285 int x = (int) event.getX();
286 int y = (int) event.getY();
287 int xDiff = Math.abs(x - mTouchDownX);
288 int yDiff = Math.abs(y - mTouchDownY);
Matthew Ng9a223632018-03-30 16:47:22 -0700289
Matthew Ngf8869462019-01-18 13:31:47 -0800290 boolean exceededSwipeHorizontalTouchSlop, exceededSwipeVerticalTouchSlop,
291 exceededSwipeVerticalDragSlop;
Matthew Ng86a436e2018-10-26 16:00:53 -0700292 int posH, touchDownH, posV, touchDownV;
Matthew Nge0903c92018-01-17 15:32:41 -0800293
Matthew Ng86a436e2018-10-26 16:00:53 -0700294 if (isNavBarVertical()) {
295 exceededSwipeHorizontalTouchSlop =
Matthew Ngf29ad752018-04-26 11:24:05 -0700296 yDiff > NavigationBarCompat.getQuickScrubTouchSlopPx() && yDiff > xDiff;
Matthew Ng86a436e2018-10-26 16:00:53 -0700297 exceededSwipeVerticalTouchSlop =
Matthew Ngf29ad752018-04-26 11:24:05 -0700298 xDiff > NavigationBarCompat.getQuickStepTouchSlopPx() && xDiff > yDiff;
Matthew Ngf8869462019-01-18 13:31:47 -0800299 exceededSwipeVerticalDragSlop =
300 xDiff > NavigationBarCompat.getQuickStepDragSlopPx() && xDiff > yDiff;
Matthew Ng86a436e2018-10-26 16:00:53 -0700301 posH = y;
302 touchDownH = mTouchDownY;
303 posV = x;
304 touchDownV = mTouchDownX;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000305 } else {
Matthew Ng86a436e2018-10-26 16:00:53 -0700306 exceededSwipeHorizontalTouchSlop =
Matthew Ngf29ad752018-04-26 11:24:05 -0700307 xDiff > NavigationBarCompat.getQuickScrubTouchSlopPx() && xDiff > yDiff;
Matthew Ng86a436e2018-10-26 16:00:53 -0700308 exceededSwipeVerticalTouchSlop =
Matthew Ngf29ad752018-04-26 11:24:05 -0700309 yDiff > NavigationBarCompat.getQuickStepTouchSlopPx() && yDiff > xDiff;
Matthew Ngf8869462019-01-18 13:31:47 -0800310 exceededSwipeVerticalDragSlop =
311 yDiff > NavigationBarCompat.getQuickStepDragSlopPx() && yDiff > xDiff;
Matthew Ng86a436e2018-10-26 16:00:53 -0700312 posH = x;
313 touchDownH = mTouchDownX;
314 posV = y;
315 touchDownV = mTouchDownY;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000316 }
Winson Chung0e490d922018-03-14 16:08:43 +0000317
Matthew Ng86a436e2018-10-26 16:00:53 -0700318 if (mCurrentAction != null) {
319 // Gesture started, provide positions to the current action
320 mCurrentAction.onGestureMove(x, y);
321 } else {
322 // Detect gesture and try to execute an action, only one can run at a time
Matthew Ngf8869462019-01-18 13:31:47 -0800323 if (exceededSwipeVerticalTouchSlop || exceededSwipeVerticalDragSlop) {
Matthew Ng86a436e2018-10-26 16:00:53 -0700324 if (mDragVPositive ? (posV < touchDownV) : (posV > touchDownV)) {
Matthew Ngf8869462019-01-18 13:31:47 -0800325 // Swipe up gesture must use the larger slop
326 if (exceededSwipeVerticalTouchSlop) {
327 // Swiping up gesture
328 tryToStartGesture(mGestureActions[ACTION_SWIPE_UP_INDEX],
329 false /* alignedWithNavBar */, event);
330 }
Matthew Ng86a436e2018-10-26 16:00:53 -0700331 } else {
332 // Swiping down gesture
333 tryToStartGesture(mGestureActions[ACTION_SWIPE_DOWN_INDEX],
Matthew Nga62b62082018-12-03 15:58:52 -0800334 false /* alignedWithNavBar */, event);
Winson Chung0e490d922018-03-14 16:08:43 +0000335 }
Matthew Ng86a436e2018-10-26 16:00:53 -0700336 } else if (exceededSwipeHorizontalTouchSlop) {
337 if (mDragHPositive ? (posH < touchDownH) : (posH > touchDownH)) {
Matthew Ngb9c84282018-12-06 17:15:27 -0800338 // Swiping left (rtl) gesture
Matthew Ngb831fb42019-01-30 11:20:48 -0800339 tryToStartGesture(mGestureActions[ACTION_SWIPE_LEFT_INDEX],
340 true /* alignedWithNavBar */, event);
Matthew Ng86a436e2018-10-26 16:00:53 -0700341 } else {
342 // Swiping right (ltr) gesture
Matthew Ngb831fb42019-01-30 11:20:48 -0800343 tryToStartGesture(mGestureActions[ACTION_SWIPE_RIGHT_INDEX],
344 true /* alignedWithNavBar */, event);
Matthew Ngc83b9892018-08-21 16:31:13 -0700345 }
346 }
Matthew Nga8f24262017-12-19 11:54:24 -0800347 }
Matthew Ng86a436e2018-10-26 16:00:53 -0700348
349 handleDragHitTarget(mGestureHorizontalDragsButton ? posH : posV,
350 mGestureHorizontalDragsButton ? touchDownH : touchDownV);
Matthew Nga8f24262017-12-19 11:54:24 -0800351 break;
352 }
353 case MotionEvent.ACTION_CANCEL:
354 case MotionEvent.ACTION_UP:
Matthew Ng86a436e2018-10-26 16:00:53 -0700355 if (mCurrentAction != null) {
356 mCurrentAction.endGesture();
Matthew Ngb831fb42019-01-30 11:20:48 -0800357 } else if (action == MotionEvent.ACTION_UP) {
358 if (canTriggerEdgeSwipe(event)) {
359 int index = mNavigationBarView.getWindowTarget() == NAV_BAR_LEFT
360 ? ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX
361 : ACTION_SWIPE_LEFT_FROM_EDGE_INDEX;
362 tryToStartGesture(mGestureActions[index], false /* alignedWithNavBar */,
363 event);
364 if (mCurrentAction != null) {
365 mCurrentAction.endGesture();
366 }
367 } else if (getBoolGlobalSetting(mContext, ENABLE_CLICK_THROUGH_NAV_PROP)
368 && !mClickThroughPressed) {
369 // Enable click through functionality where no gesture has been detected and
370 // not passed the drag slop so inject a touch event at the same location
371 // after making the navigation bar window untouchable. After a some time,
372 // the navigation bar will be able to take input events again
373 float diffX = Math.abs(event.getX() - mTouchDownX);
374 float diffY = Math.abs(event.getY() - mTouchDownY);
Matthew Ng11891c92019-01-15 12:12:37 -0800375
Matthew Ngb831fb42019-01-30 11:20:48 -0800376 if ((diffX <= NavigationBarCompat.getQuickStepDragSlopPx()
377 && diffY <= NavigationBarCompat.getQuickStepDragSlopPx())) {
378 mNavigationBarView.setWindowTouchable(false);
379 mClickThroughPressX = event.getRawX();
380 mClickThroughPressY = event.getRawY();
381 mClickThroughPressed = true;
382 mNavigationBarView.postDelayed(mClickThroughSendTap,
383 CLICK_THROUGH_TAP_DELAY);
384 }
Matthew Ng11891c92019-01-15 12:12:37 -0800385 }
Matthew Ng86a436e2018-10-26 16:00:53 -0700386 }
387
388 // Return the hit target back to its original position
389 if (mHitTarget != null) {
390 final View button = mHitTarget.getCurrentView();
391 if (mGestureHorizontalDragsButton || mGestureVerticalDragsButton) {
392 mDragBtnAnimator = button.animate().setDuration(BACK_BUTTON_FADE_IN_ALPHA)
393 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
394 if (mGestureVerticalDragsButton ^ isNavBarVertical()) {
395 mDragBtnAnimator.translationY(0);
396 } else {
397 mDragBtnAnimator.translationX(0);
398 }
399 mDragBtnAnimator.start();
400 }
401 }
Matthew Nga8f24262017-12-19 11:54:24 -0800402 break;
403 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000404
Matthew Ng90ef0632018-08-15 13:53:15 -0700405 if (shouldProxyEvents(action)) {
Matthew Ng2ea93b72018-03-14 19:43:18 +0000406 proxyMotionEvents(event);
407 }
Matthew Nga62b62082018-12-03 15:58:52 -0800408
409 // Clear action when gesture and event proxy finishes
410 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
411 mCurrentAction = null;
412 }
Matthew Ng86a436e2018-10-26 16:00:53 -0700413 return mCurrentAction != null || deadZoneConsumed;
414 }
415
416 private void handleDragHitTarget(int position, int touchDown) {
417 // Drag the hit target if gesture action requires it
418 if (mHitTarget != null && (mGestureVerticalDragsButton || mGestureHorizontalDragsButton)) {
419 final View button = mHitTarget.getCurrentView();
420 if (mDragBtnAnimator != null) {
421 mDragBtnAnimator.cancel();
422 mDragBtnAnimator = null;
423 }
424
Matthew Ngf1f8a432018-11-08 18:03:29 -0800425 // Clamp drag to the bounding box of the navigation bar
426 float diff = (position - touchDown) * mDragDampeningFactor;
427 diff = Utilities.clamp(diff, mMinDragLimit, mMaxDragLimit);
Matthew Ng86a436e2018-10-26 16:00:53 -0700428 if (mGestureVerticalDragsButton ^ isNavBarVertical()) {
429 button.setTranslationY(diff);
430 } else {
431 button.setTranslationX(diff);
432 }
433 }
Matthew Nga8f24262017-12-19 11:54:24 -0800434 }
435
Matthew Ng90ef0632018-08-15 13:53:15 -0700436 private boolean shouldProxyEvents(int action) {
Matthew Ngb831fb42019-01-30 11:20:48 -0800437 // Do not send events for side navigation bar panels
438 if (mNavigationBarView.getWindowTarget() != WINDOW_TARGET_BOTTOM) {
439 return false;
440 }
Matthew Ng86a436e2018-10-26 16:00:53 -0700441 final boolean actionValid = (mCurrentAction == null
Matthew Nga62b62082018-12-03 15:58:52 -0800442 || !mCurrentAction.disableProxyEvents());
Matthew Ng86a436e2018-10-26 16:00:53 -0700443 if (actionValid && !mIsInScreenPinning) {
Matthew Ng90ef0632018-08-15 13:53:15 -0700444 // Allow down, cancel and up events, move and other events are passed if notifications
445 // are not showing and disabled gestures (such as long press) are not executed
446 switch (action) {
447 case MotionEvent.ACTION_DOWN:
448 case MotionEvent.ACTION_CANCEL:
449 case MotionEvent.ACTION_UP:
450 return true;
451 default:
452 return !mNotificationsVisibleOnDown && mAllowGestureDetection;
453 }
454 }
455 return false;
456 }
457
Matthew Nga8f24262017-12-19 11:54:24 -0800458 @Override
459 public void onDraw(Canvas canvas) {
Matthew Ng86a436e2018-10-26 16:00:53 -0700460 if (mCurrentAction != null) {
461 mCurrentAction.onDraw(canvas);
Matthew Ng2ea93b72018-03-14 19:43:18 +0000462 }
Matthew Nga8f24262017-12-19 11:54:24 -0800463 }
464
465 @Override
466 public void onLayout(boolean changed, int left, int top, int right, int bottom) {
Matthew Ng86a436e2018-10-26 16:00:53 -0700467 for (NavigationGestureAction action: mGestureActions) {
468 if (action != null) {
469 action.onLayout(changed, left, top, right, bottom);
470 }
Matthew Nga8f24262017-12-19 11:54:24 -0800471 }
Matthew Ng1cf6eac2018-11-27 15:06:55 -0800472 mLastLayoutRect.set(left, top, right, bottom);
Matthew Nga8f24262017-12-19 11:54:24 -0800473 }
474
475 @Override
476 public void onDarkIntensityChange(float intensity) {
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700477 final float oldIntensity = mDarkIntensity;
Matthew Ng7090a802018-01-19 13:36:22 -0800478 mDarkIntensity = intensity;
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700479
480 // When in quick scrub, invalidate gradient if changing intensity from black to white and
481 // vice-versa
Matthew Ng86a436e2018-10-26 16:00:53 -0700482 if (mCurrentAction != null && mNavigationBarView.isQuickScrubEnabled()
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700483 && Math.round(intensity) != Math.round(oldIntensity)) {
Matthew Ng86a436e2018-10-26 16:00:53 -0700484 mCurrentAction.onDarkIntensityChange(mDarkIntensity);
Matthew Ng4fa3a9d2018-06-12 16:42:11 -0700485 }
Matthew Ng7090a802018-01-19 13:36:22 -0800486 mNavigationBarView.invalidate();
Matthew Nga8f24262017-12-19 11:54:24 -0800487 }
488
489 @Override
Matthew Ng86a436e2018-10-26 16:00:53 -0700490 public void setBarState(boolean isRTL, int navBarPosition) {
491 final boolean changed = (mIsRTL != isRTL) || (mNavBarPosition != navBarPosition);
Matthew Nga8f24262017-12-19 11:54:24 -0800492 mIsRTL = isRTL;
Matthew Ng86a436e2018-10-26 16:00:53 -0700493 mNavBarPosition = navBarPosition;
494
495 // Determine the drag directions depending on location of nav bar
496 switch (navBarPosition) {
497 case NAV_BAR_LEFT:
498 mDragHPositive = !isRTL;
499 mDragVPositive = false;
500 break;
501 case NAV_BAR_RIGHT:
502 mDragHPositive = isRTL;
503 mDragVPositive = true;
504 break;
505 case NAV_BAR_BOTTOM:
506 mDragHPositive = !isRTL;
507 mDragVPositive = true;
508 break;
Tiger Huang44c04552018-11-29 20:06:45 +0800509 case NAV_BAR_INVALID:
510 Log.e(TAG, "Invalid nav bar position");
511 break;
Matthew Ng86a436e2018-10-26 16:00:53 -0700512 }
513
514 for (NavigationGestureAction action: mGestureActions) {
515 if (action != null) {
516 action.setBarState(changed, mNavBarPosition, mDragHPositive, mDragVPositive);
Matthew Nga8f24262017-12-19 11:54:24 -0800517 }
Matthew Nga8f24262017-12-19 11:54:24 -0800518 }
519 }
520
Matthew Ng2ea93b72018-03-14 19:43:18 +0000521 @Override
522 public void onNavigationButtonLongPress(View v) {
523 mAllowGestureDetection = false;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000524 }
525
Winson Chung0d3815d2018-09-25 18:11:54 -0700526 @Override
527 public void dump(PrintWriter pw) {
528 pw.println("QuickStepController {");
Winson Chung0d3815d2018-09-25 18:11:54 -0700529 pw.print(" "); pw.println("mAllowGestureDetection=" + mAllowGestureDetection);
Winson Chung0d3815d2018-09-25 18:11:54 -0700530 pw.print(" "); pw.println("mNotificationsVisibleOnDown=" + mNotificationsVisibleOnDown);
Matthew Ng86a436e2018-10-26 16:00:53 -0700531 pw.print(" "); pw.println("mNavBarPosition=" + mNavBarPosition);
Winson Chung0d3815d2018-09-25 18:11:54 -0700532 pw.print(" "); pw.println("mIsRTL=" + mIsRTL);
533 pw.print(" "); pw.println("mIsInScreenPinning=" + mIsInScreenPinning);
534 pw.println("}");
535 }
536
Matthew Ng86a436e2018-10-26 16:00:53 -0700537 public NavigationGestureAction getCurrentAction() {
538 return mCurrentAction;
Matthew Ng2ea93b72018-03-14 19:43:18 +0000539 }
540
Matthew Ng86a436e2018-10-26 16:00:53 -0700541 private void tryToStartGesture(NavigationGestureAction action, boolean alignedWithNavBar,
Matthew Nga62b62082018-12-03 15:58:52 -0800542 MotionEvent event) {
Matthew Ng86a436e2018-10-26 16:00:53 -0700543 if (action == null) {
544 return;
545 }
Matthew Ng20136e62018-05-30 12:20:31 -0700546 if (mIsInScreenPinning) {
547 mNavigationBarView.showPinningEscapeToast();
548 mAllowGestureDetection = false;
549 return;
550 }
551
Matthew Ng86a436e2018-10-26 16:00:53 -0700552 // Start new action from gesture if is able to start and depending on notifications
553 // visibility and starting touch down target. If the action is enabled, then also check if
554 // can perform the action so that if action requires the button to be dragged, then the
555 // gesture will have a large dampening factor and prevent action from running.
556 final boolean validHitTarget = action.requiresTouchDownHitTarget() == HIT_TARGET_NONE
557 || action.requiresTouchDownHitTarget() == mNavigationBarView.getDownHitTarget();
558 if (mCurrentAction == null && validHitTarget && action.isEnabled()
559 && (!mNotificationsVisibleOnDown || action.canRunWhenNotificationsShowing())) {
560 if (action.canPerformAction()) {
561 mCurrentAction = action;
562 event.transform(mTransformGlobalMatrix);
563 action.startGesture(event);
564 event.transform(mTransformLocalMatrix);
Matthew Ngf1f8a432018-11-08 18:03:29 -0800565
566 // Calculate the bounding limits of drag to avoid dragging off nav bar's window
Matthew Ngb9c84282018-12-06 17:15:27 -0800567 if (action.allowHitTargetToMoveOverDrag() && mHitTarget != null) {
Matthew Ngf1f8a432018-11-08 18:03:29 -0800568 final int[] buttonCenter = new int[2];
569 View button = mHitTarget.getCurrentView();
570 button.getLocationInWindow(buttonCenter);
571 buttonCenter[0] += button.getWidth() / 2;
572 buttonCenter[1] += button.getHeight() / 2;
573 final int x = isNavBarVertical() ? buttonCenter[1] : buttonCenter[0];
574 final int y = isNavBarVertical() ? buttonCenter[0] : buttonCenter[1];
575 final int iconHalfSize = mContext.getResources()
576 .getDimensionPixelSize(R.dimen.navigation_icon_size) / 2;
577
578 if (alignedWithNavBar) {
579 mMinDragLimit = iconHalfSize - x;
580 mMaxDragLimit = -x - iconHalfSize + (isNavBarVertical()
581 ? mNavigationBarView.getHeight() : mNavigationBarView.getWidth());
582 } else {
583 mMinDragLimit = iconHalfSize - y;
584 mMaxDragLimit = -y - iconHalfSize + (isNavBarVertical()
585 ? mNavigationBarView.getWidth() : mNavigationBarView.getHeight());
586 }
587 }
Matthew Nga8f24262017-12-19 11:54:24 -0800588 }
Matthew Ng86a436e2018-10-26 16:00:53 -0700589
590 // Handle direction of the hit target drag from the axis that started the gesture
Matthew Ngf1f8a432018-11-08 18:03:29 -0800591 // Also calculate the dampening factor, weaker dampening if there is an active action
Matthew Ngb9c84282018-12-06 17:15:27 -0800592 if (action.allowHitTargetToMoveOverDrag()) {
Matthew Ng86a436e2018-10-26 16:00:53 -0700593 if (alignedWithNavBar) {
594 mGestureHorizontalDragsButton = true;
595 mGestureVerticalDragsButton = false;
Matthew Ngf1f8a432018-11-08 18:03:29 -0800596 mDragDampeningFactor = action.isActive()
597 ? HORIZONTAL_GESTURE_DAMPING : HORIZONTAL_DISABLED_GESTURE_DAMPING;
Matthew Ng86a436e2018-10-26 16:00:53 -0700598 } else {
599 mGestureVerticalDragsButton = true;
600 mGestureHorizontalDragsButton = false;
Matthew Ngf1f8a432018-11-08 18:03:29 -0800601 mDragDampeningFactor = action.isActive()
602 ? VERTICAL_GESTURE_DAMPING : VERTICAL_DISABLED_GESTURE_DAMPING;
Matthew Ng86a436e2018-10-26 16:00:53 -0700603 }
604 }
Winson Chung661d5f92018-05-21 18:41:39 -0700605
606 if (mHitTarget != null) {
607 mHitTarget.abortCurrentGesture();
608 }
Matthew Nga8f24262017-12-19 11:54:24 -0800609 }
610 }
611
Matthew Ngb831fb42019-01-30 11:20:48 -0800612 /**
613 * To trigger an edge swipe, the user must start from the left or right edges of certain height
614 * from the bottom then past the drag slope towards the center of the screen, followed by either
615 * a timed trigger for fast swipes or distance if held on the screen longer.
616 * For time, user must swipe up quickly before the Tap Timeout (typically 100ms) and for
617 * distance, the user can drag back to cancel if the touch up has not past the threshold.
618 * @param event Touch up event
619 * @return whether or not edge swipe gesture occurs
620 */
621 private boolean canTriggerEdgeSwipe(MotionEvent event) {
622 if (mNavigationBarView.getWindowTarget() == WINDOW_TARGET_BOTTOM) {
623 return false;
624 }
625 int x = (int) event.getX();
626 int y = (int) event.getY();
627 int xDiff = Math.abs(x - mTouchDownX);
628 int yDiff = Math.abs(y - mTouchDownY);
629 final boolean exceededSwipeTouchSlop = xDiff > NavigationBarCompat.getQuickStepDragSlopPx()
630 && xDiff > yDiff;
631 if (exceededSwipeTouchSlop) {
632 long timeDiff = event.getEventTime() - event.getDownTime();
633 return xDiff > mGestureRegionThreshold || timeDiff < ViewConfiguration.getTapTimeout();
634 }
635 return false;
636 }
637
Matthew Ng86a436e2018-10-26 16:00:53 -0700638 private boolean canPerformAnyAction() {
639 for (NavigationGestureAction action: mGestureActions) {
640 if (action != null && action.isEnabled()) {
641 return true;
Matthew Nga8f24262017-12-19 11:54:24 -0800642 }
Matthew Nged166f92018-02-20 16:22:09 -0800643 }
Matthew Ng86a436e2018-10-26 16:00:53 -0700644 return false;
Matthew Ngc83b9892018-08-21 16:31:13 -0700645 }
646
Matthew Ng11891c92019-01-15 12:12:37 -0800647 private void sendTap(float x, float y) {
648 long now = SystemClock.uptimeMillis();
649 injectMotionEvent(InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.ACTION_DOWN, now, x, y, 1.0f);
650 injectMotionEvent(InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.ACTION_UP, now, x, y, 0.0f);
651 }
652
653 private int getInputDeviceId(int inputSource) {
654 int[] devIds = InputDevice.getDeviceIds();
655 for (int devId : devIds) {
656 InputDevice inputDev = InputDevice.getDevice(devId);
657 if (inputDev.supportsSource(inputSource)) {
658 return devId;
659 }
660 }
661 return 0;
662 }
663
664 private void injectMotionEvent(int inputSource, int action, long when, float x, float y,
665 float pressure) {
666 final float defaultSize = 1.0f;
667 final int defaultMetaState = 0;
668 final float defaultPrecisionX = 1.0f;
669 final float defaultPrecisionY = 1.0f;
670 final int defaultEdgeFlags = 0;
671 MotionEvent event = MotionEvent.obtain(when, when, action, x, y, pressure, defaultSize,
672 defaultMetaState, defaultPrecisionX, defaultPrecisionY,
673 getInputDeviceId(inputSource), defaultEdgeFlags);
674 event.setSource(inputSource);
675 InputManager.getInstance().injectInputEvent(event,
676 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
677 }
678
Matthew Ng2ea93b72018-03-14 19:43:18 +0000679 private boolean proxyMotionEvents(MotionEvent event) {
Matthew Ng2ea93b72018-03-14 19:43:18 +0000680 event.transform(mTransformGlobalMatrix);
Sunny Goyalaac6c882019-02-11 11:57:58 -0800681 InputEventDispatcher dispatcher = mOverviewEventSender.getInputEventDispatcher();
682 if (dispatcher != null) {
683 dispatcher.dispatch(event);
684 }
685
686 final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
Matthew Ng2ea93b72018-03-14 19:43:18 +0000687 try {
688 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
689 overviewProxy.onPreMotionEvent(mNavigationBarView.getDownHitTarget());
690 }
691 overviewProxy.onMotionEvent(event);
692 if (DEBUG_OVERVIEW_PROXY) {
693 Log.d(TAG_OPS, "Send MotionEvent: " + event.toString());
694 }
695 return true;
696 } catch (RemoteException e) {
697 Log.e(TAG, "Callback failed", e);
698 } finally {
699 event.transform(mTransformLocalMatrix);
Matthew Ngdb2734c2018-02-16 16:02:20 -0800700 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000701 return false;
Matthew Ngdb2734c2018-02-16 16:02:20 -0800702 }
Matthew Ngb687c8a2018-10-02 11:31:38 -0700703
Matthew Ng86a436e2018-10-26 16:00:53 -0700704 protected boolean isNavBarVertical() {
705 return mNavBarPosition == NAV_BAR_LEFT || mNavBarPosition == NAV_BAR_RIGHT;
706 }
707
Matthew Ngb831fb42019-01-30 11:20:48 -0800708 private static int convertDpToPixel(float dp) {
709 return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
710 }
711
Matthew Ng86a436e2018-10-26 16:00:53 -0700712 static boolean getBoolGlobalSetting(Context context, String key) {
Matthew Ngb687c8a2018-10-02 11:31:38 -0700713 return Settings.Global.getInt(context.getContentResolver(), key, 0) != 0;
714 }
715
Matthew Ngb831fb42019-01-30 11:20:48 -0800716 static int getIntGlobalSetting(Context context, String key, int defaultValue) {
717 return Settings.Global.getInt(context.getContentResolver(), key, defaultValue);
718 }
719
Matthew Ngb687c8a2018-10-02 11:31:38 -0700720 public static boolean shouldhideBackButton(Context context) {
Matthew Ng86a436e2018-10-26 16:00:53 -0700721 return getBoolGlobalSetting(context, HIDE_BACK_BUTTON_PROP);
Matthew Ngb687c8a2018-10-02 11:31:38 -0700722 }
Matthew Nga8f24262017-12-19 11:54:24 -0800723}