blob: cad1c91975bf1db91cd02b52e70fce9153c010fc [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
Andy Wickham3b3b9db2020-02-18 19:02:11 +000020import android.app.ActivityManager;
21import android.content.ComponentName;
Sunny Goyala6a58122019-04-02 10:20:29 -070022import android.content.Context;
Andy Wickham3b3b9db2020-02-18 19:02:11 +000023import android.content.pm.PackageManager;
24import android.content.pm.PackageManager.NameNotFoundException;
Matthew Ng682ad7a2019-04-09 15:48:01 -070025import android.content.res.Resources;
Vinit Nayakf848e9d2020-03-24 16:04:09 -070026import android.database.ContentObserver;
Sunny Goyala6a58122019-04-02 10:20:29 -070027import android.graphics.PixelFormat;
28import android.graphics.Point;
29import android.graphics.PointF;
Sunny Goyala6a58122019-04-02 10:20:29 -070030import android.graphics.Region;
31import android.hardware.display.DisplayManager;
32import android.hardware.display.DisplayManager.DisplayListener;
33import android.hardware.input.InputManager;
Vinit Nayakf848e9d2020-03-24 16:04:09 -070034import android.net.Uri;
35import android.os.Handler;
Sunny Goyala6a58122019-04-02 10:20:29 -070036import android.os.Looper;
37import android.os.RemoteException;
38import android.os.SystemClock;
Adam Cohena699c282019-08-07 15:29:34 -070039import android.os.SystemProperties;
Vinit Nayakf848e9d2020-03-24 16:04:09 -070040import android.os.UserHandle;
Maryama142e522020-04-27 17:41:42 -070041import android.provider.DeviceConfig;
Vinit Nayakf848e9d2020-03-24 16:04:09 -070042import android.provider.Settings;
Winson Chung4ef84182020-05-01 15:22:47 -070043import android.util.DisplayMetrics;
Sunny Goyala6a58122019-04-02 10:20:29 -070044import android.util.Log;
Maryama142e522020-04-27 17:41:42 -070045import android.util.TypedValue;
Sunny Goyala6a58122019-04-02 10:20:29 -070046import android.view.ISystemGestureExclusionListener;
Siarhei Vishniakoufaa87992019-05-29 09:12:18 -070047import android.view.InputChannel;
Sunny Goyala6a58122019-04-02 10:20:29 -070048import android.view.InputDevice;
49import android.view.InputEvent;
Siarhei Vishniakoufaa87992019-05-29 09:12:18 -070050import android.view.InputEventReceiver;
Sunny Goyala6a58122019-04-02 10:20:29 -070051import android.view.InputMonitor;
52import android.view.KeyCharacterMap;
53import android.view.KeyEvent;
54import android.view.MotionEvent;
Vinit Nayakf848e9d2020-03-24 16:04:09 -070055import android.view.Surface;
Sunny Goyala6a58122019-04-02 10:20:29 -070056import android.view.ViewConfiguration;
57import android.view.WindowManager;
58import android.view.WindowManagerGlobal;
59
Maryama142e522020-04-27 17:41:42 -070060import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
Mehdi Alizadehf2b28e92019-12-05 20:13:49 -080061import com.android.internal.policy.GestureNavigationSettingsObserver;
Mady Mellor390bff42019-04-05 15:09:01 -070062import com.android.systemui.Dependency;
Sunny Goyala6a58122019-04-02 10:20:29 -070063import com.android.systemui.R;
Sunny Goyal38412952020-04-17 11:35:37 -070064import com.android.systemui.broadcast.BroadcastDispatcher;
Mady Mellor390bff42019-04-05 15:09:01 -070065import com.android.systemui.bubbles.BubbleController;
Vinit Nayak59b114e62019-08-12 11:50:00 -070066import com.android.systemui.model.SysUiState;
Andy Wickham20297012019-11-06 14:56:26 -080067import com.android.systemui.plugins.NavigationEdgeBackPlugin;
68import com.android.systemui.plugins.PluginListener;
Winson Chung04ff8bda2019-04-02 15:08:59 -070069import com.android.systemui.recents.OverviewProxyService;
Sunny Goyal38412952020-04-17 11:35:37 -070070import com.android.systemui.settings.CurrentUserTracker;
Andy Wickham20297012019-11-06 14:56:26 -080071import com.android.systemui.shared.plugins.PluginManager;
Vinit Nayakf848e9d2020-03-24 16:04:09 -070072import com.android.systemui.shared.system.ActivityManagerWrapper;
Sunny Goyala6a58122019-04-02 10:20:29 -070073import com.android.systemui.shared.system.QuickStepContract;
Muhammad Qureshi9bced7d2020-01-16 15:22:12 -080074import com.android.systemui.shared.system.SysUiStatsLog;
Vinit Nayakf848e9d2020-03-24 16:04:09 -070075import com.android.systemui.shared.system.TaskStackChangeListener;
Winson Chunga657ac92019-12-09 16:43:46 -080076import com.android.systemui.shared.tracing.ProtoTraceable;
77import com.android.systemui.tracing.ProtoTracer;
78import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
79import com.android.systemui.tracing.nano.SystemUiTraceProto;
Sunny Goyala6a58122019-04-02 10:20:29 -070080
Winson Chung316f6732019-06-14 09:31:46 -070081import java.io.PrintWriter;
Andy Wickham3b3b9db2020-02-18 19:02:11 +000082import java.util.ArrayList;
83import java.util.List;
Sunny Goyala6a58122019-04-02 10:20:29 -070084import java.util.concurrent.Executor;
85
86/**
87 * Utility class to handle edge swipes for back gesture
88 */
Sunny Goyal38412952020-04-17 11:35:37 -070089public class EdgeBackGestureHandler extends CurrentUserTracker implements DisplayListener,
Winson Chunga657ac92019-12-09 16:43:46 -080090 PluginListener<NavigationEdgeBackPlugin>, ProtoTraceable<SystemUiTraceProto> {
Sunny Goyala6a58122019-04-02 10:20:29 -070091
92 private static final String TAG = "EdgeBackGestureHandler";
Adam Cohena699c282019-08-07 15:29:34 -070093 private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt(
94 "gestures.back_timeout", 250);
Vinit Nayakf848e9d2020-03-24 16:04:09 -070095 private static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform";
96
Sunny Goyala6a58122019-04-02 10:20:29 -070097
Sunny Goyala6a58122019-04-02 10:20:29 -070098 private ISystemGestureExclusionListener mGestureExclusionListener =
99 new ISystemGestureExclusionListener.Stub() {
100 @Override
101 public void onSystemGestureExclusionChanged(int displayId,
Adrian Roos5f2c9a12019-07-03 18:31:46 +0200102 Region systemGestureExclusion, Region unrestrictedOrNull) {
Sunny Goyala6a58122019-04-02 10:20:29 -0700103 if (displayId == mDisplayId) {
Hyunyoung Song36d7a2f2019-08-05 17:19:54 -0700104 mMainExecutor.execute(() -> {
105 mExcludeRegion.set(systemGestureExclusion);
106 mUnrestrictedExcludeRegion.set(unrestrictedOrNull != null
107 ? unrestrictedOrNull : systemGestureExclusion);
108 });
Sunny Goyala6a58122019-04-02 10:20:29 -0700109 }
110 }
111 };
112
Vinit Nayakf848e9d2020-03-24 16:04:09 -0700113 private OverviewProxyService.OverviewProxyListener mQuickSwitchListener =
114 new OverviewProxyService.OverviewProxyListener() {
115 @Override
116 public void onQuickSwitchToNewTask(@Surface.Rotation int rotation) {
117 mStartingQuickstepRotation = rotation;
118 updateDisabledForQuickstep();
119 }
120 };
121
Vinit Nayakf848e9d2020-03-24 16:04:09 -0700122 private final ContentObserver mFixedRotationObserver = new ContentObserver(
123 new Handler(Looper.getMainLooper())) {
124 @Override
125 public void onChange(boolean selfChange, Uri uri) {
126 updatedFixedRotation();
127 }
128 };
129
Andy Wickham3b3b9db2020-02-18 19:02:11 +0000130 private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
131 @Override
132 public void onTaskStackChanged() {
133 mGestureBlockingActivityRunning = isGestureBlockingActivityRunning();
134 }
135 };
136
Sunny Goyala6a58122019-04-02 10:20:29 -0700137 private final Context mContext;
Winson Chung04ff8bda2019-04-02 15:08:59 -0700138 private final OverviewProxyService mOverviewProxyService;
Andy Wickham3b3b9db2020-02-18 19:02:11 +0000139 private final PluginManager mPluginManager;
140 // Activities which should not trigger Back gesture.
141 private final List<ComponentName> mGestureBlockingActivities = new ArrayList<>();
Sunny Goyala6a58122019-04-02 10:20:29 -0700142
143 private final Point mDisplaySize = new Point();
144 private final int mDisplayId;
145
146 private final Executor mMainExecutor;
147
148 private final Region mExcludeRegion = new Region();
Hyunyoung Song36d7a2f2019-08-05 17:19:54 -0700149 private final Region mUnrestrictedExcludeRegion = new Region();
150
Mehdi Alizadehf2b28e92019-12-05 20:13:49 -0800151 // The left side edge width where touch down is allowed
152 private int mEdgeWidthLeft;
153 // The right side edge width where touch down is allowed
154 private int mEdgeWidthRight;
Winson Chung77b2d1d2019-12-20 15:18:42 -0800155 // The bottom gesture area height
Maryama142e522020-04-27 17:41:42 -0700156 private float mBottomGestureHeight;
Sunny Goyala6a58122019-04-02 10:20:29 -0700157 // The slop to distinguish between horizontal and vertical motion
Maryama142e522020-04-27 17:41:42 -0700158 private float mTouchSlop;
Sunny Goyal3c317f32019-05-21 11:49:11 -0700159 // Duration after which we consider the event as longpress.
160 private final int mLongPressTimeout;
Vinit Nayakf848e9d2020-03-24 16:04:09 -0700161 private int mStartingQuickstepRotation = -1;
162 // We temporarily disable back gesture when user is quickswitching
163 // between apps of different orientations
164 private boolean mDisabledForQuickstep;
165 private boolean mFixedRotationFlagEnabled;
Sunny Goyala6a58122019-04-02 10:20:29 -0700166
167 private final PointF mDownPoint = new PointF();
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700168 private final PointF mEndPoint = new PointF();
Sunny Goyala6a58122019-04-02 10:20:29 -0700169 private boolean mThresholdCrossed = false;
Winson Chunge2a686d2019-04-17 16:54:23 -0700170 private boolean mAllowGesture = false;
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700171 private boolean mLogGesture = false;
Hyunyoung Song36d7a2f2019-08-05 17:19:54 -0700172 private boolean mInRejectedExclusion = false;
Sunny Goyala6a58122019-04-02 10:20:29 -0700173 private boolean mIsOnLeftEdge;
174
Sunny Goyala6a58122019-04-02 10:20:29 -0700175 private boolean mIsAttached;
176 private boolean mIsGesturalModeEnabled;
177 private boolean mIsEnabled;
Jorim Jaggi956ca412019-01-07 14:49:14 +0100178 private boolean mIsNavBarShownTransiently;
Sunny Goyal38412952020-04-17 11:35:37 -0700179 private boolean mIsBackGestureAllowed;
Andy Wickham3b3b9db2020-02-18 19:02:11 +0000180 private boolean mGestureBlockingActivityRunning;
Sunny Goyala6a58122019-04-02 10:20:29 -0700181
182 private InputMonitor mInputMonitor;
183 private InputEventReceiver mInputEventReceiver;
184
Andy Wickham20297012019-11-06 14:56:26 -0800185 private NavigationEdgeBackPlugin mEdgeBackPlugin;
Selim Cineke50b1212019-06-04 18:40:31 -0700186 private int mLeftInset;
187 private int mRightInset;
Vinit Nayak59b114e62019-08-12 11:50:00 -0700188 private int mSysUiFlags;
Sunny Goyala6a58122019-04-02 10:20:29 -0700189
Mehdi Alizadehf2b28e92019-12-05 20:13:49 -0800190 private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
191
Andy Wickham20297012019-11-06 14:56:26 -0800192 private final NavigationEdgeBackPlugin.BackCallback mBackCallback =
193 new NavigationEdgeBackPlugin.BackCallback() {
194 @Override
195 public void triggerBack() {
196 sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
197 sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
198
199 mOverviewProxyService.notifyBackAction(true, (int) mDownPoint.x,
200 (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700201 logGesture(mInRejectedExclusion
202 ? SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED_REJECTED
203 : SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED);
Andy Wickham20297012019-11-06 14:56:26 -0800204 }
205
206 @Override
207 public void cancelBack() {
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700208 logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE);
Andy Wickham20297012019-11-06 14:56:26 -0800209 mOverviewProxyService.notifyBackAction(false, (int) mDownPoint.x,
210 (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
Andy Wickham20297012019-11-06 14:56:26 -0800211 }
212 };
213
Vinit Nayak59b114e62019-08-12 11:50:00 -0700214 public EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService,
Andy Wickham20297012019-11-06 14:56:26 -0800215 SysUiState sysUiFlagContainer, PluginManager pluginManager) {
Sunny Goyal38412952020-04-17 11:35:37 -0700216 super(Dependency.get(BroadcastDispatcher.class));
Sunny Goyala6a58122019-04-02 10:20:29 -0700217 mContext = context;
218 mDisplayId = context.getDisplayId();
219 mMainExecutor = context.getMainExecutor();
Winson Chung04ff8bda2019-04-02 15:08:59 -0700220 mOverviewProxyService = overviewProxyService;
Andy Wickham20297012019-11-06 14:56:26 -0800221 mPluginManager = pluginManager;
Andy Wickham3b3b9db2020-02-18 19:02:11 +0000222 ComponentName recentsComponentName = ComponentName.unflattenFromString(
223 context.getString(com.android.internal.R.string.config_recentsComponentName));
224 if (recentsComponentName != null) {
225 String recentsPackageName = recentsComponentName.getPackageName();
226 PackageManager manager = context.getPackageManager();
227 try {
228 Resources resources = manager.getResourcesForApplication(recentsPackageName);
229 int resId = resources.getIdentifier(
230 "gesture_blocking_activities", "array", recentsPackageName);
231
232 if (resId == 0) {
233 Log.e(TAG, "No resource found for gesture-blocking activities");
234 } else {
235 String[] gestureBlockingActivities = resources.getStringArray(resId);
236 for (String gestureBlockingActivity : gestureBlockingActivities) {
237 mGestureBlockingActivities.add(
238 ComponentName.unflattenFromString(gestureBlockingActivity));
239 }
240 }
241 } catch (NameNotFoundException e) {
242 Log.e(TAG, "Failed to add gesture blocking activities", e);
243 }
244 }
Winson Chunga657ac92019-12-09 16:43:46 -0800245 Dependency.get(ProtoTracer.class).add(this);
Sunny Goyala6a58122019-04-02 10:20:29 -0700246
Winson Chung05d4be32019-06-19 13:12:10 -0700247 mLongPressTimeout = Math.min(MAX_LONG_PRESS_TIMEOUT,
248 ViewConfiguration.getLongPressTimeout());
Sunny Goyala6a58122019-04-02 10:20:29 -0700249
Mehdi Alizadehf2b28e92019-12-05 20:13:49 -0800250 mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(
Sunny Goyal38412952020-04-17 11:35:37 -0700251 mContext.getMainThreadHandler(), mContext, this::updateCurrentUserResources);
Mehdi Alizadehf2b28e92019-12-05 20:13:49 -0800252
Sunny Goyal38412952020-04-17 11:35:37 -0700253 updateCurrentUserResources();
Vinit Nayak59b114e62019-08-12 11:50:00 -0700254 sysUiFlagContainer.addCallback(sysUiFlags -> mSysUiFlags = sysUiFlags);
Winson Chung316f6732019-06-14 09:31:46 -0700255 }
256
Sunny Goyal38412952020-04-17 11:35:37 -0700257 public void updateCurrentUserResources() {
258 Resources res = Dependency.get(NavigationModeController.class).getCurrentUserContext()
259 .getResources();
Mehdi Alizadehf2b28e92019-12-05 20:13:49 -0800260 mEdgeWidthLeft = mGestureNavigationSettingsObserver.getLeftSensitivity(res);
261 mEdgeWidthRight = mGestureNavigationSettingsObserver.getRightSensitivity(res);
Sunny Goyal38412952020-04-17 11:35:37 -0700262 mIsBackGestureAllowed =
263 !mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible();
Mehdi Alizadehf2b28e92019-12-05 20:13:49 -0800264
Winson Chung4ef84182020-05-01 15:22:47 -0700265 final DisplayMetrics dm = res.getDisplayMetrics();
Maryama142e522020-04-27 17:41:42 -0700266 final float defaultGestureHeight = res.getDimension(
Winson Chung4ef84182020-05-01 15:22:47 -0700267 com.android.internal.R.dimen.navigation_bar_gesture_height) / dm.density;
Maryama142e522020-04-27 17:41:42 -0700268 final float gestureHeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
269 SystemUiDeviceConfigFlags.BACK_GESTURE_BOTTOM_HEIGHT,
270 defaultGestureHeight);
Winson Chung4ef84182020-05-01 15:22:47 -0700271 mBottomGestureHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, gestureHeight,
272 dm);
Maryama142e522020-04-27 17:41:42 -0700273
274 // Reduce the default touch slop to ensure that we can intercept the gesture
275 // before the app starts to react to it.
276 // TODO(b/130352502) Tune this value and extract into a constant
277 final float backGestureSlop = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
278 SystemUiDeviceConfigFlags.BACK_GESTURE_SLOP_MULTIPLIER, 0.75f);
279 mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop() * backGestureSlop;
Sunny Goyala6a58122019-04-02 10:20:29 -0700280 }
281
Sunny Goyal38412952020-04-17 11:35:37 -0700282 @Override
283 public void onUserSwitched(int newUserId) {
284 updateIsEnabled();
285 updateCurrentUserResources();
286 }
287
Sunny Goyala6a58122019-04-02 10:20:29 -0700288 /**
289 * @see NavigationBarView#onAttachedToWindow()
290 */
291 public void onNavBarAttached() {
292 mIsAttached = true;
Vinit Nayakf848e9d2020-03-24 16:04:09 -0700293 updatedFixedRotation();
294 if (mFixedRotationFlagEnabled) {
295 setRotationCallbacks(true);
296 }
297 mContext.getContentResolver().registerContentObserver(
298 Settings.Global.getUriFor(FIXED_ROTATION_TRANSFORM_SETTING_NAME),
299 false /* notifyForDescendants */, mFixedRotationObserver, UserHandle.USER_ALL);
Winson Chung1aa24b92019-04-24 15:17:33 -0700300 updateIsEnabled();
Sunny Goyal38412952020-04-17 11:35:37 -0700301 startTracking();
Sunny Goyala6a58122019-04-02 10:20:29 -0700302 }
303
304 /**
305 * @see NavigationBarView#onDetachedFromWindow()
306 */
307 public void onNavBarDetached() {
308 mIsAttached = false;
Vinit Nayakf848e9d2020-03-24 16:04:09 -0700309 if (mFixedRotationFlagEnabled) {
310 setRotationCallbacks(false);
311 }
312 mContext.getContentResolver().unregisterContentObserver(mFixedRotationObserver);
Sunny Goyala6a58122019-04-02 10:20:29 -0700313 updateIsEnabled();
Sunny Goyal38412952020-04-17 11:35:37 -0700314 stopTracking();
Sunny Goyala6a58122019-04-02 10:20:29 -0700315 }
316
Vinit Nayakf848e9d2020-03-24 16:04:09 -0700317 private void setRotationCallbacks(boolean enable) {
318 if (enable) {
Vinit Nayakf848e9d2020-03-24 16:04:09 -0700319 mOverviewProxyService.addCallback(mQuickSwitchListener);
320 } else {
Vinit Nayakf848e9d2020-03-24 16:04:09 -0700321 mOverviewProxyService.removeCallback(mQuickSwitchListener);
322 }
323 }
324
Sunny Goyal38412952020-04-17 11:35:37 -0700325 /**
326 * @see NavigationModeController.ModeChangedListener#onNavigationModeChanged
327 */
328 public void onNavigationModeChanged(int mode) {
Winson Chung1aa24b92019-04-24 15:17:33 -0700329 mIsGesturalModeEnabled = QuickStepContract.isGesturalMode(mode);
Sunny Goyala6a58122019-04-02 10:20:29 -0700330 updateIsEnabled();
Sunny Goyal38412952020-04-17 11:35:37 -0700331 updateCurrentUserResources();
Sunny Goyala6a58122019-04-02 10:20:29 -0700332 }
333
Jorim Jaggi956ca412019-01-07 14:49:14 +0100334 public void onNavBarTransientStateChanged(boolean isTransient) {
335 mIsNavBarShownTransiently = isTransient;
Winson Chung3d6c0802019-10-04 10:37:12 -0700336 }
337
Sunny Goyala6a58122019-04-02 10:20:29 -0700338 private void disposeInputChannel() {
339 if (mInputEventReceiver != null) {
340 mInputEventReceiver.dispose();
341 mInputEventReceiver = null;
342 }
343 if (mInputMonitor != null) {
344 mInputMonitor.dispose();
345 mInputMonitor = null;
346 }
347 }
348
349 private void updateIsEnabled() {
350 boolean isEnabled = mIsAttached && mIsGesturalModeEnabled;
351 if (isEnabled == mIsEnabled) {
352 return;
353 }
354 mIsEnabled = isEnabled;
355 disposeInputChannel();
356
Andy Wickham20297012019-11-06 14:56:26 -0800357 if (mEdgeBackPlugin != null) {
358 mEdgeBackPlugin.onDestroy();
359 mEdgeBackPlugin = null;
Sunny Goyala6a58122019-04-02 10:20:29 -0700360 }
361
362 if (!mIsEnabled) {
Mehdi Alizadehf2b28e92019-12-05 20:13:49 -0800363 mGestureNavigationSettingsObserver.unregister();
Sunny Goyal19049692019-04-08 11:56:07 -0700364 mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
Andy Wickham20297012019-11-06 14:56:26 -0800365 mPluginManager.removePluginListener(this);
Andy Wickham3b3b9db2020-02-18 19:02:11 +0000366 ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
Sunny Goyala6a58122019-04-02 10:20:29 -0700367
368 try {
369 WindowManagerGlobal.getWindowManagerService()
370 .unregisterSystemGestureExclusionListener(
371 mGestureExclusionListener, mDisplayId);
Winson Chung939f5bd2020-04-14 15:05:11 -0700372 } catch (RemoteException | IllegalArgumentException e) {
Sunny Goyala6a58122019-04-02 10:20:29 -0700373 Log.e(TAG, "Failed to unregister window manager callbacks", e);
374 }
375
376 } else {
Mehdi Alizadehf2b28e92019-12-05 20:13:49 -0800377 mGestureNavigationSettingsObserver.register();
Sunny Goyala6a58122019-04-02 10:20:29 -0700378 updateDisplaySize();
Sunny Goyal19049692019-04-08 11:56:07 -0700379 mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
380 mContext.getMainThreadHandler());
Andy Wickham3b3b9db2020-02-18 19:02:11 +0000381 ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
Sunny Goyala6a58122019-04-02 10:20:29 -0700382
383 try {
Sunny Goyala6a58122019-04-02 10:20:29 -0700384 WindowManagerGlobal.getWindowManagerService()
385 .registerSystemGestureExclusionListener(
386 mGestureExclusionListener, mDisplayId);
Winson Chung939f5bd2020-04-14 15:05:11 -0700387 } catch (RemoteException | IllegalArgumentException e) {
Sunny Goyala6a58122019-04-02 10:20:29 -0700388 Log.e(TAG, "Failed to register window manager callbacks", e);
389 }
390
391 // Register input event receiver
392 mInputMonitor = InputManager.getInstance().monitorGestureInput(
393 "edge-swipe", mDisplayId);
Siarhei Vishniakoufaa87992019-05-29 09:12:18 -0700394 mInputEventReceiver = new SysUiInputEventReceiver(
395 mInputMonitor.getInputChannel(), Looper.getMainLooper());
Sunny Goyala6a58122019-04-02 10:20:29 -0700396
397 // Add a nav bar panel window
Andy Wickham20297012019-11-06 14:56:26 -0800398 setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));
399 mPluginManager.addPluginListener(
400 this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
Sunny Goyala6a58122019-04-02 10:20:29 -0700401 }
402 }
403
Andy Wickham20297012019-11-06 14:56:26 -0800404 @Override
405 public void onPluginConnected(NavigationEdgeBackPlugin plugin, Context context) {
406 setEdgeBackPlugin(plugin);
407 }
408
409 @Override
410 public void onPluginDisconnected(NavigationEdgeBackPlugin plugin) {
411 setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));
412 }
413
414 private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {
415 if (mEdgeBackPlugin != null) {
416 mEdgeBackPlugin.onDestroy();
417 }
418 mEdgeBackPlugin = edgeBackPlugin;
419 mEdgeBackPlugin.setBackCallback(mBackCallback);
420 mEdgeBackPlugin.setLayoutParams(createLayoutParams());
421 updateDisplaySize();
422 }
423
Sunny Goyal38412952020-04-17 11:35:37 -0700424 public boolean isHandlingGestures() {
425 return mIsEnabled && mIsBackGestureAllowed;
426 }
427
Andy Wickham20297012019-11-06 14:56:26 -0800428 private WindowManager.LayoutParams createLayoutParams() {
429 Resources resources = mContext.getResources();
430 WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
431 resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_width),
432 resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_height),
433 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
434 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
435 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
436 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
437 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
438 PixelFormat.TRANSLUCENT);
439 layoutParams.privateFlags |=
440 WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
441 layoutParams.setTitle(TAG + mContext.getDisplayId());
442 layoutParams.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
443 layoutParams.windowAnimations = 0;
Tiger Huang52724442020-01-20 21:38:42 +0800444 layoutParams.setFitInsetsTypes(0 /* types */);
Andy Wickham20297012019-11-06 14:56:26 -0800445 return layoutParams;
446 }
447
Sunny Goyala6a58122019-04-02 10:20:29 -0700448 private void onInputEvent(InputEvent ev) {
449 if (ev instanceof MotionEvent) {
450 onMotionEvent((MotionEvent) ev);
451 }
452 }
453
454 private boolean isWithinTouchRegion(int x, int y) {
Winson Chung77b2d1d2019-12-20 15:18:42 -0800455 // Disallow if we are in the bottom gesture area
456 if (y >= (mDisplaySize.y - mBottomGestureHeight)) {
457 return false;
458 }
459
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700460 // If the point is way too far (twice the margin), it is
461 // not interesting to us for logging purposes, nor we
462 // should process it. Simply return false and keep
463 // mLogGesture = false.
464 if (x > 2 * (mEdgeWidthLeft + mLeftInset)
465 && x < (mDisplaySize.x - 2 * (mEdgeWidthRight + mRightInset))) {
466 return false;
Winson Chung3d6c0802019-10-04 10:37:12 -0700467 }
468
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700469 // Denotes whether we should proceed with the gesture.
470 // Even if it is false, we may want to log it assuming
471 // it is not invalid due to exclusion.
472 boolean withinRange = x <= mEdgeWidthLeft + mLeftInset
473 || x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset);
474
475 // Always allow if the user is in a transient sticky immersive state
476 if (mIsNavBarShownTransiently) {
477 mLogGesture = true;
478 return withinRange;
Winson Chung51787a12019-04-10 15:33:31 -0700479 }
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700480
481 if (mExcludeRegion.contains(x, y)) {
482 if (withinRange) {
483 // Log as exclusion only if it is in acceptable range in the first place.
484 mOverviewProxyService.notifyBackAction(
485 false /* completed */, -1, -1, false /* isButton */, !mIsOnLeftEdge);
486 // We don't have the end point for logging purposes.
487 mEndPoint.x = -1;
488 mEndPoint.y = -1;
489 mLogGesture = true;
490 logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_EXCLUDED);
491 }
492 return false;
493 }
494
495 mInRejectedExclusion = mUnrestrictedExcludeRegion.contains(x, y);
496 mLogGesture = true;
497 return withinRange;
Sunny Goyala6a58122019-04-02 10:20:29 -0700498 }
499
Sunny Goyal3c317f32019-05-21 11:49:11 -0700500 private void cancelGesture(MotionEvent ev) {
501 // Send action cancel to reset all the touch events
502 mAllowGesture = false;
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700503 mLogGesture = false;
Hyunyoung Song36d7a2f2019-08-05 17:19:54 -0700504 mInRejectedExclusion = false;
Sunny Goyal3c317f32019-05-21 11:49:11 -0700505 MotionEvent cancelEv = MotionEvent.obtain(ev);
506 cancelEv.setAction(MotionEvent.ACTION_CANCEL);
Andy Wickham20297012019-11-06 14:56:26 -0800507 mEdgeBackPlugin.onMotionEvent(cancelEv);
Sunny Goyal3c317f32019-05-21 11:49:11 -0700508 cancelEv.recycle();
509 }
510
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700511 private void logGesture(int backType) {
512 if (!mLogGesture) {
513 return;
514 }
515 mLogGesture = false;
516 SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, backType,
517 (int) mDownPoint.y, mIsOnLeftEdge
518 ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT
519 : SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT,
520 (int) mDownPoint.x, (int) mDownPoint.y,
521 (int) mEndPoint.x, (int) mEndPoint.y,
522 mEdgeWidthLeft + mLeftInset,
523 mDisplaySize.x - (mEdgeWidthRight + mRightInset));
524 }
525
Sunny Goyala6a58122019-04-02 10:20:29 -0700526 private void onMotionEvent(MotionEvent ev) {
Sunny Goyal3c317f32019-05-21 11:49:11 -0700527 int action = ev.getActionMasked();
528 if (action == MotionEvent.ACTION_DOWN) {
Winson Chunge2a686d2019-04-17 16:54:23 -0700529 // Verify if this is in within the touch region and we aren't in immersive mode, and
530 // either the bouncer is showing or the notification panel is hidden
Mehdi Alizadehf2b28e92019-12-05 20:13:49 -0800531 mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset;
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700532 mLogGesture = false;
Hyunyoung Song36d7a2f2019-08-05 17:19:54 -0700533 mInRejectedExclusion = false;
Sunny Goyal38412952020-04-17 11:35:37 -0700534 mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed
Andy Wickham3b3b9db2020-02-18 19:02:11 +0000535 && !mGestureBlockingActivityRunning
Sunny Goyal38412952020-04-17 11:35:37 -0700536 && !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
537 && isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
Winson Chunge2a686d2019-04-17 16:54:23 -0700538 if (mAllowGesture) {
Andy Wickham20297012019-11-06 14:56:26 -0800539 mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
540 mEdgeBackPlugin.onMotionEvent(ev);
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700541 }
542 if (mLogGesture) {
Sunny Goyala6a58122019-04-02 10:20:29 -0700543 mDownPoint.set(ev.getX(), ev.getY());
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700544 mEndPoint.set(-1, -1);
Sunny Goyala6a58122019-04-02 10:20:29 -0700545 mThresholdCrossed = false;
Sunny Goyala6a58122019-04-02 10:20:29 -0700546 }
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700547 } else if (mAllowGesture || mLogGesture) {
Sunny Goyal3c317f32019-05-21 11:49:11 -0700548 if (!mThresholdCrossed) {
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700549 mEndPoint.x = (int) ev.getX();
550 mEndPoint.y = (int) ev.getY();
Sunny Goyal3c317f32019-05-21 11:49:11 -0700551 if (action == MotionEvent.ACTION_POINTER_DOWN) {
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700552 if (mAllowGesture) {
553 logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_MULTI_TOUCH);
554 // We do not support multi touch for back gesture
555 cancelGesture(ev);
556 }
557 mLogGesture = false;
Sunny Goyala6a58122019-04-02 10:20:29 -0700558 return;
Sunny Goyal3c317f32019-05-21 11:49:11 -0700559 } else if (action == MotionEvent.ACTION_MOVE) {
560 if ((ev.getEventTime() - ev.getDownTime()) > mLongPressTimeout) {
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700561 if (mAllowGesture) {
562 logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_LONG_PRESS);
563 cancelGesture(ev);
564 }
565 mLogGesture = false;
Sunny Goyal3c317f32019-05-21 11:49:11 -0700566 return;
567 }
568 float dx = Math.abs(ev.getX() - mDownPoint.x);
569 float dy = Math.abs(ev.getY() - mDownPoint.y);
570 if (dy > dx && dy > mTouchSlop) {
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700571 if (mAllowGesture) {
572 logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_VERTICAL_MOVE);
573 cancelGesture(ev);
574 }
575 mLogGesture = false;
Sunny Goyal3c317f32019-05-21 11:49:11 -0700576 return;
Sunny Goyal3c317f32019-05-21 11:49:11 -0700577 } else if (dx > dy && dx > mTouchSlop) {
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700578 if (mAllowGesture) {
579 mThresholdCrossed = true;
580 // Capture inputs
581 mInputMonitor.pilferPointers();
582 } else {
583 logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_FAR_FROM_EDGE);
584 }
Sunny Goyal3c317f32019-05-21 11:49:11 -0700585 }
Sunny Goyala6a58122019-04-02 10:20:29 -0700586 }
587 }
588
Maryam Karimzadehgan4f9ab822020-03-09 14:51:07 -0700589 if (mAllowGesture) {
590 // forward touch
591 mEdgeBackPlugin.onMotionEvent(ev);
592 }
Sunny Goyala6a58122019-04-02 10:20:29 -0700593 }
Winson Chunga657ac92019-12-09 16:43:46 -0800594
595 Dependency.get(ProtoTracer.class).update();
Sunny Goyala6a58122019-04-02 10:20:29 -0700596 }
597
Vinit Nayakf848e9d2020-03-24 16:04:09 -0700598 private void updateDisabledForQuickstep() {
599 int rotation = mContext.getResources().getConfiguration().windowConfiguration.getRotation();
Vinit Nayaka2b672c2020-04-27 18:35:14 -0700600 mDisabledForQuickstep = mStartingQuickstepRotation > -1 &&
601 mStartingQuickstepRotation != rotation;
Vinit Nayakf848e9d2020-03-24 16:04:09 -0700602 }
603
Sunny Goyala6a58122019-04-02 10:20:29 -0700604 @Override
605 public void onDisplayAdded(int displayId) { }
606
607 @Override
608 public void onDisplayRemoved(int displayId) { }
609
610 @Override
611 public void onDisplayChanged(int displayId) {
Vinit Nayakf848e9d2020-03-24 16:04:09 -0700612 if (mStartingQuickstepRotation > -1) {
613 updateDisabledForQuickstep();
614 }
615
Sunny Goyala6a58122019-04-02 10:20:29 -0700616 if (displayId == mDisplayId) {
617 updateDisplaySize();
618 }
619 }
620
621 private void updateDisplaySize() {
Riddle Hsu410d9752020-02-06 17:29:04 +0800622 mContext.getDisplay().getRealSize(mDisplaySize);
Andy Wickham20297012019-11-06 14:56:26 -0800623 if (mEdgeBackPlugin != null) {
624 mEdgeBackPlugin.setDisplaySize(mDisplaySize);
625 }
Sunny Goyala6a58122019-04-02 10:20:29 -0700626 }
627
628 private void sendEvent(int action, int code) {
629 long when = SystemClock.uptimeMillis();
630 final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
631 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
632 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
633 InputDevice.SOURCE_KEYBOARD);
Mady Mellor390bff42019-04-05 15:09:01 -0700634
635 // Bubble controller will give us a valid display id if it should get the back event
636 BubbleController bubbleController = Dependency.get(BubbleController.class);
637 int bubbleDisplayId = bubbleController.getExpandedDisplayId(mContext);
638 if (code == KeyEvent.KEYCODE_BACK && bubbleDisplayId != INVALID_DISPLAY) {
639 ev.setDisplayId(bubbleDisplayId);
640 }
Sunny Goyala6a58122019-04-02 10:20:29 -0700641 InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
642 }
Siarhei Vishniakoufaa87992019-05-29 09:12:18 -0700643
Vinit Nayakf848e9d2020-03-24 16:04:09 -0700644 private void updatedFixedRotation() {
645 boolean oldFlag = mFixedRotationFlagEnabled;
646 mFixedRotationFlagEnabled = Settings.Global.getInt(mContext.getContentResolver(),
647 FIXED_ROTATION_TRANSFORM_SETTING_NAME, 0) != 0;
648 if (oldFlag == mFixedRotationFlagEnabled) {
649 return;
650 }
651
652 setRotationCallbacks(mFixedRotationFlagEnabled);
653 }
654
Selim Cineke50b1212019-06-04 18:40:31 -0700655 public void setInsets(int leftInset, int rightInset) {
656 mLeftInset = leftInset;
657 mRightInset = rightInset;
Andy Wickham20297012019-11-06 14:56:26 -0800658 if (mEdgeBackPlugin != null) {
659 mEdgeBackPlugin.setInsets(leftInset, rightInset);
660 }
Selim Cineke50b1212019-06-04 18:40:31 -0700661 }
662
Winson Chung316f6732019-06-14 09:31:46 -0700663 public void dump(PrintWriter pw) {
664 pw.println("EdgeBackGestureHandler:");
665 pw.println(" mIsEnabled=" + mIsEnabled);
Sunny Goyal38412952020-04-17 11:35:37 -0700666 pw.println(" mIsBackGestureAllowed=" + mIsBackGestureAllowed);
Winson Chung316f6732019-06-14 09:31:46 -0700667 pw.println(" mAllowGesture=" + mAllowGesture);
Vinit Nayakf848e9d2020-03-24 16:04:09 -0700668 pw.println(" mDisabledForQuickstep=" + mDisabledForQuickstep);
Vinit Nayak8da81752020-05-27 22:22:14 -0700669 pw.println(" mStartingQuickstepRotation=" + mStartingQuickstepRotation);
Hyunyoung Song36d7a2f2019-08-05 17:19:54 -0700670 pw.println(" mInRejectedExclusion" + mInRejectedExclusion);
Winson Chung316f6732019-06-14 09:31:46 -0700671 pw.println(" mExcludeRegion=" + mExcludeRegion);
Hyunyoung Song36d7a2f2019-08-05 17:19:54 -0700672 pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion);
Winson Chung316f6732019-06-14 09:31:46 -0700673 pw.println(" mIsAttached=" + mIsAttached);
Mehdi Alizadehf2b28e92019-12-05 20:13:49 -0800674 pw.println(" mEdgeWidthLeft=" + mEdgeWidthLeft);
675 pw.println(" mEdgeWidthRight=" + mEdgeWidthRight);
Winson Chung316f6732019-06-14 09:31:46 -0700676 }
677
Andy Wickham3b3b9db2020-02-18 19:02:11 +0000678 private boolean isGestureBlockingActivityRunning() {
679 ActivityManager.RunningTaskInfo runningTask =
680 ActivityManagerWrapper.getInstance().getRunningTask();
681 ComponentName topActivity = runningTask == null ? null : runningTask.topActivity;
682 return topActivity != null && mGestureBlockingActivities.contains(topActivity);
683 }
684
Winson Chunga657ac92019-12-09 16:43:46 -0800685 @Override
686 public void writeToProto(SystemUiTraceProto proto) {
687 if (proto.edgeBackGestureHandler == null) {
688 proto.edgeBackGestureHandler = new EdgeBackGestureHandlerProto();
689 }
690 proto.edgeBackGestureHandler.allowGesture = mAllowGesture;
691 }
692
Siarhei Vishniakoufaa87992019-05-29 09:12:18 -0700693 class SysUiInputEventReceiver extends InputEventReceiver {
694 SysUiInputEventReceiver(InputChannel channel, Looper looper) {
695 super(channel, looper);
696 }
697
698 public void onInputEvent(InputEvent event) {
699 EdgeBackGestureHandler.this.onInputEvent(event);
700 finishInputEvent(event, true);
701 }
702 }
Sunny Goyala6a58122019-04-02 10:20:29 -0700703}