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 17 package com.android.systemui.navigationbar; 18 19 import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; 20 import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; 21 import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN; 22 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; 23 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; 24 import static android.app.StatusBarManager.WindowType; 25 import static android.app.StatusBarManager.WindowVisibleState; 26 import static android.app.StatusBarManager.windowStateToString; 27 import static android.app.WindowConfiguration.ROTATION_UNDEFINED; 28 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; 29 import static android.view.InsetsState.containsType; 30 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 31 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 32 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; 33 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; 34 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; 35 36 import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; 37 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS; 38 import static com.android.systemui.navigationbar.NavBarHelper.transitionMode; 39 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; 40 import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen; 41 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; 42 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; 43 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY; 44 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING; 45 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING; 46 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN; 47 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; 48 import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode; 49 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; 50 import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode; 51 import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG_WINDOW_STATE; 52 import static com.android.systemui.statusbar.phone.CentralSurfaces.dumpBarTransitions; 53 import static com.android.systemui.util.Utils.isGesturalModeOnDefaultDisplay; 54 55 import android.annotation.IdRes; 56 import android.annotation.NonNull; 57 import android.app.ActivityTaskManager; 58 import android.app.IActivityTaskManager; 59 import android.app.StatusBarManager; 60 import android.content.Context; 61 import android.content.Intent; 62 import android.content.res.Configuration; 63 import android.graphics.Insets; 64 import android.graphics.PixelFormat; 65 import android.graphics.Point; 66 import android.graphics.Rect; 67 import android.graphics.RectF; 68 import android.graphics.Region; 69 import android.os.Binder; 70 import android.os.Bundle; 71 import android.os.Handler; 72 import android.os.IBinder; 73 import android.os.RemoteException; 74 import android.provider.DeviceConfig; 75 import android.telecom.TelecomManager; 76 import android.text.TextUtils; 77 import android.util.Log; 78 import android.view.Display; 79 import android.view.Gravity; 80 import android.view.HapticFeedbackConstants; 81 import android.view.InsetsFrameProvider; 82 import android.view.InsetsState.InternalInsetsType; 83 import android.view.InsetsVisibilities; 84 import android.view.KeyEvent; 85 import android.view.MotionEvent; 86 import android.view.Surface; 87 import android.view.SurfaceControl; 88 import android.view.SurfaceControl.Transaction; 89 import android.view.View; 90 import android.view.ViewRootImpl; 91 import android.view.ViewRootImpl.SurfaceChangedCallback; 92 import android.view.ViewTreeObserver; 93 import android.view.ViewTreeObserver.InternalInsetsInfo; 94 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; 95 import android.view.WindowInsetsController.Appearance; 96 import android.view.WindowInsetsController.Behavior; 97 import android.view.WindowManager; 98 import android.view.accessibility.AccessibilityEvent; 99 import android.view.accessibility.AccessibilityManager; 100 import android.view.inputmethod.InputMethodManager; 101 102 import androidx.annotation.Nullable; 103 import androidx.annotation.VisibleForTesting; 104 105 import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity; 106 import com.android.internal.logging.MetricsLogger; 107 import com.android.internal.logging.UiEvent; 108 import com.android.internal.logging.UiEventLogger; 109 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 110 import com.android.internal.statusbar.LetterboxDetails; 111 import com.android.internal.util.LatencyTracker; 112 import com.android.internal.view.AppearanceRegion; 113 import com.android.systemui.Gefingerpoken; 114 import com.android.systemui.R; 115 import com.android.systemui.assist.AssistManager; 116 import com.android.systemui.dagger.qualifiers.Background; 117 import com.android.systemui.dagger.qualifiers.DisplayId; 118 import com.android.systemui.dagger.qualifiers.Main; 119 import com.android.systemui.keyguard.WakefulnessLifecycle; 120 import com.android.systemui.model.SysUiState; 121 import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope; 122 import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener; 123 import com.android.systemui.navigationbar.buttons.ButtonDispatcher; 124 import com.android.systemui.navigationbar.buttons.DeadZone; 125 import com.android.systemui.navigationbar.buttons.KeyButtonView; 126 import com.android.systemui.navigationbar.buttons.RotationContextButton; 127 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler; 128 import com.android.systemui.navigationbar.gestural.QuickswitchOrientedNavHandle; 129 import com.android.systemui.plugins.statusbar.StatusBarStateController; 130 import com.android.systemui.recents.OverviewProxyService; 131 import com.android.systemui.recents.Recents; 132 import com.android.systemui.settings.DisplayTracker; 133 import com.android.systemui.settings.UserContextProvider; 134 import com.android.systemui.settings.UserTracker; 135 import com.android.systemui.shade.ShadeController; 136 import com.android.systemui.shared.navigationbar.RegionSamplingHelper; 137 import com.android.systemui.shared.recents.utilities.Utilities; 138 import com.android.systemui.shared.rotation.RotationButton; 139 import com.android.systemui.shared.rotation.RotationButtonController; 140 import com.android.systemui.shared.system.QuickStepContract; 141 import com.android.systemui.shared.system.SysUiStatsLog; 142 import com.android.systemui.shared.system.TaskStackChangeListener; 143 import com.android.systemui.shared.system.TaskStackChangeListeners; 144 import com.android.systemui.statusbar.AutoHideUiElement; 145 import com.android.systemui.statusbar.CommandQueue; 146 import com.android.systemui.statusbar.CommandQueue.Callbacks; 147 import com.android.systemui.statusbar.NotificationRemoteInputManager; 148 import com.android.systemui.statusbar.NotificationShadeDepthController; 149 import com.android.systemui.statusbar.StatusBarState; 150 import com.android.systemui.statusbar.notification.stack.StackStateAnimator; 151 import com.android.systemui.statusbar.phone.AutoHideController; 152 import com.android.systemui.statusbar.phone.BarTransitions; 153 import com.android.systemui.statusbar.phone.CentralSurfaces; 154 import com.android.systemui.statusbar.phone.LightBarController; 155 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; 156 import com.android.systemui.statusbar.policy.DeviceProvisionedController; 157 import com.android.systemui.util.DeviceConfigProxy; 158 import com.android.systemui.util.ViewController; 159 import com.android.wm.shell.back.BackAnimation; 160 import com.android.wm.shell.pip.Pip; 161 162 import java.io.PrintWriter; 163 import java.util.Locale; 164 import java.util.Map; 165 import java.util.Optional; 166 import java.util.concurrent.Executor; 167 168 import javax.inject.Inject; 169 170 import dagger.Lazy; 171 172 /** 173 * Contains logic for a navigation bar view. 174 */ 175 @NavigationBarScope 176 public class NavigationBar extends ViewController<NavigationBarView> implements Callbacks { 177 178 public static final String TAG = "NavigationBar"; 179 private static final boolean DEBUG = false; 180 private static final String EXTRA_DISABLE_STATE = "disabled_state"; 181 private static final String EXTRA_DISABLE2_STATE = "disabled2_state"; 182 private static final String EXTRA_APPEARANCE = "appearance"; 183 private static final String EXTRA_BEHAVIOR = "behavior"; 184 private static final String EXTRA_TRANSIENT_STATE = "transient_state"; 185 186 /** Allow some time inbetween the long press for back and recents. */ 187 private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200; 188 private static final long AUTODIM_TIMEOUT_MS = 2250; 189 190 private final Context mContext; 191 private final Bundle mSavedState; 192 private final WindowManager mWindowManager; 193 private final AccessibilityManager mAccessibilityManager; 194 private final DeviceProvisionedController mDeviceProvisionedController; 195 private final StatusBarStateController mStatusBarStateController; 196 private final MetricsLogger mMetricsLogger; 197 private final Lazy<AssistManager> mAssistManagerLazy; 198 private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 199 private final SysUiState mSysUiFlagsContainer; 200 private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy; 201 private final ShadeController mShadeController; 202 private final NotificationRemoteInputManager mNotificationRemoteInputManager; 203 private final OverviewProxyService mOverviewProxyService; 204 private final NavigationModeController mNavigationModeController; 205 private final UserTracker mUserTracker; 206 private final CommandQueue mCommandQueue; 207 private final Optional<Pip> mPipOptional; 208 private final Optional<Recents> mRecentsOptional; 209 private final DeviceConfigProxy mDeviceConfigProxy; 210 private final NavigationBarTransitions mNavigationBarTransitions; 211 private final Optional<BackAnimation> mBackAnimation; 212 private final Handler mHandler; 213 private final UiEventLogger mUiEventLogger; 214 private final NavBarHelper mNavBarHelper; 215 private final NotificationShadeDepthController mNotificationShadeDepthController; 216 private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener; 217 private final UserContextProvider mUserContextProvider; 218 private final WakefulnessLifecycle mWakefulnessLifecycle; 219 private final DisplayTracker mDisplayTracker; 220 private final RegionSamplingHelper mRegionSamplingHelper; 221 private final int mNavColorSampleMargin; 222 private EdgeBackGestureHandler mEdgeBackGestureHandler; 223 private NavigationBarFrame mFrame; 224 225 private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING; 226 227 private int mNavigationIconHints = 0; 228 private @TransitionMode int mTransitionMode; 229 private boolean mLongPressHomeEnabled; 230 231 private int mDisabledFlags1; 232 private int mDisabledFlags2; 233 private long mLastLockToAppLongPress; 234 235 private Locale mLocale; 236 private int mLayoutDirection; 237 238 private Optional<Long> mHomeButtonLongPressDurationMs; 239 240 /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */ 241 private @Appearance int mAppearance; 242 243 /** @see android.view.WindowInsetsController#setSystemBarsBehavior(int) */ 244 private @Behavior int mBehavior; 245 246 private boolean mTransientShown; 247 private boolean mTransientShownFromGestureOnSystemBar; 248 private int mNavBarMode = NAV_BAR_MODE_3BUTTON; 249 private LightBarController mLightBarController; 250 private final LightBarController mMainLightBarController; 251 private final LightBarController.Factory mLightBarControllerFactory; 252 private AutoHideController mAutoHideController; 253 private final AutoHideController mMainAutoHideController; 254 private final AutoHideController.Factory mAutoHideControllerFactory; 255 private final Optional<TelecomManager> mTelecomManagerOptional; 256 private final InputMethodManager mInputMethodManager; 257 private final TaskStackChangeListeners mTaskStackChangeListeners; 258 259 @VisibleForTesting 260 public int mDisplayId; 261 private boolean mIsOnDefaultDisplay; 262 public boolean mHomeBlockedThisTouch; 263 264 /** 265 * When user is QuickSwitching between apps of different orientations, we'll draw a fake 266 * home handle on the orientation they originally touched down to start their swipe 267 * gesture to indicate to them that they can continue in that orientation without having to 268 * rotate the phone 269 * The secondary handle will show when we get 270 * {@link OverviewProxyListener#notifyPrioritizedRotation(int)} callback with the 271 * original handle hidden and we'll flip the visibilities once the 272 * {@link #mTasksFrozenListener} fires 273 */ 274 private QuickswitchOrientedNavHandle mOrientationHandle; 275 private WindowManager.LayoutParams mOrientationParams; 276 private int mStartingQuickSwitchRotation = -1; 277 private int mCurrentRotation; 278 private ViewTreeObserver.OnGlobalLayoutListener mOrientationHandleGlobalLayoutListener; 279 private boolean mShowOrientedHandleForImmersiveMode; 280 private final DeadZone mDeadZone; 281 private boolean mImeVisible; 282 private final Rect mSamplingBounds = new Rect(); 283 284 /** 285 * When quickswitching between apps of different orientations, we draw a secondary home handle 286 * in the position of the first app's orientation. This rect represents the region of that 287 * home handle so we can apply the correct light/dark luma on that. 288 * @see {@link NavigationBar#mOrientationHandle} 289 */ 290 @android.annotation.Nullable 291 private Rect mOrientedHandleSamplingRegion; 292 293 @com.android.internal.annotations.VisibleForTesting 294 public enum NavBarActionEvent implements UiEventLogger.UiEventEnum { 295 296 @UiEvent(doc = "Assistant invoked via home button long press.") 297 NAVBAR_ASSIST_LONGPRESS(550); 298 299 private final int mId; 300 NavBarActionEvent(int id)301 NavBarActionEvent(int id) { 302 mId = id; 303 } 304 305 @Override getId()306 public int getId() { 307 return mId; 308 } 309 } 310 311 private final AutoHideUiElement mAutoHideUiElement = new AutoHideUiElement() { 312 @Override 313 public void synchronizeState() { 314 checkNavBarModes(); 315 } 316 317 @Override 318 public boolean shouldHideOnTouch() { 319 return !mNotificationRemoteInputManager.isRemoteInputActive(); 320 } 321 322 @Override 323 public boolean isVisible() { 324 return isTransientShown(); 325 } 326 327 @Override 328 public void hide() { 329 clearTransient(); 330 } 331 }; 332 333 private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater = 334 new NavBarHelper.NavbarTaskbarStateUpdater() { 335 @Override 336 public void updateAccessibilityServicesState() { 337 updateAccessibilityStateFlags(); 338 } 339 340 @Override 341 public void updateAssistantAvailable(boolean available, 342 boolean longPressHomeEnabled) { 343 // TODO(b/198002034): Content observers currently can still be called back after 344 // being unregistered, and in this case we can ignore the change if the nav bar 345 // has been destroyed already 346 if (mView == null) { 347 return; 348 } 349 mLongPressHomeEnabled = longPressHomeEnabled; 350 updateAssistantEntrypoints(available, longPressHomeEnabled); 351 } 352 353 @Override 354 public void updateWallpaperVisibility(boolean visible, int displayId) { 355 mNavigationBarTransitions.setWallpaperVisibility(visible); 356 } 357 358 @Override 359 public void updateRotationWatcherState(int rotation) { 360 if (mIsOnDefaultDisplay && mView != null) { 361 mView.getRotationButtonController().onRotationWatcherChanged(rotation); 362 if (mView.needsReorient(rotation)) { 363 repositionNavigationBar(rotation); 364 } 365 } 366 } 367 }; 368 369 private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() { 370 @Override 371 public void onConnectionChanged(boolean isConnected) { 372 mView.onOverviewProxyConnectionChange( 373 mOverviewProxyService.isEnabled()); 374 mView.setShouldShowSwipeUpUi(mOverviewProxyService.shouldShowSwipeUpUI()); 375 updateScreenPinningGestures(); 376 } 377 378 @Override 379 public void onPrioritizedRotation(@Surface.Rotation int rotation) { 380 mStartingQuickSwitchRotation = rotation; 381 if (rotation == -1) { 382 mShowOrientedHandleForImmersiveMode = false; 383 } 384 orientSecondaryHomeHandle(); 385 } 386 387 @Override 388 public void startAssistant(Bundle bundle) { 389 mAssistManagerLazy.get().startAssist(bundle); 390 } 391 392 @Override 393 public void onHomeRotationEnabled(boolean enabled) { 394 mView.getRotationButtonController().setHomeRotationEnabled(enabled); 395 } 396 397 @Override 398 public void onOverviewShown(boolean fromHome) { 399 // If the overview has fixed orientation that may change display to natural rotation, 400 // we don't want the user rotation to be reset. So after user returns to application, 401 // it can keep in the original rotation. 402 mView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce(); 403 } 404 405 @Override 406 public void onTaskbarStatusUpdated(boolean visible, boolean stashed) { 407 mView.getFloatingRotationButton().onTaskbarStateChanged(visible, stashed); 408 } 409 410 @Override 411 public void onToggleRecentApps() { 412 // The same case as onOverviewShown but only for 3-button navigation. 413 mView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce(); 414 } 415 }; 416 417 private NavigationBarTransitions.DarkIntensityListener mOrientationHandleIntensityListener = 418 new NavigationBarTransitions.DarkIntensityListener() { 419 @Override 420 public void onDarkIntensity(float darkIntensity) { 421 mOrientationHandle.setDarkIntensity(darkIntensity); 422 } 423 }; 424 425 private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true); 426 private final Runnable mEnableLayoutTransitions = () -> mView.setLayoutTransitionsEnabled(true); 427 private final Runnable mOnVariableDurationHomeLongClick = () -> { 428 if (onHomeLongClick(mView.getHomeButton().getCurrentView())) { 429 mView.getHomeButton().getCurrentView().performHapticFeedback( 430 HapticFeedbackConstants.LONG_PRESS, 431 HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); 432 } 433 }; 434 435 private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener = 436 new DeviceConfig.OnPropertiesChangedListener() { 437 @Override 438 public void onPropertiesChanged(DeviceConfig.Properties properties) { 439 if (properties.getKeyset().contains(HOME_BUTTON_LONG_PRESS_DURATION_MS)) { 440 mHomeButtonLongPressDurationMs = Optional.of( 441 properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0)) 442 .filter(duration -> duration != 0); 443 if (mView != null) { 444 reconfigureHomeLongClick(); 445 } 446 } 447 } 448 }; 449 450 private final NotificationShadeDepthController.DepthListener mDepthListener = 451 new NotificationShadeDepthController.DepthListener() { 452 boolean mHasBlurs; 453 454 @Override 455 public void onWallpaperZoomOutChanged(float zoomOut) { 456 } 457 458 @Override 459 public void onBlurRadiusChanged(int radius) { 460 boolean hasBlurs = radius != 0; 461 if (hasBlurs == mHasBlurs) { 462 return; 463 } 464 mHasBlurs = hasBlurs; 465 mRegionSamplingHelper.setWindowHasBlurs(hasBlurs); 466 } 467 }; 468 469 private final WakefulnessLifecycle.Observer mWakefulnessObserver = 470 new WakefulnessLifecycle.Observer() { 471 private void notifyScreenStateChanged(boolean isScreenOn) { 472 notifyNavigationBarScreenOn(); 473 mView.onScreenStateChanged(isScreenOn); 474 } 475 476 @Override 477 public void onStartedWakingUp() { 478 notifyScreenStateChanged(true); 479 if (isGesturalModeOnDefaultDisplay(getContext(), mDisplayTracker, 480 mNavBarMode)) { 481 mRegionSamplingHelper.start(mSamplingBounds); 482 } 483 } 484 485 @Override 486 public void onFinishedGoingToSleep() { 487 notifyScreenStateChanged(false); 488 mRegionSamplingHelper.stop(); 489 } 490 }; 491 492 private final ViewRootImpl.SurfaceChangedCallback mSurfaceChangedCallback = 493 new SurfaceChangedCallback() { 494 @Override 495 public void surfaceCreated(Transaction t) { 496 notifyNavigationBarSurface(); 497 } 498 499 @Override 500 public void surfaceDestroyed() { 501 notifyNavigationBarSurface(); 502 } 503 504 @Override 505 public void surfaceReplaced(Transaction t) { 506 notifyNavigationBarSurface(); 507 } 508 }; 509 510 private boolean mScreenPinningActive = false; 511 private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { 512 @Override 513 public void onLockTaskModeChanged(int mode) { 514 mScreenPinningActive = (mode == LOCK_TASK_MODE_PINNED); 515 mSysUiFlagsContainer.setFlag(SYSUI_STATE_SCREEN_PINNING, mScreenPinningActive) 516 .commitUpdate(mDisplayId); 517 mView.setInScreenPinning(mScreenPinningActive); 518 updateScreenPinningGestures(); 519 } 520 }; 521 522 @Inject NavigationBar( NavigationBarView navigationBarView, NavigationBarFrame navigationBarFrame, @Nullable Bundle savedState, @DisplayId Context context, @DisplayId WindowManager windowManager, Lazy<AssistManager> assistManagerLazy, AccessibilityManager accessibilityManager, DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger, OverviewProxyService overviewProxyService, NavigationModeController navigationModeController, StatusBarStateController statusBarStateController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, SysUiState sysUiFlagsContainer, UserTracker userTracker, CommandQueue commandQueue, Optional<Pip> pipOptional, Optional<Recents> recentsOptional, Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy, ShadeController shadeController, NotificationRemoteInputManager notificationRemoteInputManager, NotificationShadeDepthController notificationShadeDepthController, @Main Handler mainHandler, @Main Executor mainExecutor, @Background Executor bgExecutor, UiEventLogger uiEventLogger, NavBarHelper navBarHelper, LightBarController mainLightBarController, LightBarController.Factory lightBarControllerFactory, AutoHideController mainAutoHideController, AutoHideController.Factory autoHideControllerFactory, Optional<TelecomManager> telecomManagerOptional, InputMethodManager inputMethodManager, DeadZone deadZone, DeviceConfigProxy deviceConfigProxy, NavigationBarTransitions navigationBarTransitions, Optional<BackAnimation> backAnimation, UserContextProvider userContextProvider, WakefulnessLifecycle wakefulnessLifecycle, TaskStackChangeListeners taskStackChangeListeners, DisplayTracker displayTracker)523 NavigationBar( 524 NavigationBarView navigationBarView, 525 NavigationBarFrame navigationBarFrame, 526 @Nullable Bundle savedState, 527 @DisplayId Context context, 528 @DisplayId WindowManager windowManager, 529 Lazy<AssistManager> assistManagerLazy, 530 AccessibilityManager accessibilityManager, 531 DeviceProvisionedController deviceProvisionedController, 532 MetricsLogger metricsLogger, 533 OverviewProxyService overviewProxyService, 534 NavigationModeController navigationModeController, 535 StatusBarStateController statusBarStateController, 536 StatusBarKeyguardViewManager statusBarKeyguardViewManager, 537 SysUiState sysUiFlagsContainer, 538 UserTracker userTracker, 539 CommandQueue commandQueue, 540 Optional<Pip> pipOptional, 541 Optional<Recents> recentsOptional, 542 Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy, 543 ShadeController shadeController, 544 NotificationRemoteInputManager notificationRemoteInputManager, 545 NotificationShadeDepthController notificationShadeDepthController, 546 @Main Handler mainHandler, 547 @Main Executor mainExecutor, 548 @Background Executor bgExecutor, 549 UiEventLogger uiEventLogger, 550 NavBarHelper navBarHelper, 551 LightBarController mainLightBarController, 552 LightBarController.Factory lightBarControllerFactory, 553 AutoHideController mainAutoHideController, 554 AutoHideController.Factory autoHideControllerFactory, 555 Optional<TelecomManager> telecomManagerOptional, 556 InputMethodManager inputMethodManager, 557 DeadZone deadZone, 558 DeviceConfigProxy deviceConfigProxy, 559 NavigationBarTransitions navigationBarTransitions, 560 Optional<BackAnimation> backAnimation, 561 UserContextProvider userContextProvider, 562 WakefulnessLifecycle wakefulnessLifecycle, 563 TaskStackChangeListeners taskStackChangeListeners, 564 DisplayTracker displayTracker) { 565 super(navigationBarView); 566 mFrame = navigationBarFrame; 567 mContext = context; 568 mSavedState = savedState; 569 mWindowManager = windowManager; 570 mAccessibilityManager = accessibilityManager; 571 mDeviceProvisionedController = deviceProvisionedController; 572 mStatusBarStateController = statusBarStateController; 573 mMetricsLogger = metricsLogger; 574 mAssistManagerLazy = assistManagerLazy; 575 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; 576 mSysUiFlagsContainer = sysUiFlagsContainer; 577 mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy; 578 mShadeController = shadeController; 579 mNotificationRemoteInputManager = notificationRemoteInputManager; 580 mOverviewProxyService = overviewProxyService; 581 mNavigationModeController = navigationModeController; 582 mUserTracker = userTracker; 583 mCommandQueue = commandQueue; 584 mPipOptional = pipOptional; 585 mRecentsOptional = recentsOptional; 586 mDeadZone = deadZone; 587 mDeviceConfigProxy = deviceConfigProxy; 588 mNavigationBarTransitions = navigationBarTransitions; 589 mBackAnimation = backAnimation; 590 mHandler = mainHandler; 591 mUiEventLogger = uiEventLogger; 592 mNavBarHelper = navBarHelper; 593 mNotificationShadeDepthController = notificationShadeDepthController; 594 mMainLightBarController = mainLightBarController; 595 mLightBarControllerFactory = lightBarControllerFactory; 596 mMainAutoHideController = mainAutoHideController; 597 mAutoHideControllerFactory = autoHideControllerFactory; 598 mTelecomManagerOptional = telecomManagerOptional; 599 mInputMethodManager = inputMethodManager; 600 mUserContextProvider = userContextProvider; 601 mWakefulnessLifecycle = wakefulnessLifecycle; 602 mTaskStackChangeListeners = taskStackChangeListeners; 603 mDisplayTracker = displayTracker; 604 mEdgeBackGestureHandler = navBarHelper.getEdgeBackGestureHandler(); 605 606 mNavColorSampleMargin = getResources() 607 .getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin); 608 609 mOnComputeInternalInsetsListener = info -> { 610 // When the nav bar is in 2-button or 3-button mode, or when IME is visible in fully 611 // gestural mode, the entire nav bar should be touchable. 612 if (!mEdgeBackGestureHandler.isHandlingGestures()) { 613 // We're in 2/3 button mode OR back button force-shown in SUW 614 if (!mImeVisible) { 615 // IME not showing, take all touches 616 info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); 617 return; 618 } 619 if (!mView.isImeRenderingNavButtons()) { 620 // IME showing but not drawing any buttons, take all touches 621 info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); 622 return; 623 } 624 } 625 626 // When in gestural and the IME is showing, don't use the nearest region since it will 627 // take gesture space away from the IME 628 info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION); 629 info.touchableRegion.set(getButtonLocations(false /* includeFloatingButtons */, 630 false /* inScreen */, false /* useNearestRegion */)); 631 }; 632 633 mRegionSamplingHelper = new RegionSamplingHelper(mView, 634 new RegionSamplingHelper.SamplingCallback() { 635 @Override 636 public void onRegionDarknessChanged(boolean isRegionDark) { 637 getBarTransitions().getLightTransitionsController().setIconsDark( 638 !isRegionDark, true /* animate */); 639 } 640 641 @Override 642 public Rect getSampledRegion(View sampledView) { 643 if (mOrientedHandleSamplingRegion != null) { 644 return mOrientedHandleSamplingRegion; 645 } 646 647 return calculateSamplingRect(); 648 } 649 650 @Override 651 public boolean isSamplingEnabled() { 652 return isGesturalModeOnDefaultDisplay(getContext(), mDisplayTracker, 653 mNavBarMode); 654 } 655 }, mainExecutor, bgExecutor); 656 657 mView.setBackgroundExecutor(bgExecutor); 658 mView.setEdgeBackGestureHandler(mEdgeBackGestureHandler); 659 mView.setDisplayTracker(mDisplayTracker); 660 mNavBarMode = mNavigationModeController.addListener(mModeChangedListener); 661 } 662 getView()663 public NavigationBarView getView() { 664 return mView; 665 } 666 667 @Override onInit()668 public void onInit() { 669 // TODO: A great deal of this code should probably live in onViewAttached. 670 // It should also has corresponding cleanup in onViewDetached. 671 mView.setBarTransitions(mNavigationBarTransitions); 672 mView.setTouchHandler(mTouchHandler); 673 setNavBarMode(mNavBarMode); 674 mEdgeBackGestureHandler.setStateChangeCallback(mView::updateStates); 675 mNavigationBarTransitions.addListener(this::onBarTransition); 676 mView.updateRotationButton(); 677 678 mView.setVisibility( 679 mStatusBarKeyguardViewManager.isNavBarVisible() ? View.VISIBLE : View.INVISIBLE); 680 681 if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mView); 682 683 mWindowManager.addView(mFrame, 684 getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration 685 .getRotation())); 686 mDisplayId = mContext.getDisplayId(); 687 mIsOnDefaultDisplay = mDisplayId == mDisplayTracker.getDefaultDisplayId(); 688 689 // Ensure we try to get currentSysuiState from navBarHelper before command queue callbacks 690 // start firing, since the latter is source of truth 691 parseCurrentSysuiState(); 692 mCommandQueue.addCallback(this); 693 mHomeButtonLongPressDurationMs = Optional.of(mDeviceConfigProxy.getLong( 694 DeviceConfig.NAMESPACE_SYSTEMUI, 695 HOME_BUTTON_LONG_PRESS_DURATION_MS, 696 /* defaultValue = */ 0 697 )).filter(duration -> duration != 0); 698 // This currently MUST be called after mHomeButtonLongPressDurationMs is initialized since 699 // the registration callbacks will trigger code that uses it 700 mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater); 701 mDeviceConfigProxy.addOnPropertiesChangedListener( 702 DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener); 703 704 if (mSavedState != null) { 705 mDisabledFlags1 = mSavedState.getInt(EXTRA_DISABLE_STATE, 0); 706 mDisabledFlags2 = mSavedState.getInt(EXTRA_DISABLE2_STATE, 0); 707 mAppearance = mSavedState.getInt(EXTRA_APPEARANCE, 0); 708 mBehavior = mSavedState.getInt(EXTRA_BEHAVIOR, 0); 709 mTransientShown = mSavedState.getBoolean(EXTRA_TRANSIENT_STATE, false); 710 } 711 712 // Respect the latest disabled-flags. 713 mCommandQueue.recomputeDisableFlags(mDisplayId, false); 714 715 mNotificationShadeDepthController.addListener(mDepthListener); 716 mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener); 717 } 718 destroyView()719 public void destroyView() { 720 setAutoHideController(/* autoHideController */ null); 721 mCommandQueue.removeCallback(this); 722 mWindowManager.removeViewImmediate(mView.getRootView()); 723 mNavigationModeController.removeListener(mModeChangedListener); 724 mEdgeBackGestureHandler.setStateChangeCallback(null); 725 726 mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater); 727 mNotificationShadeDepthController.removeListener(mDepthListener); 728 729 mDeviceConfigProxy.removeOnPropertiesChangedListener(mOnPropertiesChangedListener); 730 mTaskStackChangeListeners.unregisterTaskStackListener(mTaskStackListener); 731 } 732 733 @Override onViewAttached()734 public void onViewAttached() { 735 final Display display = mView.getDisplay(); 736 mView.setComponents(mRecentsOptional); 737 if (mCentralSurfacesOptionalLazy.get().isPresent()) { 738 mView.setComponents( 739 mCentralSurfacesOptionalLazy.get().get().getNotificationPanelViewController()); 740 } 741 mView.setDisabledFlags(mDisabledFlags1, mSysUiFlagsContainer); 742 mView.setOnVerticalChangedListener(this::onVerticalChanged); 743 mView.setOnTouchListener(this::onNavigationTouch); 744 if (mSavedState != null) { 745 getBarTransitions().getLightTransitionsController().restoreState(mSavedState); 746 } 747 setNavigationIconHints(mNavigationIconHints); 748 setWindowVisible(isNavBarWindowVisible()); 749 mView.setBehavior(mBehavior); 750 setNavBarMode(mNavBarMode); 751 mView.setUpdateActiveTouchRegionsCallback( 752 () -> mOverviewProxyService.onActiveNavBarRegionChanges( 753 getButtonLocations( 754 true /* includeFloatingButtons */, 755 true /* inScreen */, 756 true /* useNearestRegion */))); 757 758 mView.getViewTreeObserver().addOnComputeInternalInsetsListener( 759 mOnComputeInternalInsetsListener); 760 mView.getViewRootImpl().addSurfaceChangedCallback(mSurfaceChangedCallback); 761 notifyNavigationBarSurface(); 762 763 mPipOptional.ifPresent(mView::addPipExclusionBoundsChangeListener); 764 mBackAnimation.ifPresent(mView::registerBackAnimation); 765 766 prepareNavigationBarView(); 767 checkNavBarModes(); 768 769 mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor()); 770 mWakefulnessLifecycle.addObserver(mWakefulnessObserver); 771 notifyNavigationBarScreenOn(); 772 773 mOverviewProxyService.addCallback(mOverviewProxyListener); 774 updateSystemUiStateFlags(); 775 776 // Currently there is no accelerometer sensor on non-default display. 777 if (mIsOnDefaultDisplay) { 778 final RotationButtonController rotationButtonController = 779 mView.getRotationButtonController(); 780 781 // Reset user rotation pref to match that of the WindowManager if starting in locked 782 // mode. This will automatically happen when switching from auto-rotate to locked mode. 783 if (display != null && rotationButtonController.isRotationLocked()) { 784 rotationButtonController.setRotationLockedAtAngle(display.getRotation()); 785 } 786 } else { 787 mDisabledFlags2 |= StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS; 788 } 789 setDisabled2Flags(mDisabledFlags2); 790 791 initSecondaryHomeHandleForRotation(); 792 793 // Unfortunately, we still need it because status bar needs LightBarController 794 // before notifications creation. We cannot directly use getLightBarController() 795 // from NavigationBarFragment directly. 796 LightBarController lightBarController = mIsOnDefaultDisplay 797 ? mMainLightBarController : mLightBarControllerFactory.create(mContext); 798 setLightBarController(lightBarController); 799 800 // TODO(b/118592525): to support multi-display, we start to add something which is 801 // per-display, while others may be global. I think it's time to 802 // add a new class maybe named DisplayDependency to solve 803 // per-display Dependency problem. 804 // Alternative: this is a good case for a Dagger subcomponent. Same with LightBarController. 805 AutoHideController autoHideController = mIsOnDefaultDisplay 806 ? mMainAutoHideController : mAutoHideControllerFactory.create(mContext); 807 setAutoHideController(autoHideController); 808 restoreAppearanceAndTransientState(); 809 } 810 811 @Override onViewDetached()812 public void onViewDetached() { 813 mView.setUpdateActiveTouchRegionsCallback(null); 814 getBarTransitions().destroy(); 815 mOverviewProxyService.removeCallback(mOverviewProxyListener); 816 mUserTracker.removeCallback(mUserChangedCallback); 817 mWakefulnessLifecycle.removeObserver(mWakefulnessObserver); 818 if (mOrientationHandle != null) { 819 resetSecondaryHandle(); 820 getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener); 821 mWindowManager.removeView(mOrientationHandle); 822 mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener( 823 mOrientationHandleGlobalLayoutListener); 824 } 825 mView.getViewTreeObserver().removeOnComputeInternalInsetsListener( 826 mOnComputeInternalInsetsListener); 827 mHandler.removeCallbacks(mAutoDim); 828 mHandler.removeCallbacks(mOnVariableDurationHomeLongClick); 829 mHandler.removeCallbacks(mEnableLayoutTransitions); 830 mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater); 831 mPipOptional.ifPresent(mView::removePipExclusionBoundsChangeListener); 832 ViewRootImpl viewRoot = mView.getViewRootImpl(); 833 if (viewRoot != null) { 834 viewRoot.removeSurfaceChangedCallback(mSurfaceChangedCallback); 835 } 836 mFrame = null; 837 mOrientationHandle = null; 838 } 839 840 // TODO: Remove this when we update nav bar recreation onSaveInstanceState(Bundle outState)841 public void onSaveInstanceState(Bundle outState) { 842 outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1); 843 outState.putInt(EXTRA_DISABLE2_STATE, mDisabledFlags2); 844 outState.putInt(EXTRA_APPEARANCE, mAppearance); 845 outState.putInt(EXTRA_BEHAVIOR, mBehavior); 846 outState.putBoolean(EXTRA_TRANSIENT_STATE, mTransientShown); 847 getBarTransitions().getLightTransitionsController().saveState(outState); 848 } 849 850 /** 851 * Called when a non-reloading configuration change happens and we need to update. 852 */ onConfigurationChanged(Configuration newConfig)853 public void onConfigurationChanged(Configuration newConfig) { 854 final int rotation = newConfig.windowConfiguration.getRotation(); 855 final Locale locale = mContext.getResources().getConfiguration().locale; 856 final int ld = TextUtils.getLayoutDirectionFromLocale(locale); 857 if (!locale.equals(mLocale) || ld != mLayoutDirection) { 858 if (DEBUG) { 859 Log.v(TAG, String.format( 860 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection, 861 locale, ld)); 862 } 863 mLocale = locale; 864 mLayoutDirection = ld; 865 refreshLayout(ld); 866 } 867 868 repositionNavigationBar(rotation); 869 // NOTE(b/260220098): In some cases, the recreated nav bar will already have the right 870 // configuration, which means that NavBarView will not receive a configuration change to 871 // propagate to EdgeBackGestureHandler (which is injected into this and NBV). As such, we 872 // should also force-update the gesture handler to ensure it updates to the right bounds 873 mEdgeBackGestureHandler.onConfigurationChanged(newConfig); 874 if (canShowSecondaryHandle()) { 875 if (rotation != mCurrentRotation) { 876 mCurrentRotation = rotation; 877 orientSecondaryHomeHandle(); 878 } 879 } 880 } 881 initSecondaryHomeHandleForRotation()882 private void initSecondaryHomeHandleForRotation() { 883 if (mNavBarMode != NAV_BAR_MODE_GESTURAL) { 884 return; 885 } 886 887 mOrientationHandle = new QuickswitchOrientedNavHandle(mContext); 888 mOrientationHandle.setId(R.id.secondary_home_handle); 889 890 getBarTransitions().addDarkIntensityListener(mOrientationHandleIntensityListener); 891 mOrientationParams = new WindowManager.LayoutParams(0, 0, 892 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, 893 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 894 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 895 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 896 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 897 | WindowManager.LayoutParams.FLAG_SLIPPERY, 898 PixelFormat.TRANSLUCENT); 899 mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId()); 900 mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION 901 | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT; 902 mWindowManager.addView(mOrientationHandle, mOrientationParams); 903 mOrientationHandle.setVisibility(View.GONE); 904 mOrientationParams.setFitInsetsTypes(0 /* types*/); 905 mOrientationHandleGlobalLayoutListener = 906 () -> { 907 if (mStartingQuickSwitchRotation == -1) { 908 return; 909 } 910 911 RectF boundsOnScreen = mOrientationHandle.computeHomeHandleBounds(); 912 mOrientationHandle.mapRectFromViewToScreenCoords(boundsOnScreen, true); 913 Rect boundsRounded = new Rect(); 914 boundsOnScreen.roundOut(boundsRounded); 915 setOrientedHandleSamplingRegion(boundsRounded); 916 }; 917 mOrientationHandle.getViewTreeObserver().addOnGlobalLayoutListener( 918 mOrientationHandleGlobalLayoutListener); 919 } 920 orientSecondaryHomeHandle()921 private void orientSecondaryHomeHandle() { 922 if (!canShowSecondaryHandle()) { 923 return; 924 } 925 926 if (mStartingQuickSwitchRotation == -1) { 927 resetSecondaryHandle(); 928 } else { 929 int deltaRotation = deltaRotation(mCurrentRotation, mStartingQuickSwitchRotation); 930 if (mStartingQuickSwitchRotation == -1 || deltaRotation == -1) { 931 // Curious if starting quickswitch can change between the if check and our delta 932 Log.d(TAG, "secondary nav delta rotation: " + deltaRotation 933 + " current: " + mCurrentRotation 934 + " starting: " + mStartingQuickSwitchRotation); 935 } 936 int height = 0; 937 int width = 0; 938 Rect dispSize = mWindowManager.getCurrentWindowMetrics().getBounds(); 939 mOrientationHandle.setDeltaRotation(deltaRotation); 940 switch (deltaRotation) { 941 case Surface.ROTATION_90: 942 case Surface.ROTATION_270: 943 height = dispSize.height(); 944 width = mView.getHeight(); 945 break; 946 case Surface.ROTATION_180: 947 case Surface.ROTATION_0: 948 // TODO(b/152683657): Need to determine best UX for this 949 if (!mShowOrientedHandleForImmersiveMode) { 950 resetSecondaryHandle(); 951 return; 952 } 953 width = dispSize.width(); 954 height = mView.getHeight(); 955 break; 956 } 957 958 mOrientationParams.gravity = 959 deltaRotation == Surface.ROTATION_0 ? Gravity.BOTTOM : 960 (deltaRotation == Surface.ROTATION_90 ? Gravity.LEFT : Gravity.RIGHT); 961 mOrientationParams.height = height; 962 mOrientationParams.width = width; 963 mWindowManager.updateViewLayout(mOrientationHandle, mOrientationParams); 964 mView.setVisibility(View.GONE); 965 mOrientationHandle.setVisibility(View.VISIBLE); 966 } 967 } 968 resetSecondaryHandle()969 private void resetSecondaryHandle() { 970 if (mOrientationHandle != null) { 971 // Case where nav mode is changed w/o ever invoking a quickstep 972 // mOrientedHandle is initialized lazily 973 mOrientationHandle.setVisibility(View.GONE); 974 } 975 mView.setVisibility(View.VISIBLE); 976 setOrientedHandleSamplingRegion(null); 977 } 978 parseCurrentSysuiState()979 private void parseCurrentSysuiState() { 980 NavBarHelper.CurrentSysuiState state = mNavBarHelper.getCurrentSysuiState(); 981 if (state.mWindowStateDisplayId == mDisplayId) { 982 mNavigationBarWindowState = state.mWindowState; 983 } 984 } 985 reconfigureHomeLongClick()986 private void reconfigureHomeLongClick() { 987 if (mView.getHomeButton().getCurrentView() == null) { 988 return; 989 } 990 if (mHomeButtonLongPressDurationMs.isPresent() || !mLongPressHomeEnabled) { 991 mView.getHomeButton().getCurrentView().setLongClickable(false); 992 mView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false); 993 mView.getHomeButton().setOnLongClickListener(null); 994 } else { 995 mView.getHomeButton().getCurrentView().setLongClickable(true); 996 mView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(true); 997 mView.getHomeButton().setOnLongClickListener(this::onHomeLongClick); 998 } 999 } 1000 notifyNavigationBarSurface()1001 private void notifyNavigationBarSurface() { 1002 ViewRootImpl viewRoot = mView.getViewRootImpl(); 1003 SurfaceControl surface = viewRoot != null ? viewRoot.getSurfaceControl() : null; 1004 mOverviewProxyService.onNavigationBarSurfaceChanged(surface); 1005 } 1006 deltaRotation(int oldRotation, int newRotation)1007 private int deltaRotation(int oldRotation, int newRotation) { 1008 int delta = newRotation - oldRotation; 1009 if (delta < 0) delta += 4; 1010 return delta; 1011 } 1012 dump(PrintWriter pw)1013 public void dump(PrintWriter pw) { 1014 pw.println("NavigationBar (displayId=" + mDisplayId + "):"); 1015 pw.println(" mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation); 1016 pw.println(" mCurrentRotation=" + mCurrentRotation); 1017 pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs); 1018 pw.println(" mLongPressHomeEnabled=" + mLongPressHomeEnabled); 1019 pw.println(" mNavigationBarWindowState=" 1020 + windowStateToString(mNavigationBarWindowState)); 1021 pw.println(" mTransitionMode=" 1022 + BarTransitions.modeToString(mTransitionMode)); 1023 pw.println(" mTransientShown=" + mTransientShown); 1024 pw.println(" mTransientShownFromGestureOnSystemBar=" 1025 + mTransientShownFromGestureOnSystemBar); 1026 pw.println(" mScreenPinningActive=" + mScreenPinningActive); 1027 dumpBarTransitions(pw, "mNavigationBarView", getBarTransitions()); 1028 1029 pw.println(" mOrientedHandleSamplingRegion: " + mOrientedHandleSamplingRegion); 1030 mView.dump(pw); 1031 mRegionSamplingHelper.dump(pw); 1032 if (mAutoHideController != null) { 1033 mAutoHideController.dump(pw); 1034 } 1035 } 1036 1037 // ----- CommandQueue Callbacks ----- 1038 1039 @Override setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher)1040 public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, 1041 boolean showImeSwitcher) { 1042 if (displayId != mDisplayId) { 1043 return; 1044 } 1045 boolean imeShown = mNavBarHelper.isImeShown(vis); 1046 showImeSwitcher = imeShown && showImeSwitcher; 1047 int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition, 1048 imeShown, showImeSwitcher); 1049 if (hints == mNavigationIconHints) return; 1050 1051 setNavigationIconHints(hints); 1052 checkBarModes(); 1053 updateSystemUiStateFlags(); 1054 } 1055 1056 @Override setWindowState( int displayId, @WindowType int window, @WindowVisibleState int state)1057 public void setWindowState( 1058 int displayId, @WindowType int window, @WindowVisibleState int state) { 1059 if (displayId == mDisplayId 1060 && window == StatusBarManager.WINDOW_NAVIGATION_BAR 1061 && mNavigationBarWindowState != state) { 1062 mNavigationBarWindowState = state; 1063 updateSystemUiStateFlags(); 1064 mShowOrientedHandleForImmersiveMode = state == WINDOW_STATE_HIDDEN; 1065 if (mOrientationHandle != null 1066 && mStartingQuickSwitchRotation != -1) { 1067 orientSecondaryHomeHandle(); 1068 } 1069 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state)); 1070 setWindowVisible(isNavBarWindowVisible()); 1071 } 1072 } 1073 1074 @Override onRotationProposal(final int rotation, boolean isValid)1075 public void onRotationProposal(final int rotation, boolean isValid) { 1076 // The CommandQueue callbacks are added when the view is created to ensure we track other 1077 // states, but until the view is attached (at the next traversal), the view's display is 1078 // not valid. Just ignore the rotation in this case. 1079 if (!mView.isAttachedToWindow()) return; 1080 1081 final boolean rotateSuggestionsDisabled = RotationButtonController 1082 .hasDisable2RotateSuggestionFlag(mDisabledFlags2); 1083 final RotationButtonController rotationButtonController = 1084 mView.getRotationButtonController(); 1085 final RotationButton rotationButton = rotationButtonController.getRotationButton(); 1086 1087 if (RotationContextButton.DEBUG_ROTATION) { 1088 Log.v(TAG, "onRotationProposal proposedRotation=" + Surface.rotationToString(rotation) 1089 + ", isValid=" + isValid + ", mNavBarWindowState=" 1090 + StatusBarManager.windowStateToString(mNavigationBarWindowState) 1091 + ", rotateSuggestionsDisabled=" + rotateSuggestionsDisabled 1092 + ", isRotateButtonVisible=" + rotationButton.isVisible()); 1093 } 1094 1095 // Respect the disabled flag, no need for action as flag change callback will handle hiding 1096 if (rotateSuggestionsDisabled) return; 1097 1098 rotationButtonController.onRotationProposal(rotation, isValid); 1099 } 1100 1101 @Override onRecentsAnimationStateChanged(boolean running)1102 public void onRecentsAnimationStateChanged(boolean running) { 1103 mView.getRotationButtonController().setRecentsAnimationRunning(running); 1104 } 1105 1106 /** Restores the appearance and the transient saved state to {@link NavigationBar}. */ restoreAppearanceAndTransientState()1107 public void restoreAppearanceAndTransientState() { 1108 final int transitionMode = transitionMode(mTransientShown, mAppearance); 1109 mTransitionMode = transitionMode; 1110 checkNavBarModes(); 1111 if (mAutoHideController != null) { 1112 mAutoHideController.touchAutoHide(); 1113 } 1114 if (mLightBarController != null) { 1115 mLightBarController.onNavigationBarAppearanceChanged(mAppearance, 1116 true /* nbModeChanged */, transitionMode, false /* navbarColorManagedByIme */); 1117 } 1118 } 1119 1120 @Override onSystemBarAttributesChanged(int displayId, @Appearance int appearance, AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName, LetterboxDetails[] letterboxDetails)1121 public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, 1122 AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, 1123 @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName, 1124 LetterboxDetails[] letterboxDetails) { 1125 if (displayId != mDisplayId) { 1126 return; 1127 } 1128 boolean nbModeChanged = false; 1129 if (mAppearance != appearance) { 1130 mAppearance = appearance; 1131 nbModeChanged = updateTransitionMode(transitionMode(mTransientShown, appearance)); 1132 } 1133 if (mLightBarController != null) { 1134 mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged, 1135 mTransitionMode, navbarColorManagedByIme); 1136 } 1137 if (mBehavior != behavior) { 1138 mBehavior = behavior; 1139 mView.setBehavior(behavior); 1140 updateSystemUiStateFlags(); 1141 } 1142 } 1143 1144 @Override showTransient(int displayId, @InternalInsetsType int[] types, boolean isGestureOnSystemBar)1145 public void showTransient(int displayId, @InternalInsetsType int[] types, 1146 boolean isGestureOnSystemBar) { 1147 if (displayId != mDisplayId) { 1148 return; 1149 } 1150 if (!containsType(types, ITYPE_NAVIGATION_BAR)) { 1151 return; 1152 } 1153 if (!mTransientShown) { 1154 mTransientShown = true; 1155 mTransientShownFromGestureOnSystemBar = isGestureOnSystemBar; 1156 handleTransientChanged(); 1157 } 1158 } 1159 1160 @Override abortTransient(int displayId, @InternalInsetsType int[] types)1161 public void abortTransient(int displayId, @InternalInsetsType int[] types) { 1162 if (displayId != mDisplayId) { 1163 return; 1164 } 1165 if (!containsType(types, ITYPE_NAVIGATION_BAR)) { 1166 return; 1167 } 1168 clearTransient(); 1169 } 1170 clearTransient()1171 private void clearTransient() { 1172 if (mTransientShown) { 1173 mTransientShown = false; 1174 mTransientShownFromGestureOnSystemBar = false; 1175 handleTransientChanged(); 1176 } 1177 } 1178 handleTransientChanged()1179 private void handleTransientChanged() { 1180 mEdgeBackGestureHandler.onNavBarTransientStateChanged(mTransientShown); 1181 1182 final int transitionMode = transitionMode(mTransientShown, mAppearance); 1183 if (updateTransitionMode(transitionMode) && mLightBarController != null) { 1184 mLightBarController.onNavigationBarModeChanged(transitionMode); 1185 } 1186 } 1187 1188 // Returns true if the bar mode is changed. updateTransitionMode(int barMode)1189 private boolean updateTransitionMode(int barMode) { 1190 if (mTransitionMode != barMode) { 1191 mTransitionMode = barMode; 1192 checkNavBarModes(); 1193 if (mAutoHideController != null) { 1194 mAutoHideController.touchAutoHide(); 1195 } 1196 return true; 1197 } 1198 return false; 1199 } 1200 1201 @Override disable(int displayId, int state1, int state2, boolean animate)1202 public void disable(int displayId, int state1, int state2, boolean animate) { 1203 if (displayId != mDisplayId) { 1204 return; 1205 } 1206 // Navigation bar flags are in both state1 and state2. 1207 final int masked = state1 & (StatusBarManager.DISABLE_HOME 1208 | StatusBarManager.DISABLE_RECENT 1209 | StatusBarManager.DISABLE_BACK 1210 | StatusBarManager.DISABLE_SEARCH); 1211 if (masked != mDisabledFlags1) { 1212 mDisabledFlags1 = masked; 1213 mView.setDisabledFlags(state1, mSysUiFlagsContainer); 1214 updateScreenPinningGestures(); 1215 } 1216 1217 // Only default display supports rotation suggestions. 1218 if (mIsOnDefaultDisplay) { 1219 final int masked2 = state2 & (StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS); 1220 if (masked2 != mDisabledFlags2) { 1221 mDisabledFlags2 = masked2; 1222 setDisabled2Flags(masked2); 1223 } 1224 } 1225 } 1226 setDisabled2Flags(int state2)1227 private void setDisabled2Flags(int state2) { 1228 // Method only called on change of disable2 flags 1229 mView.getRotationButtonController().onDisable2FlagChanged(state2); 1230 } 1231 1232 // ----- Internal stuff ----- 1233 refreshLayout(int layoutDirection)1234 private void refreshLayout(int layoutDirection) { 1235 mView.setLayoutDirection(layoutDirection); 1236 } 1237 shouldDisableNavbarGestures()1238 private boolean shouldDisableNavbarGestures() { 1239 return !mDeviceProvisionedController.isDeviceProvisioned() 1240 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0; 1241 } 1242 repositionNavigationBar(int rotation)1243 private void repositionNavigationBar(int rotation) { 1244 if (mView == null || !mView.isAttachedToWindow()) return; 1245 1246 prepareNavigationBarView(); 1247 1248 mWindowManager.updateViewLayout(mFrame, getBarLayoutParams(rotation)); 1249 } 1250 updateScreenPinningGestures()1251 private void updateScreenPinningGestures() { 1252 // Change the cancel pin gesture to home and back if recents button is invisible 1253 ButtonDispatcher backButton = mView.getBackButton(); 1254 ButtonDispatcher recentsButton = mView.getRecentsButton(); 1255 if (mScreenPinningActive) { 1256 boolean recentsVisible = mView.isRecentsButtonVisible(); 1257 backButton.setOnLongClickListener(recentsVisible 1258 ? this::onLongPressBackRecents 1259 : this::onLongPressBackHome); 1260 recentsButton.setOnLongClickListener(this::onLongPressBackRecents); 1261 } else { 1262 backButton.setOnLongClickListener(null); 1263 recentsButton.setOnLongClickListener(null); 1264 } 1265 // Note, this needs to be set after even if we're setting the listener to null 1266 backButton.setLongClickable(mScreenPinningActive); 1267 recentsButton.setLongClickable(mScreenPinningActive); 1268 } 1269 notifyNavigationBarScreenOn()1270 private void notifyNavigationBarScreenOn() { 1271 mView.updateNavButtonIcons(); 1272 } 1273 prepareNavigationBarView()1274 private void prepareNavigationBarView() { 1275 mView.reorient(); 1276 1277 ButtonDispatcher recentsButton = mView.getRecentsButton(); 1278 recentsButton.setOnClickListener(this::onRecentsClick); 1279 recentsButton.setOnTouchListener(this::onRecentsTouch); 1280 1281 ButtonDispatcher homeButton = mView.getHomeButton(); 1282 homeButton.setOnTouchListener(this::onHomeTouch); 1283 1284 reconfigureHomeLongClick(); 1285 1286 ButtonDispatcher accessibilityButton = mView.getAccessibilityButton(); 1287 accessibilityButton.setOnClickListener(this::onAccessibilityClick); 1288 accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick); 1289 updateAccessibilityStateFlags(); 1290 1291 ButtonDispatcher imeSwitcherButton = mView.getImeSwitchButton(); 1292 imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick); 1293 1294 updateScreenPinningGestures(); 1295 } 1296 1297 @VisibleForTesting onHomeTouch(View v, MotionEvent event)1298 boolean onHomeTouch(View v, MotionEvent event) { 1299 if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) { 1300 return true; 1301 } 1302 // If an incoming call is ringing, HOME is totally disabled. 1303 // (The user is already on the InCallUI at this point, 1304 // and their ONLY options are to answer or reject the call.) 1305 final Optional<CentralSurfaces> centralSurfacesOptional = mCentralSurfacesOptionalLazy.get(); 1306 switch (event.getAction()) { 1307 case MotionEvent.ACTION_DOWN: 1308 mHomeBlockedThisTouch = false; 1309 if (mTelecomManagerOptional.isPresent() 1310 && mTelecomManagerOptional.get().isRinging()) { 1311 if (centralSurfacesOptional.map(CentralSurfaces::isKeyguardShowing) 1312 .orElse(false)) { 1313 Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " + 1314 "No heads up"); 1315 mHomeBlockedThisTouch = true; 1316 return true; 1317 } 1318 } 1319 if (mLongPressHomeEnabled) { 1320 mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> { 1321 mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration); 1322 }); 1323 } 1324 break; 1325 case MotionEvent.ACTION_UP: 1326 case MotionEvent.ACTION_CANCEL: 1327 mHandler.removeCallbacks(mOnVariableDurationHomeLongClick); 1328 centralSurfacesOptional.ifPresent(CentralSurfaces::awakenDreams); 1329 break; 1330 } 1331 return false; 1332 } 1333 onVerticalChanged(boolean isVertical)1334 private void onVerticalChanged(boolean isVertical) { 1335 Optional<CentralSurfaces> cs = mCentralSurfacesOptionalLazy.get(); 1336 if (cs.isPresent() && cs.get().getNotificationPanelViewController() != null) { 1337 cs.get().getNotificationPanelViewController().setQsScrimEnabled(!isVertical); 1338 } 1339 } 1340 onNavigationTouch(View v, MotionEvent event)1341 private boolean onNavigationTouch(View v, MotionEvent event) { 1342 if (mAutoHideController != null) { 1343 mAutoHideController.checkUserAutoHide(event); 1344 } 1345 return false; 1346 } 1347 1348 @VisibleForTesting onHomeLongClick(View v)1349 boolean onHomeLongClick(View v) { 1350 if (!mView.isRecentsButtonVisible() && mScreenPinningActive) { 1351 return onLongPressBackHome(v); 1352 } 1353 if (shouldDisableNavbarGestures()) { 1354 return false; 1355 } 1356 mMetricsLogger.action(MetricsEvent.ACTION_ASSIST_LONG_PRESS); 1357 mUiEventLogger.log(NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS); 1358 Bundle args = new Bundle(); 1359 args.putInt( 1360 AssistManager.INVOCATION_TYPE_KEY, 1361 AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS); 1362 mAssistManagerLazy.get().startAssist(args); 1363 mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::awakenDreams); 1364 mView.abortCurrentGesture(); 1365 return true; 1366 } 1367 1368 // additional optimization when we have software system buttons - start loading the recent 1369 // tasks on touch down onRecentsTouch(View v, MotionEvent event)1370 private boolean onRecentsTouch(View v, MotionEvent event) { 1371 int action = event.getAction() & MotionEvent.ACTION_MASK; 1372 if (action == MotionEvent.ACTION_DOWN) { 1373 mCommandQueue.preloadRecentApps(); 1374 } else if (action == MotionEvent.ACTION_CANCEL) { 1375 mCommandQueue.cancelPreloadRecentApps(); 1376 } else if (action == MotionEvent.ACTION_UP) { 1377 if (!v.isPressed()) { 1378 mCommandQueue.cancelPreloadRecentApps(); 1379 } 1380 } 1381 return false; 1382 } 1383 onRecentsClick(View v)1384 private void onRecentsClick(View v) { 1385 if (LatencyTracker.isEnabled(mContext)) { 1386 LatencyTracker.getInstance(mContext).onActionStart( 1387 LatencyTracker.ACTION_TOGGLE_RECENTS); 1388 } 1389 mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::awakenDreams); 1390 mCommandQueue.toggleRecentApps(); 1391 } 1392 onImeSwitcherClick(View v)1393 private void onImeSwitcherClick(View v) { 1394 mInputMethodManager.showInputMethodPickerFromSystem( 1395 true /* showAuxiliarySubtypes */, mDisplayId); 1396 mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP); 1397 }; 1398 onLongPressBackHome(View v)1399 private boolean onLongPressBackHome(View v) { 1400 return onLongPressNavigationButtons(v, R.id.back, R.id.home); 1401 } 1402 onLongPressBackRecents(View v)1403 private boolean onLongPressBackRecents(View v) { 1404 return onLongPressNavigationButtons(v, R.id.back, R.id.recent_apps); 1405 } 1406 1407 /** 1408 * This handles long-press of both back and recents/home. Back is the common button with 1409 * combination of recents if it is visible or home if recents is invisible. 1410 * They are handled together to capture them both being long-pressed 1411 * at the same time to exit screen pinning (lock task). 1412 * 1413 * When accessibility mode is on, only a long-press from recents/home 1414 * is required to exit. 1415 * 1416 * In all other circumstances we try to pass through long-press events 1417 * for Back, so that apps can still use it. Which can be from two things. 1418 * 1) Not currently in screen pinning (lock task). 1419 * 2) Back is long-pressed without recents/home. 1420 */ onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2)1421 private boolean onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2) { 1422 try { 1423 boolean sendBackLongPress = false; 1424 IActivityTaskManager activityManager = ActivityTaskManager.getService(); 1425 boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled(); 1426 boolean inLockTaskMode = activityManager.isInLockTaskMode(); 1427 boolean stopLockTaskMode = false; 1428 try { 1429 if (inLockTaskMode && !touchExplorationEnabled) { 1430 long time = System.currentTimeMillis(); 1431 1432 // If we recently long-pressed the other button then they were 1433 // long-pressed 'together' 1434 if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) { 1435 stopLockTaskMode = true; 1436 return true; 1437 } else if (v.getId() == btnId1) { 1438 ButtonDispatcher button = btnId2 == R.id.recent_apps 1439 ? mView.getRecentsButton() : mView.getHomeButton(); 1440 if (!button.getCurrentView().isPressed()) { 1441 // If we aren't pressing recents/home right now then they presses 1442 // won't be together, so send the standard long-press action. 1443 sendBackLongPress = true; 1444 } 1445 } 1446 mLastLockToAppLongPress = time; 1447 } else { 1448 // If this is back still need to handle sending the long-press event. 1449 if (v.getId() == btnId1) { 1450 sendBackLongPress = true; 1451 } else if (touchExplorationEnabled && inLockTaskMode) { 1452 // When in accessibility mode a long press that is recents/home (not back) 1453 // should stop lock task. 1454 stopLockTaskMode = true; 1455 return true; 1456 } else if (v.getId() == btnId2) { 1457 return btnId2 == R.id.recent_apps 1458 ? false 1459 : onHomeLongClick(mView.getHomeButton().getCurrentView()); 1460 } 1461 } 1462 } finally { 1463 if (stopLockTaskMode) { 1464 activityManager.stopSystemLockTaskMode(); 1465 // When exiting refresh disabled flags. 1466 mView.updateNavButtonIcons(); 1467 } 1468 } 1469 1470 if (sendBackLongPress) { 1471 KeyButtonView keyButtonView = (KeyButtonView) v; 1472 keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS); 1473 keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); 1474 return true; 1475 } 1476 } catch (RemoteException e) { 1477 Log.d(TAG, "Unable to reach activity manager", e); 1478 } 1479 return false; 1480 } 1481 onAccessibilityClick(View v)1482 private void onAccessibilityClick(View v) { 1483 final Display display = v.getDisplay(); 1484 mAccessibilityManager.notifyAccessibilityButtonClicked( 1485 display != null ? display.getDisplayId() : mDisplayTracker.getDefaultDisplayId()); 1486 } 1487 onAccessibilityLongClick(View v)1488 private boolean onAccessibilityLongClick(View v) { 1489 final Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON); 1490 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 1491 final String chooserClassName = AccessibilityButtonChooserActivity.class.getName(); 1492 intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName); 1493 mContext.startActivityAsUser(intent, mUserTracker.getUserHandle()); 1494 return true; 1495 } 1496 updateAccessibilityStateFlags()1497 void updateAccessibilityStateFlags() { 1498 mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled(); 1499 if (mView != null) { 1500 int a11yFlags = mNavBarHelper.getA11yButtonState(); 1501 boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; 1502 boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; 1503 mView.setAccessibilityButtonState(clickable, longClickable); 1504 } 1505 updateSystemUiStateFlags(); 1506 } 1507 updateSystemUiStateFlags()1508 public void updateSystemUiStateFlags() { 1509 int a11yFlags = mNavBarHelper.getA11yButtonState(); 1510 boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; 1511 boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; 1512 1513 mSysUiFlagsContainer.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable) 1514 .setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable) 1515 .setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible()) 1516 .setFlag(SYSUI_STATE_IME_SHOWING, 1517 (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0) 1518 .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING, 1519 (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0) 1520 .setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY, 1521 allowSystemGestureIgnoringBarVisibility()) 1522 .commitUpdate(mDisplayId); 1523 } 1524 updateAssistantEntrypoints(boolean assistantAvailable, boolean longPressHomeEnabled)1525 private void updateAssistantEntrypoints(boolean assistantAvailable, 1526 boolean longPressHomeEnabled) { 1527 if (mOverviewProxyService.getProxy() != null) { 1528 try { 1529 mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable, 1530 longPressHomeEnabled); 1531 } catch (RemoteException e) { 1532 Log.w(TAG, "Unable to send assistant availability data to launcher"); 1533 } 1534 } 1535 reconfigureHomeLongClick(); 1536 } 1537 1538 // ----- Methods that DisplayNavigationBarController talks to ----- 1539 1540 /** Applies auto dimming animation on navigation bar when touched. */ touchAutoDim()1541 public void touchAutoDim() { 1542 getBarTransitions().setAutoDim(false); 1543 mHandler.removeCallbacks(mAutoDim); 1544 int state = mStatusBarStateController.getState(); 1545 if (state != StatusBarState.KEYGUARD && state != StatusBarState.SHADE_LOCKED) { 1546 mHandler.postDelayed(mAutoDim, AUTODIM_TIMEOUT_MS); 1547 } 1548 } 1549 setLightBarController(LightBarController lightBarController)1550 public void setLightBarController(LightBarController lightBarController) { 1551 mLightBarController = lightBarController; 1552 if (mLightBarController != null) { 1553 mLightBarController.setNavigationBar( 1554 getBarTransitions().getLightTransitionsController()); 1555 } 1556 } 1557 setWindowVisible(boolean visible)1558 private void setWindowVisible(boolean visible) { 1559 mRegionSamplingHelper.setWindowVisible(visible); 1560 mView.setWindowVisible(visible); 1561 } 1562 1563 /** Sets {@link AutoHideController} to the navigation bar. */ setAutoHideController(AutoHideController autoHideController)1564 private void setAutoHideController(AutoHideController autoHideController) { 1565 mAutoHideController = autoHideController; 1566 if (mAutoHideController != null) { 1567 mAutoHideController.setNavigationBar(mAutoHideUiElement); 1568 } 1569 mView.setAutoHideController(autoHideController); 1570 } 1571 isTransientShown()1572 private boolean isTransientShown() { 1573 return mTransientShown; 1574 } 1575 checkBarModes()1576 private void checkBarModes() { 1577 // We only have status bar on default display now. 1578 if (mIsOnDefaultDisplay) { 1579 mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::checkBarModes); 1580 } else { 1581 checkNavBarModes(); 1582 } 1583 } 1584 isNavBarWindowVisible()1585 public boolean isNavBarWindowVisible() { 1586 return mNavigationBarWindowState == WINDOW_STATE_SHOWING; 1587 } 1588 allowSystemGestureIgnoringBarVisibility()1589 private boolean allowSystemGestureIgnoringBarVisibility() { 1590 return mBehavior != BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 1591 } 1592 1593 /** 1594 * Checks current navigation bar mode and make transitions. 1595 */ checkNavBarModes()1596 public void checkNavBarModes() { 1597 final boolean anim = 1598 mCentralSurfacesOptionalLazy.get().map(CentralSurfaces::isDeviceInteractive) 1599 .orElse(false) 1600 && mNavigationBarWindowState != WINDOW_STATE_HIDDEN; 1601 getBarTransitions().transitionTo(mTransitionMode, anim); 1602 } 1603 disableAnimationsDuringHide(long delay)1604 public void disableAnimationsDuringHide(long delay) { 1605 mView.setLayoutTransitionsEnabled(false); 1606 mHandler.postDelayed(mEnableLayoutTransitions, 1607 delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE); 1608 } 1609 1610 /** 1611 * Performs transitions on navigation bar. 1612 * 1613 * @param barMode transition bar mode. 1614 * @param animate shows animations if {@code true}. 1615 */ transitionTo(@ransitionMode int barMode, boolean animate)1616 public void transitionTo(@TransitionMode int barMode, boolean animate) { 1617 getBarTransitions().transitionTo(barMode, animate); 1618 } 1619 getBarTransitions()1620 public NavigationBarTransitions getBarTransitions() { 1621 return mNavigationBarTransitions; 1622 } 1623 finishBarAnimations()1624 public void finishBarAnimations() { 1625 getBarTransitions().finishAnimations(); 1626 } 1627 getBarLayoutParams(int rotation)1628 private WindowManager.LayoutParams getBarLayoutParams(int rotation) { 1629 WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation); 1630 lp.paramsForRotation = new WindowManager.LayoutParams[4]; 1631 for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) { 1632 lp.paramsForRotation[rot] = getBarLayoutParamsForRotation(rot); 1633 } 1634 return lp; 1635 } 1636 getBarLayoutParamsForRotation(int rotation)1637 private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) { 1638 int width = WindowManager.LayoutParams.MATCH_PARENT; 1639 int height = WindowManager.LayoutParams.MATCH_PARENT; 1640 int insetsHeight = -1; 1641 int gravity = Gravity.BOTTOM; 1642 boolean navBarCanMove = true; 1643 final Context userContext = mUserContextProvider.createCurrentUserContext(mContext); 1644 if (mWindowManager != null && mWindowManager.getCurrentWindowMetrics() != null) { 1645 Rect displaySize = mWindowManager.getCurrentWindowMetrics().getBounds(); 1646 navBarCanMove = displaySize.width() != displaySize.height() 1647 && userContext.getResources().getBoolean( 1648 com.android.internal.R.bool.config_navBarCanMove); 1649 } 1650 if (!navBarCanMove) { 1651 height = userContext.getResources().getDimensionPixelSize( 1652 com.android.internal.R.dimen.navigation_bar_frame_height); 1653 insetsHeight = userContext.getResources().getDimensionPixelSize( 1654 com.android.internal.R.dimen.navigation_bar_height); 1655 } else { 1656 switch (rotation) { 1657 case ROTATION_UNDEFINED: 1658 case Surface.ROTATION_0: 1659 case Surface.ROTATION_180: 1660 height = userContext.getResources().getDimensionPixelSize( 1661 com.android.internal.R.dimen.navigation_bar_frame_height); 1662 insetsHeight = userContext.getResources().getDimensionPixelSize( 1663 com.android.internal.R.dimen.navigation_bar_height); 1664 break; 1665 case Surface.ROTATION_90: 1666 gravity = Gravity.RIGHT; 1667 width = userContext.getResources().getDimensionPixelSize( 1668 com.android.internal.R.dimen.navigation_bar_width); 1669 break; 1670 case Surface.ROTATION_270: 1671 gravity = Gravity.LEFT; 1672 width = userContext.getResources().getDimensionPixelSize( 1673 com.android.internal.R.dimen.navigation_bar_width); 1674 break; 1675 } 1676 } 1677 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 1678 width, 1679 height, 1680 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, 1681 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING 1682 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 1683 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 1684 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH 1685 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH 1686 | WindowManager.LayoutParams.FLAG_SLIPPERY, 1687 PixelFormat.TRANSLUCENT); 1688 lp.gravity = gravity; 1689 if (insetsHeight != -1) { 1690 lp.providedInsets = new InsetsFrameProvider[] { 1691 new InsetsFrameProvider(ITYPE_NAVIGATION_BAR, Insets.of(0, 0, 0, insetsHeight)) 1692 }; 1693 } else { 1694 lp.providedInsets = new InsetsFrameProvider[] { 1695 new InsetsFrameProvider(ITYPE_NAVIGATION_BAR) 1696 }; 1697 } 1698 lp.token = new Binder(); 1699 lp.accessibilityTitle = userContext.getString(R.string.nav_bar); 1700 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC 1701 | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT; 1702 lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 1703 lp.windowAnimations = 0; 1704 lp.setTitle("NavigationBar" + userContext.getDisplayId()); 1705 lp.setFitInsetsTypes(0 /* types */); 1706 lp.setTrustedOverlay(); 1707 return lp; 1708 } 1709 canShowSecondaryHandle()1710 private boolean canShowSecondaryHandle() { 1711 return mNavBarMode == NAV_BAR_MODE_GESTURAL && mOrientationHandle != null; 1712 } 1713 1714 private final UserTracker.Callback mUserChangedCallback = 1715 new UserTracker.Callback() { 1716 @Override 1717 public void onUserChanged(int newUser, @NonNull Context userContext) { 1718 // The accessibility settings may be different for the new user 1719 updateAccessibilityStateFlags(); 1720 } 1721 }; 1722 1723 @VisibleForTesting getNavigationIconHints()1724 int getNavigationIconHints() { 1725 return mNavigationIconHints; 1726 } 1727 setNavigationIconHints(int hints)1728 private void setNavigationIconHints(int hints) { 1729 if (hints == mNavigationIconHints) return; 1730 if (!isLargeScreen(mContext)) { 1731 // All IME functions handled by launcher via Sysui flags for large screen 1732 final boolean newBackAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 1733 final boolean oldBackAlt = 1734 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 1735 if (newBackAlt != oldBackAlt) { 1736 mView.onImeVisibilityChanged(newBackAlt); 1737 mImeVisible = newBackAlt; 1738 } 1739 1740 mView.setNavigationIconHints(hints); 1741 } 1742 if (DEBUG) { 1743 android.widget.Toast.makeText(mContext, 1744 "Navigation icon hints = " + hints, 1745 500).show(); 1746 } 1747 mNavigationIconHints = hints; 1748 } 1749 1750 /** 1751 * @param includeFloatingButtons Whether to include the floating rotation and overlay button in 1752 * the region for all the buttons 1753 * @param inScreenSpace Whether to return values in screen space or window space 1754 * @param useNearestRegion Whether to use the nearest region instead of the actual button bounds 1755 * @return 1756 */ getButtonLocations(boolean includeFloatingButtons, boolean inScreenSpace, boolean useNearestRegion)1757 Region getButtonLocations(boolean includeFloatingButtons, boolean inScreenSpace, 1758 boolean useNearestRegion) { 1759 if (useNearestRegion && !inScreenSpace) { 1760 // We currently don't support getting the nearest region in anything but screen space 1761 useNearestRegion = false; 1762 } 1763 Region region = new Region(); 1764 Map<View, Rect> touchRegionCache = mView.getButtonTouchRegionCache(); 1765 updateButtonLocation( 1766 region, touchRegionCache, mView.getBackButton(), inScreenSpace, useNearestRegion); 1767 updateButtonLocation( 1768 region, touchRegionCache, mView.getHomeButton(), inScreenSpace, useNearestRegion); 1769 updateButtonLocation(region, touchRegionCache, mView.getRecentsButton(), inScreenSpace, 1770 useNearestRegion); 1771 updateButtonLocation(region, touchRegionCache, mView.getImeSwitchButton(), inScreenSpace, 1772 useNearestRegion); 1773 updateButtonLocation( 1774 region, touchRegionCache, mView.getAccessibilityButton(), inScreenSpace, 1775 useNearestRegion); 1776 if (includeFloatingButtons && mView.getFloatingRotationButton().isVisible()) { 1777 // Note: this button is floating so the nearest region doesn't apply 1778 updateButtonLocation( 1779 region, mView.getFloatingRotationButton().getCurrentView(), inScreenSpace); 1780 } else { 1781 updateButtonLocation(region, touchRegionCache, mView.getRotateSuggestionButton(), 1782 inScreenSpace, useNearestRegion); 1783 } 1784 return region; 1785 } 1786 updateButtonLocation( Region region, Map<View, Rect> touchRegionCache, ButtonDispatcher button, boolean inScreenSpace, boolean useNearestRegion)1787 private void updateButtonLocation( 1788 Region region, 1789 Map<View, Rect> touchRegionCache, 1790 ButtonDispatcher button, 1791 boolean inScreenSpace, 1792 boolean useNearestRegion) { 1793 if (button == null) { 1794 return; 1795 } 1796 View view = button.getCurrentView(); 1797 if (view == null || !button.isVisible()) { 1798 return; 1799 } 1800 // If the button is tappable from perspective of NearestTouchFrame, then we'll 1801 // include the regions where the tap is valid instead of just the button layout location 1802 if (useNearestRegion && touchRegionCache.containsKey(view)) { 1803 region.op(touchRegionCache.get(view), Region.Op.UNION); 1804 return; 1805 } 1806 updateButtonLocation(region, view, inScreenSpace); 1807 } 1808 updateButtonLocation(Region region, View view, boolean inScreenSpace)1809 private void updateButtonLocation(Region region, View view, boolean inScreenSpace) { 1810 Rect bounds = new Rect(); 1811 if (inScreenSpace) { 1812 view.getBoundsOnScreen(bounds); 1813 } else { 1814 int[] location = new int[2]; 1815 view.getLocationInWindow(location); 1816 bounds.set(location[0], location[1], 1817 location[0] + view.getWidth(), 1818 location[1] + view.getHeight()); 1819 } 1820 region.op(bounds, Region.Op.UNION); 1821 } 1822 setOrientedHandleSamplingRegion(Rect orientedHandleSamplingRegion)1823 void setOrientedHandleSamplingRegion(Rect orientedHandleSamplingRegion) { 1824 mOrientedHandleSamplingRegion = orientedHandleSamplingRegion; 1825 mRegionSamplingHelper.updateSamplingRect(); 1826 } 1827 calculateSamplingRect()1828 private Rect calculateSamplingRect() { 1829 mSamplingBounds.setEmpty(); 1830 // TODO: Extend this to 2/3 button layout as well 1831 View view = mView.getHomeHandle().getCurrentView(); 1832 1833 if (view != null) { 1834 int[] pos = new int[2]; 1835 view.getLocationOnScreen(pos); 1836 Point displaySize = new Point(); 1837 view.getContext().getDisplay().getRealSize(displaySize); 1838 final Rect samplingBounds = new Rect(pos[0] - mNavColorSampleMargin, 1839 displaySize.y - mView.getNavBarHeight(), 1840 pos[0] + view.getWidth() + mNavColorSampleMargin, 1841 displaySize.y); 1842 mSamplingBounds.set(samplingBounds); 1843 } 1844 1845 return mSamplingBounds; 1846 } 1847 setNavigationBarLumaSamplingEnabled(boolean enable)1848 void setNavigationBarLumaSamplingEnabled(boolean enable) { 1849 if (enable) { 1850 mRegionSamplingHelper.start(mSamplingBounds); 1851 } else { 1852 mRegionSamplingHelper.stop(); 1853 } 1854 } 1855 setNavBarMode(int mode)1856 private void setNavBarMode(int mode) { 1857 mView.setNavBarMode(mode, mNavigationModeController.getImeDrawsImeNavBar()); 1858 if (isGesturalMode(mode)) { 1859 mRegionSamplingHelper.start(mSamplingBounds); 1860 } else { 1861 mRegionSamplingHelper.stop(); 1862 } 1863 } 1864 onBarTransition(int newMode)1865 void onBarTransition(int newMode) { 1866 if (newMode == MODE_OPAQUE) { 1867 // If the nav bar background is opaque, stop auto tinting since we know the icons are 1868 // showing over a dark background 1869 mRegionSamplingHelper.stop(); 1870 getBarTransitions().getLightTransitionsController().setIconsDark( 1871 false /* dark */, true /* animate */); 1872 } else { 1873 mRegionSamplingHelper.start(mSamplingBounds); 1874 } 1875 } 1876 1877 private final ModeChangedListener mModeChangedListener = new ModeChangedListener() { 1878 @Override 1879 public void onNavigationModeChanged(int mode) { 1880 mNavBarMode = mode; 1881 1882 if (!QuickStepContract.isGesturalMode(mode)) { 1883 // Reset the override alpha 1884 if (getBarTransitions() != null) { 1885 getBarTransitions().setBackgroundOverrideAlpha(1f); 1886 } 1887 } 1888 updateScreenPinningGestures(); 1889 1890 if (!canShowSecondaryHandle()) { 1891 resetSecondaryHandle(); 1892 } 1893 setNavBarMode(mode); 1894 mView.setShouldShowSwipeUpUi(mOverviewProxyService.shouldShowSwipeUpUI()); 1895 } 1896 }; 1897 1898 private final Gefingerpoken mTouchHandler = new Gefingerpoken() { 1899 private boolean mDeadZoneConsuming; 1900 1901 @Override 1902 public boolean onInterceptTouchEvent(MotionEvent ev) { 1903 if (isGesturalMode(mNavBarMode) && mImeVisible 1904 && ev.getAction() == MotionEvent.ACTION_DOWN) { 1905 SysUiStatsLog.write(SysUiStatsLog.IME_TOUCH_REPORTED, 1906 (int) ev.getX(), (int) ev.getY()); 1907 } 1908 return shouldDeadZoneConsumeTouchEvents(ev); 1909 } 1910 1911 @Override 1912 public boolean onTouchEvent(MotionEvent ev) { 1913 shouldDeadZoneConsumeTouchEvents(ev); 1914 return false; 1915 } 1916 1917 private boolean shouldDeadZoneConsumeTouchEvents(MotionEvent event) { 1918 int action = event.getActionMasked(); 1919 if (action == MotionEvent.ACTION_DOWN) { 1920 mDeadZoneConsuming = false; 1921 } 1922 if (mDeadZone.onTouchEvent(event) || mDeadZoneConsuming) { 1923 switch (action) { 1924 case MotionEvent.ACTION_DOWN: 1925 // Allow gestures starting in the deadzone to be slippery 1926 mView.setSlippery(true); 1927 mDeadZoneConsuming = true; 1928 break; 1929 case MotionEvent.ACTION_CANCEL: 1930 case MotionEvent.ACTION_UP: 1931 // When a gesture started in the deadzone is finished, restore 1932 // slippery state 1933 mView.updateSlippery(); 1934 mDeadZoneConsuming = false; 1935 break; 1936 } 1937 return true; 1938 } 1939 return false; 1940 } 1941 }; 1942 } 1943