1 /* 2 * Copyright (C) 2020 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 package com.android.systemui.navigationbar.gestural; 17 18 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION; 19 20 import static com.android.systemui.classifier.Classifier.BACK_GESTURE; 21 22 import android.annotation.NonNull; 23 import android.app.ActivityManager; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.pm.PackageManager; 27 import android.content.pm.PackageManager.NameNotFoundException; 28 import android.content.res.Configuration; 29 import android.content.res.Resources; 30 import android.graphics.PixelFormat; 31 import android.graphics.Point; 32 import android.graphics.PointF; 33 import android.graphics.Rect; 34 import android.graphics.Region; 35 import android.hardware.input.InputManager; 36 import android.os.Looper; 37 import android.os.RemoteException; 38 import android.os.SystemClock; 39 import android.os.SystemProperties; 40 import android.os.Trace; 41 import android.provider.DeviceConfig; 42 import android.util.DisplayMetrics; 43 import android.util.Log; 44 import android.util.TypedValue; 45 import android.view.Choreographer; 46 import android.view.ISystemGestureExclusionListener; 47 import android.view.IWindowManager; 48 import android.view.InputDevice; 49 import android.view.InputEvent; 50 import android.view.InputMonitor; 51 import android.view.KeyCharacterMap; 52 import android.view.KeyEvent; 53 import android.view.MotionEvent; 54 import android.view.Surface; 55 import android.view.ViewConfiguration; 56 import android.view.WindowManager; 57 import android.window.BackEvent; 58 59 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; 60 import com.android.internal.policy.GestureNavigationSettingsObserver; 61 import com.android.systemui.R; 62 import com.android.systemui.dagger.qualifiers.Background; 63 import com.android.systemui.dagger.qualifiers.Main; 64 import com.android.systemui.flags.FeatureFlags; 65 import com.android.systemui.flags.Flags; 66 import com.android.systemui.model.SysUiState; 67 import com.android.systemui.navigationbar.NavigationBarView; 68 import com.android.systemui.navigationbar.NavigationModeController; 69 import com.android.systemui.plugins.FalsingManager; 70 import com.android.systemui.plugins.NavigationEdgeBackPlugin; 71 import com.android.systemui.plugins.PluginListener; 72 import com.android.systemui.plugins.PluginManager; 73 import com.android.systemui.recents.OverviewProxyService; 74 import com.android.systemui.settings.UserTracker; 75 import com.android.systemui.shared.system.ActivityManagerWrapper; 76 import com.android.systemui.shared.system.InputChannelCompat; 77 import com.android.systemui.shared.system.QuickStepContract; 78 import com.android.systemui.shared.system.SysUiStatsLog; 79 import com.android.systemui.shared.system.TaskStackChangeListener; 80 import com.android.systemui.shared.system.TaskStackChangeListeners; 81 import com.android.systemui.shared.tracing.ProtoTraceable; 82 import com.android.systemui.tracing.ProtoTracer; 83 import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto; 84 import com.android.systemui.tracing.nano.SystemUiTraceProto; 85 import com.android.systemui.util.Assert; 86 import com.android.wm.shell.back.BackAnimation; 87 import com.android.wm.shell.pip.Pip; 88 89 import java.io.PrintWriter; 90 import java.util.ArrayDeque; 91 import java.util.ArrayList; 92 import java.util.List; 93 import java.util.Map; 94 import java.util.Optional; 95 import java.util.concurrent.Executor; 96 import java.util.function.Consumer; 97 98 import javax.inject.Inject; 99 import javax.inject.Provider; 100 101 /** 102 * Utility class to handle edge swipes for back gesture 103 */ 104 public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBackPlugin>, 105 ProtoTraceable<SystemUiTraceProto> { 106 107 private static final String TAG = "EdgeBackGestureHandler"; 108 private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt( 109 "gestures.back_timeout", 250); 110 111 private static final int MAX_NUM_LOGGED_PREDICTIONS = 10; 112 private static final int MAX_NUM_LOGGED_GESTURES = 10; 113 114 static final boolean DEBUG_MISSING_GESTURE = false; 115 public static final String DEBUG_MISSING_GESTURE_TAG = "NoBackGesture"; 116 117 private ISystemGestureExclusionListener mGestureExclusionListener = 118 new ISystemGestureExclusionListener.Stub() { 119 @Override 120 public void onSystemGestureExclusionChanged(int displayId, 121 Region systemGestureExclusion, Region unrestrictedOrNull) { 122 if (displayId == mDisplayId) { 123 mMainExecutor.execute(() -> { 124 mExcludeRegion.set(systemGestureExclusion); 125 mUnrestrictedExcludeRegion.set(unrestrictedOrNull != null 126 ? unrestrictedOrNull : systemGestureExclusion); 127 }); 128 } 129 } 130 }; 131 132 private OverviewProxyService.OverviewProxyListener mQuickSwitchListener = 133 new OverviewProxyService.OverviewProxyListener() { 134 @Override 135 public void onPrioritizedRotation(@Surface.Rotation int rotation) { 136 mStartingQuickstepRotation = rotation; 137 updateDisabledForQuickstep(mLastReportedConfig); 138 } 139 }; 140 141 private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { 142 @Override 143 public void onTaskStackChanged() { 144 mGestureBlockingActivityRunning = isGestureBlockingActivityRunning(); 145 } 146 @Override 147 public void onTaskCreated(int taskId, ComponentName componentName) { 148 if (componentName != null) { 149 mPackageName = componentName.getPackageName(); 150 } else { 151 mPackageName = "_UNKNOWN"; 152 } 153 } 154 }; 155 156 private DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener = 157 new DeviceConfig.OnPropertiesChangedListener() { 158 @Override 159 public void onPropertiesChanged(DeviceConfig.Properties properties) { 160 if (DeviceConfig.NAMESPACE_SYSTEMUI.equals(properties.getNamespace()) 161 && (properties.getKeyset().contains( 162 SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD) 163 || properties.getKeyset().contains( 164 SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL) 165 || properties.getKeyset().contains( 166 SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_NAME))) { 167 updateMLModelState(); 168 } 169 } 170 }; 171 172 173 private final Context mContext; 174 private final UserTracker mUserTracker; 175 private final OverviewProxyService mOverviewProxyService; 176 private final SysUiState mSysUiState; 177 private Runnable mStateChangeCallback; 178 179 private final PluginManager mPluginManager; 180 private final ProtoTracer mProtoTracer; 181 private final NavigationModeController mNavigationModeController; 182 private final BackPanelController.Factory mBackPanelControllerFactory; 183 private final ViewConfiguration mViewConfiguration; 184 private final WindowManager mWindowManager; 185 private final IWindowManager mWindowManagerService; 186 private final Optional<Pip> mPipOptional; 187 private final FalsingManager mFalsingManager; 188 private final Configuration mLastReportedConfig = new Configuration(); 189 // Activities which should not trigger Back gesture. 190 private final List<ComponentName> mGestureBlockingActivities = new ArrayList<>(); 191 192 private final Point mDisplaySize = new Point(); 193 private final int mDisplayId; 194 195 private final Executor mMainExecutor; 196 private final Executor mBackgroundExecutor; 197 198 private final Rect mPipExcludedBounds = new Rect(); 199 private final Rect mNavBarOverlayExcludedBounds = new Rect(); 200 private final Region mExcludeRegion = new Region(); 201 private final Region mUnrestrictedExcludeRegion = new Region(); 202 private final Provider<NavigationBarEdgePanel> mNavBarEdgePanelProvider; 203 private final Provider<BackGestureTfClassifierProvider> 204 mBackGestureTfClassifierProviderProvider; 205 private final FeatureFlags mFeatureFlags; 206 207 // The left side edge width where touch down is allowed 208 private int mEdgeWidthLeft; 209 // The right side edge width where touch down is allowed 210 private int mEdgeWidthRight; 211 // The bottom gesture area height 212 private float mBottomGestureHeight; 213 // The slop to distinguish between horizontal and vertical motion 214 private float mTouchSlop; 215 // The threshold for triggering back 216 private float mBackSwipeTriggerThreshold; 217 // The threshold for back swipe full progress. 218 private float mBackSwipeProgressThreshold; 219 // Duration after which we consider the event as longpress. 220 private final int mLongPressTimeout; 221 private int mStartingQuickstepRotation = -1; 222 // We temporarily disable back gesture when user is quickswitching 223 // between apps of different orientations 224 private boolean mDisabledForQuickstep; 225 // This gets updated when the value of PipTransitionState#isInPip changes. 226 private boolean mIsInPip; 227 228 private final PointF mDownPoint = new PointF(); 229 private final PointF mEndPoint = new PointF(); 230 private boolean mThresholdCrossed = false; 231 private boolean mAllowGesture = false; 232 private boolean mLogGesture = false; 233 private boolean mInRejectedExclusion = false; 234 private boolean mIsOnLeftEdge; 235 236 private boolean mIsAttached; 237 private boolean mIsGesturalModeEnabled; 238 private boolean mIsEnabled; 239 private boolean mIsNavBarShownTransiently; 240 private boolean mIsBackGestureAllowed; 241 private boolean mGestureBlockingActivityRunning; 242 private boolean mIsNewBackAffordanceEnabled; 243 244 private InputMonitor mInputMonitor; 245 private InputChannelCompat.InputEventReceiver mInputEventReceiver; 246 247 private NavigationEdgeBackPlugin mEdgeBackPlugin; 248 private BackAnimation mBackAnimation; 249 private int mLeftInset; 250 private int mRightInset; 251 private int mSysUiFlags; 252 253 // For Tf-Lite model. 254 private BackGestureTfClassifierProvider mBackGestureTfClassifierProvider; 255 private Map<String, Integer> mVocab; 256 private boolean mUseMLModel; 257 private boolean mMLModelIsLoading; 258 // minimum width below which we do not run the model 259 private int mMLEnableWidth; 260 private float mMLModelThreshold; 261 private String mPackageName; 262 private float mMLResults; 263 264 // For debugging 265 private LogArray mPredictionLog = new LogArray(MAX_NUM_LOGGED_PREDICTIONS); 266 private LogArray mGestureLogInsideInsets = new LogArray(MAX_NUM_LOGGED_GESTURES); 267 private LogArray mGestureLogOutsideInsets = new LogArray(MAX_NUM_LOGGED_GESTURES); 268 269 private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver; 270 271 private final NavigationEdgeBackPlugin.BackCallback mBackCallback = 272 new NavigationEdgeBackPlugin.BackCallback() { 273 @Override 274 public void triggerBack() { 275 // Notify FalsingManager that an intentional gesture has occurred. 276 mFalsingManager.isFalseTouch(BACK_GESTURE); 277 // Only inject back keycodes when ahead-of-time back dispatching is disabled. 278 if (mBackAnimation == null) { 279 boolean sendDown = sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK); 280 boolean sendUp = sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK); 281 if (DEBUG_MISSING_GESTURE) { 282 Log.d(DEBUG_MISSING_GESTURE_TAG, "Triggered back: down=" 283 + sendDown + ", up=" + sendUp); 284 } 285 } else { 286 mBackAnimation.setTriggerBack(true); 287 } 288 289 logGesture(mInRejectedExclusion 290 ? SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED_REJECTED 291 : SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED); 292 } 293 294 @Override 295 public void cancelBack() { 296 if (mBackAnimation != null) { 297 mBackAnimation.setTriggerBack(false); 298 } 299 logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE); 300 } 301 302 @Override 303 public void setTriggerBack(boolean triggerBack) { 304 if (mBackAnimation != null) { 305 mBackAnimation.setTriggerBack(triggerBack); 306 } 307 } 308 }; 309 310 private final SysUiState.SysUiStateCallback mSysUiStateCallback = 311 new SysUiState.SysUiStateCallback() { 312 @Override 313 public void onSystemUiStateChanged(int sysUiFlags) { 314 mSysUiFlags = sysUiFlags; 315 } 316 }; 317 318 private final Consumer<Boolean> mOnIsInPipStateChangedListener = 319 (isInPip) -> mIsInPip = isInPip; 320 321 private final UserTracker.Callback mUserChangedCallback = 322 new UserTracker.Callback() { 323 @Override 324 public void onUserChanged(int newUser, @NonNull Context userContext) { 325 updateIsEnabled(); 326 updateCurrentUserResources(); 327 } 328 }; 329 EdgeBackGestureHandler( Context context, OverviewProxyService overviewProxyService, SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor, @Background Executor backgroundExecutor, UserTracker userTracker, ProtoTracer protoTracer, NavigationModeController navigationModeController, BackPanelController.Factory backPanelControllerFactory, ViewConfiguration viewConfiguration, WindowManager windowManager, IWindowManager windowManagerService, Optional<Pip> pipOptional, FalsingManager falsingManager, Provider<NavigationBarEdgePanel> navigationBarEdgePanelProvider, Provider<BackGestureTfClassifierProvider> backGestureTfClassifierProviderProvider, FeatureFlags featureFlags)330 EdgeBackGestureHandler( 331 Context context, 332 OverviewProxyService overviewProxyService, 333 SysUiState sysUiState, 334 PluginManager pluginManager, 335 @Main Executor executor, 336 @Background Executor backgroundExecutor, 337 UserTracker userTracker, 338 ProtoTracer protoTracer, 339 NavigationModeController navigationModeController, 340 BackPanelController.Factory backPanelControllerFactory, 341 ViewConfiguration viewConfiguration, 342 WindowManager windowManager, 343 IWindowManager windowManagerService, 344 Optional<Pip> pipOptional, 345 FalsingManager falsingManager, 346 Provider<NavigationBarEdgePanel> navigationBarEdgePanelProvider, 347 Provider<BackGestureTfClassifierProvider> backGestureTfClassifierProviderProvider, 348 FeatureFlags featureFlags) { 349 mContext = context; 350 mDisplayId = context.getDisplayId(); 351 mMainExecutor = executor; 352 mBackgroundExecutor = backgroundExecutor; 353 mUserTracker = userTracker; 354 mOverviewProxyService = overviewProxyService; 355 mSysUiState = sysUiState; 356 mPluginManager = pluginManager; 357 mProtoTracer = protoTracer; 358 mNavigationModeController = navigationModeController; 359 mBackPanelControllerFactory = backPanelControllerFactory; 360 mViewConfiguration = viewConfiguration; 361 mWindowManager = windowManager; 362 mWindowManagerService = windowManagerService; 363 mPipOptional = pipOptional; 364 mFalsingManager = falsingManager; 365 mNavBarEdgePanelProvider = navigationBarEdgePanelProvider; 366 mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider; 367 mFeatureFlags = featureFlags; 368 mLastReportedConfig.setTo(mContext.getResources().getConfiguration()); 369 ComponentName recentsComponentName = ComponentName.unflattenFromString( 370 context.getString(com.android.internal.R.string.config_recentsComponentName)); 371 if (recentsComponentName != null) { 372 String recentsPackageName = recentsComponentName.getPackageName(); 373 PackageManager manager = context.getPackageManager(); 374 try { 375 Resources resources = manager.getResourcesForApplication( 376 manager.getApplicationInfo(recentsPackageName, 377 PackageManager.MATCH_UNINSTALLED_PACKAGES 378 | PackageManager.MATCH_DISABLED_COMPONENTS 379 | PackageManager.GET_SHARED_LIBRARY_FILES)); 380 int resId = resources.getIdentifier( 381 "gesture_blocking_activities", "array", recentsPackageName); 382 383 if (resId == 0) { 384 Log.e(TAG, "No resource found for gesture-blocking activities"); 385 } else { 386 String[] gestureBlockingActivities = resources.getStringArray(resId); 387 for (String gestureBlockingActivity : gestureBlockingActivities) { 388 mGestureBlockingActivities.add( 389 ComponentName.unflattenFromString(gestureBlockingActivity)); 390 } 391 } 392 } catch (NameNotFoundException e) { 393 Log.e(TAG, "Failed to add gesture blocking activities", e); 394 } 395 } 396 mLongPressTimeout = Math.min(MAX_LONG_PRESS_TIMEOUT, 397 ViewConfiguration.getLongPressTimeout()); 398 399 mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver( 400 mContext.getMainThreadHandler(), mContext, this::onNavigationSettingsChanged); 401 402 updateCurrentUserResources(); 403 } 404 setStateChangeCallback(Runnable callback)405 public void setStateChangeCallback(Runnable callback) { 406 mStateChangeCallback = callback; 407 } 408 updateCurrentUserResources()409 public void updateCurrentUserResources() { 410 Resources res = mNavigationModeController.getCurrentUserContext().getResources(); 411 mEdgeWidthLeft = mGestureNavigationSettingsObserver.getLeftSensitivity(res); 412 mEdgeWidthRight = mGestureNavigationSettingsObserver.getRightSensitivity(res); 413 mIsBackGestureAllowed = 414 !mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible(); 415 416 final DisplayMetrics dm = res.getDisplayMetrics(); 417 final float defaultGestureHeight = res.getDimension( 418 com.android.internal.R.dimen.navigation_bar_gesture_height) / dm.density; 419 final float gestureHeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI, 420 SystemUiDeviceConfigFlags.BACK_GESTURE_BOTTOM_HEIGHT, 421 defaultGestureHeight); 422 mBottomGestureHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, gestureHeight, 423 dm); 424 425 // Set the minimum bounds to activate ML to 12dp or the minimum of configured values 426 mMLEnableWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12.0f, dm); 427 if (mMLEnableWidth > mEdgeWidthRight) mMLEnableWidth = mEdgeWidthRight; 428 if (mMLEnableWidth > mEdgeWidthLeft) mMLEnableWidth = mEdgeWidthLeft; 429 430 // Reduce the default touch slop to ensure that we can intercept the gesture 431 // before the app starts to react to it. 432 // TODO(b/130352502) Tune this value and extract into a constant 433 final float backGestureSlop = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI, 434 SystemUiDeviceConfigFlags.BACK_GESTURE_SLOP_MULTIPLIER, 0.75f); 435 mTouchSlop = mViewConfiguration.getScaledTouchSlop() * backGestureSlop; 436 mBackSwipeTriggerThreshold = res.getDimension( 437 R.dimen.navigation_edge_action_drag_threshold); 438 mBackSwipeProgressThreshold = res.getDimension( 439 R.dimen.navigation_edge_action_progress_threshold); 440 updateBackAnimationThresholds(); 441 } 442 updateNavigationBarOverlayExcludeRegion(Rect exclude)443 public void updateNavigationBarOverlayExcludeRegion(Rect exclude) { 444 mNavBarOverlayExcludedBounds.set(exclude); 445 } 446 onNavigationSettingsChanged()447 private void onNavigationSettingsChanged() { 448 boolean wasBackAllowed = isHandlingGestures(); 449 updateCurrentUserResources(); 450 if (mStateChangeCallback != null && wasBackAllowed != isHandlingGestures()) { 451 mStateChangeCallback.run(); 452 } 453 } 454 455 /** 456 * Called when the nav/task bar is attached. 457 */ onNavBarAttached()458 public void onNavBarAttached() { 459 mIsAttached = true; 460 mProtoTracer.add(this); 461 mOverviewProxyService.addCallback(mQuickSwitchListener); 462 mSysUiState.addCallback(mSysUiStateCallback); 463 updateIsEnabled(); 464 mUserTracker.addCallback(mUserChangedCallback, mMainExecutor); 465 } 466 467 /** 468 * Called when the nav/task bar is detached. 469 */ onNavBarDetached()470 public void onNavBarDetached() { 471 mIsAttached = false; 472 mProtoTracer.remove(this); 473 mOverviewProxyService.removeCallback(mQuickSwitchListener); 474 mSysUiState.removeCallback(mSysUiStateCallback); 475 updateIsEnabled(); 476 mUserTracker.removeCallback(mUserChangedCallback); 477 } 478 479 /** 480 * @see NavigationModeController.ModeChangedListener#onNavigationModeChanged 481 */ onNavigationModeChanged(int mode)482 public void onNavigationModeChanged(int mode) { 483 mIsGesturalModeEnabled = QuickStepContract.isGesturalMode(mode); 484 updateIsEnabled(); 485 updateCurrentUserResources(); 486 } 487 onNavBarTransientStateChanged(boolean isTransient)488 public void onNavBarTransientStateChanged(boolean isTransient) { 489 mIsNavBarShownTransiently = isTransient; 490 } 491 disposeInputChannel()492 private void disposeInputChannel() { 493 if (mInputEventReceiver != null) { 494 mInputEventReceiver.dispose(); 495 mInputEventReceiver = null; 496 } 497 if (mInputMonitor != null) { 498 mInputMonitor.dispose(); 499 mInputMonitor = null; 500 } 501 } 502 updateIsEnabled()503 private void updateIsEnabled() { 504 try { 505 Trace.beginSection("EdgeBackGestureHandler#updateIsEnabled"); 506 updateIsEnabledTraced(); 507 } finally { 508 Trace.endSection(); 509 } 510 } 511 updateIsEnabledTraced()512 private void updateIsEnabledTraced() { 513 boolean isEnabled = mIsAttached && mIsGesturalModeEnabled; 514 if (isEnabled == mIsEnabled) { 515 return; 516 } 517 mIsEnabled = isEnabled; 518 disposeInputChannel(); 519 520 if (mEdgeBackPlugin != null) { 521 mEdgeBackPlugin.onDestroy(); 522 mEdgeBackPlugin = null; 523 } 524 525 if (!mIsEnabled) { 526 mGestureNavigationSettingsObserver.unregister(); 527 if (DEBUG_MISSING_GESTURE) { 528 Log.d(DEBUG_MISSING_GESTURE_TAG, "Unregister display listener"); 529 } 530 mPluginManager.removePluginListener(this); 531 TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener); 532 DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener); 533 mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener(null)); 534 535 try { 536 mWindowManagerService.unregisterSystemGestureExclusionListener( 537 mGestureExclusionListener, mDisplayId); 538 } catch (RemoteException | IllegalArgumentException e) { 539 Log.e(TAG, "Failed to unregister window manager callbacks", e); 540 } 541 542 } else { 543 mGestureNavigationSettingsObserver.register(); 544 updateDisplaySize(); 545 if (DEBUG_MISSING_GESTURE) { 546 Log.d(DEBUG_MISSING_GESTURE_TAG, "Register display listener"); 547 } 548 TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener); 549 DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, 550 mMainExecutor::execute, mOnPropertiesChangedListener); 551 mPipOptional.ifPresent( 552 pip -> pip.setOnIsInPipStateChangedListener(mOnIsInPipStateChangedListener)); 553 554 try { 555 mWindowManagerService.registerSystemGestureExclusionListener( 556 mGestureExclusionListener, mDisplayId); 557 } catch (RemoteException | IllegalArgumentException e) { 558 Log.e(TAG, "Failed to register window manager callbacks", e); 559 } 560 561 // Register input event receiver 562 mInputMonitor = InputManager.getInstance().monitorGestureInput( 563 "edge-swipe", mDisplayId); 564 mInputEventReceiver = new InputChannelCompat.InputEventReceiver( 565 mInputMonitor.getInputChannel(), Looper.getMainLooper(), 566 Choreographer.getInstance(), this::onInputEvent); 567 568 // Add a nav bar panel window 569 mIsNewBackAffordanceEnabled = mFeatureFlags.isEnabled(Flags.NEW_BACK_AFFORDANCE); 570 resetEdgeBackPlugin(); 571 mPluginManager.addPluginListener( 572 this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false); 573 } 574 // Update the ML model resources. 575 updateMLModelState(); 576 } 577 578 @Override onPluginConnected(NavigationEdgeBackPlugin plugin, Context context)579 public void onPluginConnected(NavigationEdgeBackPlugin plugin, Context context) { 580 setEdgeBackPlugin(plugin); 581 } 582 583 @Override onPluginDisconnected(NavigationEdgeBackPlugin plugin)584 public void onPluginDisconnected(NavigationEdgeBackPlugin plugin) { 585 resetEdgeBackPlugin(); 586 } 587 resetEdgeBackPlugin()588 private void resetEdgeBackPlugin() { 589 if (mIsNewBackAffordanceEnabled) { 590 setEdgeBackPlugin( 591 mBackPanelControllerFactory.create(mContext)); 592 } else { 593 setEdgeBackPlugin(mNavBarEdgePanelProvider.get()); 594 } 595 } 596 setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin)597 private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) { 598 try { 599 Trace.beginSection("setEdgeBackPlugin"); 600 if (mEdgeBackPlugin != null) { 601 mEdgeBackPlugin.onDestroy(); 602 } 603 mEdgeBackPlugin = edgeBackPlugin; 604 mEdgeBackPlugin.setBackCallback(mBackCallback); 605 mEdgeBackPlugin.setLayoutParams(createLayoutParams()); 606 updateDisplaySize(); 607 } finally { 608 Trace.endSection(); 609 } 610 } 611 isHandlingGestures()612 public boolean isHandlingGestures() { 613 return mIsEnabled && mIsBackGestureAllowed; 614 } 615 616 /** 617 * Update the PiP bounds, used for exclusion calculation. 618 */ setPipStashExclusionBounds(Rect bounds)619 public void setPipStashExclusionBounds(Rect bounds) { 620 mPipExcludedBounds.set(bounds); 621 } 622 createLayoutParams()623 private WindowManager.LayoutParams createLayoutParams() { 624 Resources resources = mContext.getResources(); 625 WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams( 626 resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_width), 627 resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_height), 628 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, 629 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 630 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 631 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, 632 PixelFormat.TRANSLUCENT); 633 layoutParams.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel); 634 layoutParams.windowAnimations = 0; 635 layoutParams.privateFlags |= 636 (WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS 637 | PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION); 638 layoutParams.setTitle(TAG + mContext.getDisplayId()); 639 layoutParams.setFitInsetsTypes(0 /* types */); 640 layoutParams.setTrustedOverlay(); 641 return layoutParams; 642 } 643 onInputEvent(InputEvent ev)644 private void onInputEvent(InputEvent ev) { 645 if (!(ev instanceof MotionEvent)) return; 646 MotionEvent event = (MotionEvent) ev; 647 onMotionEvent(event); 648 } 649 updateMLModelState()650 private void updateMLModelState() { 651 boolean newState = 652 mIsGesturalModeEnabled && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, 653 SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL, false); 654 655 if (newState == mUseMLModel) { 656 return; 657 } 658 659 mUseMLModel = newState; 660 661 if (mUseMLModel) { 662 Assert.isMainThread(); 663 if (mMLModelIsLoading) { 664 Log.d(TAG, "Model tried to load while already loading."); 665 return; 666 } 667 mMLModelIsLoading = true; 668 mBackgroundExecutor.execute(() -> loadMLModel()); 669 } else if (mBackGestureTfClassifierProvider != null) { 670 mBackGestureTfClassifierProvider.release(); 671 mBackGestureTfClassifierProvider = null; 672 mVocab = null; 673 } 674 } 675 loadMLModel()676 private void loadMLModel() { 677 BackGestureTfClassifierProvider provider = mBackGestureTfClassifierProviderProvider.get(); 678 float threshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI, 679 SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f); 680 Map<String, Integer> vocab = null; 681 if (provider != null && !provider.isActive()) { 682 provider.release(); 683 provider = null; 684 Log.w(TAG, "Cannot load model because it isn't active"); 685 } 686 if (provider != null) { 687 Trace.beginSection("EdgeBackGestureHandler#loadVocab"); 688 vocab = provider.loadVocab(mContext.getAssets()); 689 Trace.endSection(); 690 } 691 BackGestureTfClassifierProvider finalProvider = provider; 692 Map<String, Integer> finalVocab = vocab; 693 mMainExecutor.execute(() -> onMLModelLoadFinished(finalProvider, finalVocab, threshold)); 694 } 695 onMLModelLoadFinished(BackGestureTfClassifierProvider provider, Map<String, Integer> vocab, float threshold)696 private void onMLModelLoadFinished(BackGestureTfClassifierProvider provider, 697 Map<String, Integer> vocab, float threshold) { 698 Assert.isMainThread(); 699 mMLModelIsLoading = false; 700 if (!mUseMLModel) { 701 // This can happen if the user disables Gesture Nav while the model is loading. 702 if (provider != null) { 703 provider.release(); 704 } 705 Log.d(TAG, "Model finished loading but isn't needed."); 706 return; 707 } 708 mBackGestureTfClassifierProvider = provider; 709 mVocab = vocab; 710 mMLModelThreshold = threshold; 711 } 712 getBackGesturePredictionsCategory(int x, int y, int app)713 private int getBackGesturePredictionsCategory(int x, int y, int app) { 714 BackGestureTfClassifierProvider provider = mBackGestureTfClassifierProvider; 715 if (provider == null || app == -1) { 716 return -1; 717 } 718 int distanceFromEdge; 719 int location; 720 if (x <= mDisplaySize.x / 2.0) { 721 location = 1; // left 722 distanceFromEdge = x; 723 } else { 724 location = 2; // right 725 distanceFromEdge = mDisplaySize.x - x; 726 } 727 728 Object[] featuresVector = { 729 new long[]{(long) mDisplaySize.x}, 730 new long[]{(long) distanceFromEdge}, 731 new long[]{(long) location}, 732 new long[]{(long) app}, 733 new long[]{(long) y}, 734 }; 735 736 mMLResults = provider.predict(featuresVector); 737 if (mMLResults == -1) { 738 return -1; 739 } 740 return mMLResults >= mMLModelThreshold ? 1 : 0; 741 } 742 isWithinInsets(int x, int y)743 private boolean isWithinInsets(int x, int y) { 744 // Disallow if we are in the bottom gesture area 745 if (y >= (mDisplaySize.y - mBottomGestureHeight)) { 746 return false; 747 } 748 // If the point is way too far (twice the margin), it is 749 // not interesting to us for logging purposes, nor we 750 // should process it. Simply return false and keep 751 // mLogGesture = false. 752 if (x > 2 * (mEdgeWidthLeft + mLeftInset) 753 && x < (mDisplaySize.x - 2 * (mEdgeWidthRight + mRightInset))) { 754 return false; 755 } 756 return true; 757 } 758 isWithinTouchRegion(int x, int y)759 private boolean isWithinTouchRegion(int x, int y) { 760 // If the point is inside the PiP or Nav bar overlay excluded bounds, then ignore the back 761 // gesture 762 final boolean isInsidePip = mIsInPip && mPipExcludedBounds.contains(x, y); 763 if (isInsidePip || mNavBarOverlayExcludedBounds.contains(x, y)) { 764 return false; 765 } 766 767 int app = -1; 768 if (mVocab != null) { 769 app = mVocab.getOrDefault(mPackageName, -1); 770 } 771 772 // Denotes whether we should proceed with the gesture. Even if it is false, we may want to 773 // log it assuming it is not invalid due to exclusion. 774 boolean withinRange = x < mEdgeWidthLeft + mLeftInset 775 || x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset); 776 if (withinRange) { 777 int results = -1; 778 779 // Check if we are within the tightest bounds beyond which we would not need to run the 780 // ML model 781 boolean withinMinRange = x < mMLEnableWidth + mLeftInset 782 || x >= (mDisplaySize.x - mMLEnableWidth - mRightInset); 783 if (!withinMinRange && mUseMLModel && !mMLModelIsLoading 784 && (results = getBackGesturePredictionsCategory(x, y, app)) != -1) { 785 withinRange = (results == 1); 786 } 787 } 788 789 // For debugging purposes 790 mPredictionLog.log(String.format("Prediction [%d,%d,%d,%d,%f,%d]", 791 System.currentTimeMillis(), x, y, app, mMLResults, withinRange ? 1 : 0)); 792 793 // Always allow if the user is in a transient sticky immersive state 794 if (mIsNavBarShownTransiently) { 795 mLogGesture = true; 796 return withinRange; 797 } 798 799 if (mExcludeRegion.contains(x, y)) { 800 if (withinRange) { 801 // We don't have the end point for logging purposes. 802 mEndPoint.x = -1; 803 mEndPoint.y = -1; 804 mLogGesture = true; 805 logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_EXCLUDED); 806 } 807 return false; 808 } 809 810 mInRejectedExclusion = mUnrestrictedExcludeRegion.contains(x, y); 811 mLogGesture = true; 812 return withinRange; 813 } 814 cancelGesture(MotionEvent ev)815 private void cancelGesture(MotionEvent ev) { 816 // Send action cancel to reset all the touch events 817 mAllowGesture = false; 818 mLogGesture = false; 819 mInRejectedExclusion = false; 820 MotionEvent cancelEv = MotionEvent.obtain(ev); 821 cancelEv.setAction(MotionEvent.ACTION_CANCEL); 822 mEdgeBackPlugin.onMotionEvent(cancelEv); 823 dispatchToBackAnimation(cancelEv); 824 cancelEv.recycle(); 825 } 826 logGesture(int backType)827 private void logGesture(int backType) { 828 if (!mLogGesture) { 829 return; 830 } 831 mLogGesture = false; 832 String logPackageName = ""; 833 Map<String, Integer> vocab = mVocab; 834 // Due to privacy, only top 100 most used apps by all users can be logged. 835 if (mUseMLModel && vocab != null && vocab.containsKey(mPackageName) 836 && vocab.get(mPackageName) < 100) { 837 logPackageName = mPackageName; 838 } 839 SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, backType, 840 (int) mDownPoint.y, mIsOnLeftEdge 841 ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT 842 : SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT, 843 (int) mDownPoint.x, (int) mDownPoint.y, 844 (int) mEndPoint.x, (int) mEndPoint.y, 845 mEdgeWidthLeft + mLeftInset, 846 mDisplaySize.x - (mEdgeWidthRight + mRightInset), 847 mUseMLModel ? mMLResults : -2, logPackageName); 848 } 849 onMotionEvent(MotionEvent ev)850 private void onMotionEvent(MotionEvent ev) { 851 int action = ev.getActionMasked(); 852 if (action == MotionEvent.ACTION_DOWN) { 853 if (DEBUG_MISSING_GESTURE) { 854 Log.d(DEBUG_MISSING_GESTURE_TAG, "Start gesture: " + ev); 855 } 856 857 // Verify if this is in within the touch region and we aren't in immersive mode, and 858 // either the bouncer is showing or the notification panel is hidden 859 mInputEventReceiver.setBatchingEnabled(false); 860 mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset; 861 mMLResults = 0; 862 mLogGesture = false; 863 mInRejectedExclusion = false; 864 boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY()); 865 mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed && isWithinInsets 866 && !mGestureBlockingActivityRunning 867 && !QuickStepContract.isBackGestureDisabled(mSysUiFlags) 868 && isWithinTouchRegion((int) ev.getX(), (int) ev.getY()); 869 if (mAllowGesture) { 870 mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge); 871 mEdgeBackPlugin.onMotionEvent(ev); 872 dispatchToBackAnimation(ev); 873 } 874 if (mLogGesture) { 875 mDownPoint.set(ev.getX(), ev.getY()); 876 mEndPoint.set(-1, -1); 877 mThresholdCrossed = false; 878 } 879 880 // For debugging purposes, only log edge points 881 (isWithinInsets ? mGestureLogInsideInsets : mGestureLogOutsideInsets).log(String.format( 882 "Gesture [%d,alw=%B,%B,%B,%B,disp=%s,wl=%d,il=%d,wr=%d,ir=%d,excl=%s]", 883 System.currentTimeMillis(), mAllowGesture, mIsOnLeftEdge, 884 mIsBackGestureAllowed, 885 QuickStepContract.isBackGestureDisabled(mSysUiFlags), mDisplaySize, 886 mEdgeWidthLeft, mLeftInset, mEdgeWidthRight, mRightInset, mExcludeRegion)); 887 } else if (mAllowGesture || mLogGesture) { 888 if (!mThresholdCrossed) { 889 mEndPoint.x = (int) ev.getX(); 890 mEndPoint.y = (int) ev.getY(); 891 if (action == MotionEvent.ACTION_POINTER_DOWN) { 892 if (mAllowGesture) { 893 logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_MULTI_TOUCH); 894 if (DEBUG_MISSING_GESTURE) { 895 Log.d(DEBUG_MISSING_GESTURE_TAG, "Cancel back: multitouch"); 896 } 897 // We do not support multi touch for back gesture 898 cancelGesture(ev); 899 } 900 mLogGesture = false; 901 return; 902 } else if (action == MotionEvent.ACTION_MOVE) { 903 if ((ev.getEventTime() - ev.getDownTime()) > mLongPressTimeout) { 904 if (mAllowGesture) { 905 logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_LONG_PRESS); 906 cancelGesture(ev); 907 if (DEBUG_MISSING_GESTURE) { 908 Log.d(DEBUG_MISSING_GESTURE_TAG, "Cancel back [longpress]: " 909 + ev.getEventTime() 910 + " " + ev.getDownTime() 911 + " " + mLongPressTimeout); 912 } 913 } 914 mLogGesture = false; 915 return; 916 } 917 float dx = Math.abs(ev.getX() - mDownPoint.x); 918 float dy = Math.abs(ev.getY() - mDownPoint.y); 919 if (dy > dx && dy > mTouchSlop) { 920 if (mAllowGesture) { 921 logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_VERTICAL_MOVE); 922 cancelGesture(ev); 923 if (DEBUG_MISSING_GESTURE) { 924 Log.d(DEBUG_MISSING_GESTURE_TAG, "Cancel back [vertical move]: " 925 + dy + " " + dx + " " + mTouchSlop); 926 } 927 } 928 mLogGesture = false; 929 return; 930 } else if (dx > dy && dx > mTouchSlop) { 931 if (mAllowGesture) { 932 mThresholdCrossed = true; 933 // Capture inputs 934 mInputMonitor.pilferPointers(); 935 if (mBackAnimation != null) { 936 // Notify FalsingManager that an intentional gesture has occurred. 937 mFalsingManager.isFalseTouch(BACK_GESTURE); 938 } 939 mInputEventReceiver.setBatchingEnabled(true); 940 } else { 941 logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_FAR_FROM_EDGE); 942 } 943 } 944 } 945 } 946 947 if (mAllowGesture) { 948 // forward touch 949 mEdgeBackPlugin.onMotionEvent(ev); 950 dispatchToBackAnimation(ev); 951 } 952 } 953 954 mProtoTracer.scheduleFrameUpdate(); 955 } 956 dispatchToBackAnimation(MotionEvent event)957 private void dispatchToBackAnimation(MotionEvent event) { 958 if (mBackAnimation != null) { 959 mBackAnimation.onBackMotion( 960 event.getX(), 961 event.getY(), 962 event.getActionMasked(), 963 mIsOnLeftEdge ? BackEvent.EDGE_LEFT : BackEvent.EDGE_RIGHT); 964 } 965 } 966 updateDisabledForQuickstep(Configuration newConfig)967 private void updateDisabledForQuickstep(Configuration newConfig) { 968 int rotation = newConfig.windowConfiguration.getRotation(); 969 mDisabledForQuickstep = mStartingQuickstepRotation > -1 && 970 mStartingQuickstepRotation != rotation; 971 } 972 onConfigurationChanged(@onNull Configuration newConfig)973 public void onConfigurationChanged(@NonNull Configuration newConfig) { 974 if (mStartingQuickstepRotation > -1) { 975 updateDisabledForQuickstep(newConfig); 976 } 977 978 // TODO(b/243765256): Disable this logging once b/243765256 is fixed. 979 Log.i(DEBUG_MISSING_GESTURE_TAG, "Config changed: newConfig=" + newConfig 980 + " lastReportedConfig=" + mLastReportedConfig); 981 mLastReportedConfig.updateFrom(newConfig); 982 updateDisplaySize(); 983 } 984 updateDisplaySize()985 private void updateDisplaySize() { 986 Rect bounds = mLastReportedConfig.windowConfiguration.getMaxBounds(); 987 mDisplaySize.set(bounds.width(), bounds.height()); 988 if (DEBUG_MISSING_GESTURE) { 989 Log.d(DEBUG_MISSING_GESTURE_TAG, "Update display size: mDisplaySize=" + mDisplaySize); 990 } 991 if (mEdgeBackPlugin != null) { 992 mEdgeBackPlugin.setDisplaySize(mDisplaySize); 993 } 994 updateBackAnimationThresholds(); 995 } 996 updateBackAnimationThresholds()997 private void updateBackAnimationThresholds() { 998 if (mBackAnimation == null) { 999 return; 1000 } 1001 mBackAnimation.setSwipeThresholds( 1002 mBackSwipeTriggerThreshold, 1003 Math.min(mDisplaySize.x, mBackSwipeProgressThreshold)); 1004 } 1005 sendEvent(int action, int code)1006 private boolean sendEvent(int action, int code) { 1007 long when = SystemClock.uptimeMillis(); 1008 final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */, 1009 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */, 1010 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, 1011 InputDevice.SOURCE_KEYBOARD); 1012 1013 ev.setDisplayId(mContext.getDisplay().getDisplayId()); 1014 return InputManager.getInstance() 1015 .injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); 1016 } 1017 setInsets(int leftInset, int rightInset)1018 public void setInsets(int leftInset, int rightInset) { 1019 mLeftInset = leftInset; 1020 mRightInset = rightInset; 1021 if (mEdgeBackPlugin != null) { 1022 mEdgeBackPlugin.setInsets(leftInset, rightInset); 1023 } 1024 } 1025 dump(PrintWriter pw)1026 public void dump(PrintWriter pw) { 1027 pw.println("EdgeBackGestureHandler:"); 1028 pw.println(" mIsEnabled=" + mIsEnabled); 1029 pw.println(" mIsAttached=" + mIsAttached); 1030 pw.println(" mIsBackGestureAllowed=" + mIsBackGestureAllowed); 1031 pw.println(" mIsGesturalModeEnabled=" + mIsGesturalModeEnabled); 1032 pw.println(" mIsNavBarShownTransiently=" + mIsNavBarShownTransiently); 1033 pw.println(" mGestureBlockingActivityRunning=" + mGestureBlockingActivityRunning); 1034 pw.println(" mAllowGesture=" + mAllowGesture); 1035 pw.println(" mUseMLModel=" + mUseMLModel); 1036 pw.println(" mDisabledForQuickstep=" + mDisabledForQuickstep); 1037 pw.println(" mStartingQuickstepRotation=" + mStartingQuickstepRotation); 1038 pw.println(" mInRejectedExclusion=" + mInRejectedExclusion); 1039 pw.println(" mExcludeRegion=" + mExcludeRegion); 1040 pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion); 1041 pw.println(" mIsInPip=" + mIsInPip); 1042 pw.println(" mPipExcludedBounds=" + mPipExcludedBounds); 1043 pw.println(" mNavBarOverlayExcludedBounds=" + mNavBarOverlayExcludedBounds); 1044 pw.println(" mEdgeWidthLeft=" + mEdgeWidthLeft); 1045 pw.println(" mEdgeWidthRight=" + mEdgeWidthRight); 1046 pw.println(" mLeftInset=" + mLeftInset); 1047 pw.println(" mRightInset=" + mRightInset); 1048 pw.println(" mMLEnableWidth=" + mMLEnableWidth); 1049 pw.println(" mMLModelThreshold=" + mMLModelThreshold); 1050 pw.println(" mTouchSlop=" + mTouchSlop); 1051 pw.println(" mBottomGestureHeight=" + mBottomGestureHeight); 1052 pw.println(" mPredictionLog=" + String.join("\n", mPredictionLog)); 1053 pw.println(" mGestureLogInsideInsets=" + String.join("\n", mGestureLogInsideInsets)); 1054 pw.println(" mGestureLogOutsideInsets=" + String.join("\n", mGestureLogOutsideInsets)); 1055 pw.println(" mEdgeBackPlugin=" + mEdgeBackPlugin); 1056 if (mEdgeBackPlugin != null) { 1057 mEdgeBackPlugin.dump(pw); 1058 } 1059 } 1060 isGestureBlockingActivityRunning()1061 private boolean isGestureBlockingActivityRunning() { 1062 ActivityManager.RunningTaskInfo runningTask = 1063 ActivityManagerWrapper.getInstance().getRunningTask(); 1064 ComponentName topActivity = runningTask == null ? null : runningTask.topActivity; 1065 if (topActivity != null) { 1066 mPackageName = topActivity.getPackageName(); 1067 } else { 1068 mPackageName = "_UNKNOWN"; 1069 } 1070 return topActivity != null && mGestureBlockingActivities.contains(topActivity); 1071 } 1072 1073 @Override writeToProto(SystemUiTraceProto proto)1074 public void writeToProto(SystemUiTraceProto proto) { 1075 if (proto.edgeBackGestureHandler == null) { 1076 proto.edgeBackGestureHandler = new EdgeBackGestureHandlerProto(); 1077 } 1078 proto.edgeBackGestureHandler.allowGesture = mAllowGesture; 1079 } 1080 setBackAnimation(BackAnimation backAnimation)1081 public void setBackAnimation(BackAnimation backAnimation) { 1082 mBackAnimation = backAnimation; 1083 updateBackAnimationThresholds(); 1084 } 1085 1086 /** 1087 * Injectable instance to create a new EdgeBackGestureHandler. 1088 * 1089 * Necessary because we don't have good handling of per-display contexts at the moment. With 1090 * this, you can pass in a specific context that knows what display it is in. 1091 */ 1092 public static class Factory { 1093 private final OverviewProxyService mOverviewProxyService; 1094 private final SysUiState mSysUiState; 1095 private final PluginManager mPluginManager; 1096 private final Executor mExecutor; 1097 private final Executor mBackgroundExecutor; 1098 private final UserTracker mUserTracker; 1099 private final ProtoTracer mProtoTracer; 1100 private final NavigationModeController mNavigationModeController; 1101 private final BackPanelController.Factory mBackPanelControllerFactory; 1102 private final ViewConfiguration mViewConfiguration; 1103 private final WindowManager mWindowManager; 1104 private final IWindowManager mWindowManagerService; 1105 private final Optional<Pip> mPipOptional; 1106 private final FalsingManager mFalsingManager; 1107 private final Provider<NavigationBarEdgePanel> mNavBarEdgePanelProvider; 1108 private final Provider<BackGestureTfClassifierProvider> 1109 mBackGestureTfClassifierProviderProvider; 1110 private final FeatureFlags mFeatureFlags; 1111 1112 @Inject Factory(OverviewProxyService overviewProxyService, SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor, @Background Executor backgroundExecutor, UserTracker userTracker, ProtoTracer protoTracer, NavigationModeController navigationModeController, BackPanelController.Factory backPanelControllerFactory, ViewConfiguration viewConfiguration, WindowManager windowManager, IWindowManager windowManagerService, Optional<Pip> pipOptional, FalsingManager falsingManager, Provider<NavigationBarEdgePanel> navBarEdgePanelProvider, Provider<BackGestureTfClassifierProvider> backGestureTfClassifierProviderProvider, FeatureFlags featureFlags)1113 public Factory(OverviewProxyService overviewProxyService, 1114 SysUiState sysUiState, 1115 PluginManager pluginManager, 1116 @Main Executor executor, 1117 @Background Executor backgroundExecutor, 1118 UserTracker userTracker, 1119 ProtoTracer protoTracer, 1120 NavigationModeController navigationModeController, 1121 BackPanelController.Factory backPanelControllerFactory, 1122 ViewConfiguration viewConfiguration, 1123 WindowManager windowManager, 1124 IWindowManager windowManagerService, 1125 Optional<Pip> pipOptional, 1126 FalsingManager falsingManager, 1127 Provider<NavigationBarEdgePanel> navBarEdgePanelProvider, 1128 Provider<BackGestureTfClassifierProvider> 1129 backGestureTfClassifierProviderProvider, 1130 FeatureFlags featureFlags) { 1131 mOverviewProxyService = overviewProxyService; 1132 mSysUiState = sysUiState; 1133 mPluginManager = pluginManager; 1134 mExecutor = executor; 1135 mBackgroundExecutor = backgroundExecutor; 1136 mUserTracker = userTracker; 1137 mProtoTracer = protoTracer; 1138 mNavigationModeController = navigationModeController; 1139 mBackPanelControllerFactory = backPanelControllerFactory; 1140 mViewConfiguration = viewConfiguration; 1141 mWindowManager = windowManager; 1142 mWindowManagerService = windowManagerService; 1143 mPipOptional = pipOptional; 1144 mFalsingManager = falsingManager; 1145 mNavBarEdgePanelProvider = navBarEdgePanelProvider; 1146 mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider; 1147 mFeatureFlags = featureFlags; 1148 } 1149 1150 /** Construct a {@link EdgeBackGestureHandler}. */ create(Context context)1151 public EdgeBackGestureHandler create(Context context) { 1152 return new EdgeBackGestureHandler( 1153 context, 1154 mOverviewProxyService, 1155 mSysUiState, 1156 mPluginManager, 1157 mExecutor, 1158 mBackgroundExecutor, 1159 mUserTracker, 1160 mProtoTracer, 1161 mNavigationModeController, 1162 mBackPanelControllerFactory, 1163 mViewConfiguration, 1164 mWindowManager, 1165 mWindowManagerService, 1166 mPipOptional, 1167 mFalsingManager, 1168 mNavBarEdgePanelProvider, 1169 mBackGestureTfClassifierProviderProvider, 1170 mFeatureFlags); 1171 } 1172 } 1173 1174 private static class LogArray extends ArrayDeque<String> { 1175 private final int mLength; 1176 LogArray(int length)1177 LogArray(int length) { 1178 mLength = length; 1179 } 1180 log(String message)1181 void log(String message) { 1182 if (size() >= mLength) { 1183 removeFirst(); 1184 } 1185 addLast(message); 1186 if (DEBUG_MISSING_GESTURE) { 1187 Log.d(DEBUG_MISSING_GESTURE_TAG, message); 1188 } 1189 } 1190 } 1191 } 1192