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