blob: db692c8a8c89c8f47aaed41d15b1bc7158b9c5d9 [file] [log] [blame]
Sunny Goyala6a58122019-04-02 10:20:29 -07001/**
2 * Copyright (C) 2019 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 */
16package com.android.systemui.statusbar.phone;
17
Mady Mellor390bff42019-04-05 15:09:01 -070018import static android.view.Display.INVALID_DISPLAY;
19
Sunny Goyala6a58122019-04-02 10:20:29 -070020import android.content.Context;
Matthew Ng682ad7a2019-04-09 15:48:01 -070021import android.content.res.Resources;
Sunny Goyala6a58122019-04-02 10:20:29 -070022import android.graphics.PixelFormat;
23import android.graphics.Point;
24import android.graphics.PointF;
Sunny Goyala6a58122019-04-02 10:20:29 -070025import android.graphics.Region;
26import android.hardware.display.DisplayManager;
27import android.hardware.display.DisplayManager.DisplayListener;
28import android.hardware.input.InputManager;
29import android.os.Looper;
30import android.os.RemoteException;
31import android.os.SystemClock;
Adam Cohena699c282019-08-07 15:29:34 -070032import android.os.SystemProperties;
Sunny Goyala6a58122019-04-02 10:20:29 -070033import android.util.Log;
Sunny Goyala6a58122019-04-02 10:20:29 -070034import android.view.ISystemGestureExclusionListener;
Siarhei Vishniakoufaa87992019-05-29 09:12:18 -070035import android.view.InputChannel;
Sunny Goyala6a58122019-04-02 10:20:29 -070036import android.view.InputDevice;
37import android.view.InputEvent;
Siarhei Vishniakoufaa87992019-05-29 09:12:18 -070038import android.view.InputEventReceiver;
Sunny Goyala6a58122019-04-02 10:20:29 -070039import android.view.InputMonitor;
40import android.view.KeyCharacterMap;
41import android.view.KeyEvent;
42import android.view.MotionEvent;
43import android.view.ViewConfiguration;
44import android.view.WindowManager;
45import android.view.WindowManagerGlobal;
46
Mady Mellor390bff42019-04-05 15:09:01 -070047import com.android.systemui.Dependency;
Sunny Goyala6a58122019-04-02 10:20:29 -070048import com.android.systemui.R;
Mady Mellor390bff42019-04-05 15:09:01 -070049import com.android.systemui.bubbles.BubbleController;
Vinit Nayak59b114e62019-08-12 11:50:00 -070050import com.android.systemui.model.SysUiState;
Andy Wickham20297012019-11-06 14:56:26 -080051import com.android.systemui.plugins.NavigationEdgeBackPlugin;
52import com.android.systemui.plugins.PluginListener;
Winson Chung04ff8bda2019-04-02 15:08:59 -070053import com.android.systemui.recents.OverviewProxyService;
Andy Wickham20297012019-11-06 14:56:26 -080054import com.android.systemui.shared.plugins.PluginManager;
Sunny Goyala6a58122019-04-02 10:20:29 -070055import com.android.systemui.shared.system.QuickStepContract;
Muhammad Qureshi9bced7d2020-01-16 15:22:12 -080056import com.android.systemui.shared.system.SysUiStatsLog;
Winson Chunga657ac92019-12-09 16:43:46 -080057import com.android.systemui.shared.tracing.ProtoTraceable;
58import com.android.systemui.tracing.ProtoTracer;
59import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
60import com.android.systemui.tracing.nano.SystemUiTraceProto;
Sunny Goyala6a58122019-04-02 10:20:29 -070061
Winson Chung316f6732019-06-14 09:31:46 -070062import java.io.PrintWriter;
Sunny Goyala6a58122019-04-02 10:20:29 -070063import java.util.concurrent.Executor;
64
65/**
66 * Utility class to handle edge swipes for back gesture
67 */
Andy Wickham20297012019-11-06 14:56:26 -080068public class EdgeBackGestureHandler implements DisplayListener,
Winson Chunga657ac92019-12-09 16:43:46 -080069 PluginListener<NavigationEdgeBackPlugin>, ProtoTraceable<SystemUiTraceProto> {
Sunny Goyala6a58122019-04-02 10:20:29 -070070
71 private static final String TAG = "EdgeBackGestureHandler";
Adam Cohena699c282019-08-07 15:29:34 -070072 private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt(
73 "gestures.back_timeout", 250);
Sunny Goyala6a58122019-04-02 10:20:29 -070074
Sunny Goyala6a58122019-04-02 10:20:29 -070075 private ISystemGestureExclusionListener mGestureExclusionListener =
76 new ISystemGestureExclusionListener.Stub() {
77 @Override
78 public void onSystemGestureExclusionChanged(int displayId,
Adrian Roos5f2c9a12019-07-03 18:31:46 +020079 Region systemGestureExclusion, Region unrestrictedOrNull) {
Sunny Goyala6a58122019-04-02 10:20:29 -070080 if (displayId == mDisplayId) {
Hyunyoung Song36d7a2f2019-08-05 17:19:54 -070081 mMainExecutor.execute(() -> {
82 mExcludeRegion.set(systemGestureExclusion);
83 mUnrestrictedExcludeRegion.set(unrestrictedOrNull != null
84 ? unrestrictedOrNull : systemGestureExclusion);
85 });
Sunny Goyala6a58122019-04-02 10:20:29 -070086 }
87 }
88 };
89
90 private final Context mContext;
Winson Chung04ff8bda2019-04-02 15:08:59 -070091 private final OverviewProxyService mOverviewProxyService;
Andy Wickham20297012019-11-06 14:56:26 -080092 private PluginManager mPluginManager;
Sunny Goyala6a58122019-04-02 10:20:29 -070093
94 private final Point mDisplaySize = new Point();
95 private final int mDisplayId;
96
97 private final Executor mMainExecutor;
98
99 private final Region mExcludeRegion = new Region();
Hyunyoung Song36d7a2f2019-08-05 17:19:54 -0700100 private final Region mUnrestrictedExcludeRegion = new Region();
101
Sunny Goyala6a58122019-04-02 10:20:29 -0700102 // The edge width where touch down is allowed
Winson Chung316f6732019-06-14 09:31:46 -0700103 private int mEdgeWidth;
Winson Chung77b2d1d2019-12-20 15:18:42 -0800104 // The bottom gesture area height
105 private int mBottomGestureHeight;
Sunny Goyala6a58122019-04-02 10:20:29 -0700106 // The slop to distinguish between horizontal and vertical motion
107 private final float mTouchSlop;
Sunny Goyal3c317f32019-05-21 11:49:11 -0700108 // Duration after which we consider the event as longpress.
109 private final int mLongPressTimeout;
Sunny Goyala6a58122019-04-02 10:20:29 -0700110
111 private final PointF mDownPoint = new PointF();
112 private boolean mThresholdCrossed = false;
Winson Chunge2a686d2019-04-17 16:54:23 -0700113 private boolean mAllowGesture = false;
Hyunyoung Song36d7a2f2019-08-05 17:19:54 -0700114 private boolean mInRejectedExclusion = false;
Sunny Goyala6a58122019-04-02 10:20:29 -0700115 private boolean mIsOnLeftEdge;
116
Sunny Goyala6a58122019-04-02 10:20:29 -0700117 private boolean mIsAttached;
118 private boolean mIsGesturalModeEnabled;
119 private boolean mIsEnabled;
Jorim Jaggi956ca412019-01-07 14:49:14 +0100120 private boolean mIsNavBarShownTransiently;
Sunny Goyala6a58122019-04-02 10:20:29 -0700121
122 private InputMonitor mInputMonitor;
123 private InputEventReceiver mInputEventReceiver;
124
Andy Wickham20297012019-11-06 14:56:26 -0800125 private NavigationEdgeBackPlugin mEdgeBackPlugin;
Selim Cineke50b1212019-06-04 18:40:31 -0700126 private int mLeftInset;
127 private int mRightInset;
Vinit Nayak59b114e62019-08-12 11:50:00 -0700128 private int mSysUiFlags;
Sunny Goyala6a58122019-04-02 10:20:29 -0700129
Andy Wickham20297012019-11-06 14:56:26 -0800130 private final NavigationEdgeBackPlugin.BackCallback mBackCallback =
131 new NavigationEdgeBackPlugin.BackCallback() {
132 @Override
133 public void triggerBack() {
134 sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
135 sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
136
137 mOverviewProxyService.notifyBackAction(true, (int) mDownPoint.x,
138 (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
139 int backtype = (mInRejectedExclusion
Muhammad Qureshi9bced7d2020-01-16 15:22:12 -0800140 ? SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED_REJECTED :
141 SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED);
142 SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, backtype,
Andy Wickham20297012019-11-06 14:56:26 -0800143 (int) mDownPoint.y, mIsOnLeftEdge
Muhammad Qureshi9bced7d2020-01-16 15:22:12 -0800144 ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT :
145 SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT);
Andy Wickham20297012019-11-06 14:56:26 -0800146 }
147
148 @Override
149 public void cancelBack() {
150 mOverviewProxyService.notifyBackAction(false, (int) mDownPoint.x,
151 (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
Muhammad Qureshi9bced7d2020-01-16 15:22:12 -0800152 int backtype = SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE;
153 SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, backtype,
Andy Wickham20297012019-11-06 14:56:26 -0800154 (int) mDownPoint.y, mIsOnLeftEdge
Muhammad Qureshi9bced7d2020-01-16 15:22:12 -0800155 ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT :
156 SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT);
Andy Wickham20297012019-11-06 14:56:26 -0800157 }
158 };
159
Vinit Nayak59b114e62019-08-12 11:50:00 -0700160 public EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService,
Andy Wickham20297012019-11-06 14:56:26 -0800161 SysUiState sysUiFlagContainer, PluginManager pluginManager) {
Matthew Ng682ad7a2019-04-09 15:48:01 -0700162 final Resources res = context.getResources();
Sunny Goyala6a58122019-04-02 10:20:29 -0700163 mContext = context;
164 mDisplayId = context.getDisplayId();
165 mMainExecutor = context.getMainExecutor();
Winson Chung04ff8bda2019-04-02 15:08:59 -0700166 mOverviewProxyService = overviewProxyService;
Andy Wickham20297012019-11-06 14:56:26 -0800167 mPluginManager = pluginManager;
Winson Chunga657ac92019-12-09 16:43:46 -0800168 Dependency.get(ProtoTracer.class).add(this);
Sunny Goyala6a58122019-04-02 10:20:29 -0700169
Siarhei Vishniakoufaa87992019-05-29 09:12:18 -0700170 // Reduce the default touch slop to ensure that we can intercept the gesture
171 // before the app starts to react to it.
172 // TODO(b/130352502) Tune this value and extract into a constant
173 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 0.75f;
Winson Chung05d4be32019-06-19 13:12:10 -0700174 mLongPressTimeout = Math.min(MAX_LONG_PRESS_TIMEOUT,
175 ViewConfiguration.getLongPressTimeout());
Sunny Goyala6a58122019-04-02 10:20:29 -0700176
Winson Chung316f6732019-06-14 09:31:46 -0700177 updateCurrentUserResources(res);
Vinit Nayak59b114e62019-08-12 11:50:00 -0700178 sysUiFlagContainer.addCallback(sysUiFlags -> mSysUiFlags = sysUiFlags);
Winson Chung316f6732019-06-14 09:31:46 -0700179 }
180
181 public void updateCurrentUserResources(Resources res) {
182 mEdgeWidth = res.getDimensionPixelSize(
183 com.android.internal.R.dimen.config_backGestureInset);
Winson Chung77b2d1d2019-12-20 15:18:42 -0800184 mBottomGestureHeight = res.getDimensionPixelSize(
185 com.android.internal.R.dimen.navigation_bar_gesture_height);
Sunny Goyala6a58122019-04-02 10:20:29 -0700186 }
187
188 /**
189 * @see NavigationBarView#onAttachedToWindow()
190 */
191 public void onNavBarAttached() {
192 mIsAttached = true;
Winson Chung1aa24b92019-04-24 15:17:33 -0700193 updateIsEnabled();
Sunny Goyala6a58122019-04-02 10:20:29 -0700194 }
195
196 /**
197 * @see NavigationBarView#onDetachedFromWindow()
198 */
199 public void onNavBarDetached() {
200 mIsAttached = false;
201 updateIsEnabled();
202 }
203
Winson Chung316f6732019-06-14 09:31:46 -0700204 public void onNavigationModeChanged(int mode, Context currentUserContext) {
Winson Chung1aa24b92019-04-24 15:17:33 -0700205 mIsGesturalModeEnabled = QuickStepContract.isGesturalMode(mode);
Sunny Goyala6a58122019-04-02 10:20:29 -0700206 updateIsEnabled();
Winson Chung316f6732019-06-14 09:31:46 -0700207 updateCurrentUserResources(currentUserContext.getResources());
Sunny Goyala6a58122019-04-02 10:20:29 -0700208 }
209
Jorim Jaggi956ca412019-01-07 14:49:14 +0100210 public void onNavBarTransientStateChanged(boolean isTransient) {
211 mIsNavBarShownTransiently = isTransient;
Winson Chung3d6c0802019-10-04 10:37:12 -0700212 }
213
Sunny Goyala6a58122019-04-02 10:20:29 -0700214 private void disposeInputChannel() {
215 if (mInputEventReceiver != null) {
216 mInputEventReceiver.dispose();
217 mInputEventReceiver = null;
218 }
219 if (mInputMonitor != null) {
220 mInputMonitor.dispose();
221 mInputMonitor = null;
222 }
223 }
224
225 private void updateIsEnabled() {
226 boolean isEnabled = mIsAttached && mIsGesturalModeEnabled;
227 if (isEnabled == mIsEnabled) {
228 return;
229 }
230 mIsEnabled = isEnabled;
231 disposeInputChannel();
232
Andy Wickham20297012019-11-06 14:56:26 -0800233 if (mEdgeBackPlugin != null) {
234 mEdgeBackPlugin.onDestroy();
235 mEdgeBackPlugin = null;
Sunny Goyala6a58122019-04-02 10:20:29 -0700236 }
237
238 if (!mIsEnabled) {
Sunny Goyal19049692019-04-08 11:56:07 -0700239 mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
Andy Wickham20297012019-11-06 14:56:26 -0800240 mPluginManager.removePluginListener(this);
Sunny Goyala6a58122019-04-02 10:20:29 -0700241
242 try {
243 WindowManagerGlobal.getWindowManagerService()
244 .unregisterSystemGestureExclusionListener(
245 mGestureExclusionListener, mDisplayId);
246 } catch (RemoteException e) {
247 Log.e(TAG, "Failed to unregister window manager callbacks", e);
248 }
249
250 } else {
251 updateDisplaySize();
Sunny Goyal19049692019-04-08 11:56:07 -0700252 mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
253 mContext.getMainThreadHandler());
Sunny Goyala6a58122019-04-02 10:20:29 -0700254
255 try {
Sunny Goyala6a58122019-04-02 10:20:29 -0700256 WindowManagerGlobal.getWindowManagerService()
257 .registerSystemGestureExclusionListener(
258 mGestureExclusionListener, mDisplayId);
259 } catch (RemoteException e) {
260 Log.e(TAG, "Failed to register window manager callbacks", e);
261 }
262
263 // Register input event receiver
264 mInputMonitor = InputManager.getInstance().monitorGestureInput(
265 "edge-swipe", mDisplayId);
Siarhei Vishniakoufaa87992019-05-29 09:12:18 -0700266 mInputEventReceiver = new SysUiInputEventReceiver(
267 mInputMonitor.getInputChannel(), Looper.getMainLooper());
Sunny Goyala6a58122019-04-02 10:20:29 -0700268
269 // Add a nav bar panel window
Andy Wickham20297012019-11-06 14:56:26 -0800270 setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));
271 mPluginManager.addPluginListener(
272 this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
Sunny Goyala6a58122019-04-02 10:20:29 -0700273 }
274 }
275
Andy Wickham20297012019-11-06 14:56:26 -0800276 @Override
277 public void onPluginConnected(NavigationEdgeBackPlugin plugin, Context context) {
278 setEdgeBackPlugin(plugin);
279 }
280
281 @Override
282 public void onPluginDisconnected(NavigationEdgeBackPlugin plugin) {
283 setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));
284 }
285
286 private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {
287 if (mEdgeBackPlugin != null) {
288 mEdgeBackPlugin.onDestroy();
289 }
290 mEdgeBackPlugin = edgeBackPlugin;
291 mEdgeBackPlugin.setBackCallback(mBackCallback);
292 mEdgeBackPlugin.setLayoutParams(createLayoutParams());
293 updateDisplaySize();
294 }
295
296 private WindowManager.LayoutParams createLayoutParams() {
297 Resources resources = mContext.getResources();
298 WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
299 resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_width),
300 resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_height),
301 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
302 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
303 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
304 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
305 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
306 PixelFormat.TRANSLUCENT);
307 layoutParams.privateFlags |=
308 WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
309 layoutParams.setTitle(TAG + mContext.getDisplayId());
310 layoutParams.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
311 layoutParams.windowAnimations = 0;
Tiger Huang52724442020-01-20 21:38:42 +0800312 layoutParams.setFitInsetsTypes(0 /* types */);
Andy Wickham20297012019-11-06 14:56:26 -0800313 return layoutParams;
314 }
315
Sunny Goyala6a58122019-04-02 10:20:29 -0700316 private void onInputEvent(InputEvent ev) {
317 if (ev instanceof MotionEvent) {
318 onMotionEvent((MotionEvent) ev);
319 }
320 }
321
322 private boolean isWithinTouchRegion(int x, int y) {
Winson Chung3d6c0802019-10-04 10:37:12 -0700323 // Disallow if too far from the edge
Selim Cineke50b1212019-06-04 18:40:31 -0700324 if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) {
Sunny Goyala6a58122019-04-02 10:20:29 -0700325 return false;
326 }
Winson Chung3d6c0802019-10-04 10:37:12 -0700327
Winson Chung77b2d1d2019-12-20 15:18:42 -0800328 // Disallow if we are in the bottom gesture area
329 if (y >= (mDisplaySize.y - mBottomGestureHeight)) {
330 return false;
331 }
332
Winson Chung3d6c0802019-10-04 10:37:12 -0700333 // Always allow if the user is in a transient sticky immersive state
Jorim Jaggi956ca412019-01-07 14:49:14 +0100334 if (mIsNavBarShownTransiently) {
Winson Chung3d6c0802019-10-04 10:37:12 -0700335 return true;
336 }
337
Winson Chung51787a12019-04-10 15:33:31 -0700338 boolean isInExcludedRegion = mExcludeRegion.contains(x, y);
339 if (isInExcludedRegion) {
340 mOverviewProxyService.notifyBackAction(false /* completed */, -1, -1,
341 false /* isButton */, !mIsOnLeftEdge);
Muhammad Qureshi9bced7d2020-01-16 15:22:12 -0800342 SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED,
343 SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_EXCLUDED, y,
344 mIsOnLeftEdge ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT :
345 SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT);
Hyunyoung Song36d7a2f2019-08-05 17:19:54 -0700346 } else {
347 mInRejectedExclusion = mUnrestrictedExcludeRegion.contains(x, y);
Winson Chung51787a12019-04-10 15:33:31 -0700348 }
349 return !isInExcludedRegion;
Sunny Goyala6a58122019-04-02 10:20:29 -0700350 }
351
Sunny Goyal3c317f32019-05-21 11:49:11 -0700352 private void cancelGesture(MotionEvent ev) {
353 // Send action cancel to reset all the touch events
354 mAllowGesture = false;
Hyunyoung Song36d7a2f2019-08-05 17:19:54 -0700355 mInRejectedExclusion = false;
Sunny Goyal3c317f32019-05-21 11:49:11 -0700356 MotionEvent cancelEv = MotionEvent.obtain(ev);
357 cancelEv.setAction(MotionEvent.ACTION_CANCEL);
Andy Wickham20297012019-11-06 14:56:26 -0800358 mEdgeBackPlugin.onMotionEvent(cancelEv);
Sunny Goyal3c317f32019-05-21 11:49:11 -0700359 cancelEv.recycle();
360 }
361
Sunny Goyala6a58122019-04-02 10:20:29 -0700362 private void onMotionEvent(MotionEvent ev) {
Sunny Goyal3c317f32019-05-21 11:49:11 -0700363 int action = ev.getActionMasked();
364 if (action == MotionEvent.ACTION_DOWN) {
Winson Chunge2a686d2019-04-17 16:54:23 -0700365 // Verify if this is in within the touch region and we aren't in immersive mode, and
366 // either the bouncer is showing or the notification panel is hidden
Selim Cineke50b1212019-06-04 18:40:31 -0700367 mIsOnLeftEdge = ev.getX() <= mEdgeWidth + mLeftInset;
Hyunyoung Song36d7a2f2019-08-05 17:19:54 -0700368 mInRejectedExclusion = false;
Vinit Nayak59b114e62019-08-12 11:50:00 -0700369 mAllowGesture = !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
Winson Chunge2a686d2019-04-17 16:54:23 -0700370 && isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
371 if (mAllowGesture) {
Andy Wickham20297012019-11-06 14:56:26 -0800372 mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
373 mEdgeBackPlugin.onMotionEvent(ev);
Sunny Goyala6a58122019-04-02 10:20:29 -0700374
375 mDownPoint.set(ev.getX(), ev.getY());
376 mThresholdCrossed = false;
Sunny Goyala6a58122019-04-02 10:20:29 -0700377 }
Winson Chunge2a686d2019-04-17 16:54:23 -0700378 } else if (mAllowGesture) {
Sunny Goyal3c317f32019-05-21 11:49:11 -0700379 if (!mThresholdCrossed) {
380 if (action == MotionEvent.ACTION_POINTER_DOWN) {
381 // We do not support multi touch for back gesture
382 cancelGesture(ev);
Sunny Goyala6a58122019-04-02 10:20:29 -0700383 return;
Sunny Goyal3c317f32019-05-21 11:49:11 -0700384 } else if (action == MotionEvent.ACTION_MOVE) {
385 if ((ev.getEventTime() - ev.getDownTime()) > mLongPressTimeout) {
386 cancelGesture(ev);
387 return;
388 }
389 float dx = Math.abs(ev.getX() - mDownPoint.x);
390 float dy = Math.abs(ev.getY() - mDownPoint.y);
391 if (dy > dx && dy > mTouchSlop) {
392 cancelGesture(ev);
393 return;
Sunny Goyala6a58122019-04-02 10:20:29 -0700394
Sunny Goyal3c317f32019-05-21 11:49:11 -0700395 } else if (dx > dy && dx > mTouchSlop) {
396 mThresholdCrossed = true;
397 // Capture inputs
398 mInputMonitor.pilferPointers();
399 }
Sunny Goyala6a58122019-04-02 10:20:29 -0700400 }
Sunny Goyal3c317f32019-05-21 11:49:11 -0700401
Sunny Goyala6a58122019-04-02 10:20:29 -0700402 }
403
404 // forward touch
Andy Wickham20297012019-11-06 14:56:26 -0800405 mEdgeBackPlugin.onMotionEvent(ev);
Sunny Goyala6a58122019-04-02 10:20:29 -0700406 }
Winson Chunga657ac92019-12-09 16:43:46 -0800407
408 Dependency.get(ProtoTracer.class).update();
Sunny Goyala6a58122019-04-02 10:20:29 -0700409 }
410
411 @Override
412 public void onDisplayAdded(int displayId) { }
413
414 @Override
415 public void onDisplayRemoved(int displayId) { }
416
417 @Override
418 public void onDisplayChanged(int displayId) {
419 if (displayId == mDisplayId) {
420 updateDisplaySize();
421 }
422 }
423
424 private void updateDisplaySize() {
425 mContext.getSystemService(DisplayManager.class)
Sunny Goyal19049692019-04-08 11:56:07 -0700426 .getDisplay(mDisplayId)
427 .getRealSize(mDisplaySize);
Andy Wickham20297012019-11-06 14:56:26 -0800428 if (mEdgeBackPlugin != null) {
429 mEdgeBackPlugin.setDisplaySize(mDisplaySize);
430 }
Sunny Goyala6a58122019-04-02 10:20:29 -0700431 }
432
433 private void sendEvent(int action, int code) {
434 long when = SystemClock.uptimeMillis();
435 final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
436 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
437 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
438 InputDevice.SOURCE_KEYBOARD);
Mady Mellor390bff42019-04-05 15:09:01 -0700439
440 // Bubble controller will give us a valid display id if it should get the back event
441 BubbleController bubbleController = Dependency.get(BubbleController.class);
442 int bubbleDisplayId = bubbleController.getExpandedDisplayId(mContext);
443 if (code == KeyEvent.KEYCODE_BACK && bubbleDisplayId != INVALID_DISPLAY) {
444 ev.setDisplayId(bubbleDisplayId);
445 }
Sunny Goyala6a58122019-04-02 10:20:29 -0700446 InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
447 }
Siarhei Vishniakoufaa87992019-05-29 09:12:18 -0700448
Selim Cineke50b1212019-06-04 18:40:31 -0700449 public void setInsets(int leftInset, int rightInset) {
450 mLeftInset = leftInset;
451 mRightInset = rightInset;
Andy Wickham20297012019-11-06 14:56:26 -0800452 if (mEdgeBackPlugin != null) {
453 mEdgeBackPlugin.setInsets(leftInset, rightInset);
454 }
Selim Cineke50b1212019-06-04 18:40:31 -0700455 }
456
Winson Chung316f6732019-06-14 09:31:46 -0700457 public void dump(PrintWriter pw) {
458 pw.println("EdgeBackGestureHandler:");
459 pw.println(" mIsEnabled=" + mIsEnabled);
460 pw.println(" mAllowGesture=" + mAllowGesture);
Hyunyoung Song36d7a2f2019-08-05 17:19:54 -0700461 pw.println(" mInRejectedExclusion" + mInRejectedExclusion);
Winson Chung316f6732019-06-14 09:31:46 -0700462 pw.println(" mExcludeRegion=" + mExcludeRegion);
Hyunyoung Song36d7a2f2019-08-05 17:19:54 -0700463 pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion);
Winson Chung316f6732019-06-14 09:31:46 -0700464 pw.println(" mIsAttached=" + mIsAttached);
465 pw.println(" mEdgeWidth=" + mEdgeWidth);
466 }
467
Winson Chunga657ac92019-12-09 16:43:46 -0800468 @Override
469 public void writeToProto(SystemUiTraceProto proto) {
470 if (proto.edgeBackGestureHandler == null) {
471 proto.edgeBackGestureHandler = new EdgeBackGestureHandlerProto();
472 }
473 proto.edgeBackGestureHandler.allowGesture = mAllowGesture;
474 }
475
Siarhei Vishniakoufaa87992019-05-29 09:12:18 -0700476 class SysUiInputEventReceiver extends InputEventReceiver {
477 SysUiInputEventReceiver(InputChannel channel, Looper looper) {
478 super(channel, looper);
479 }
480
481 public void onInputEvent(InputEvent event) {
482 EdgeBackGestureHandler.this.onInputEvent(event);
483 finishInputEvent(event, true);
484 }
485 }
Sunny Goyala6a58122019-04-02 10:20:29 -0700486}