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.inputmethodservice.InputMethodService.canImeRenderGesturalNavButtons; 20 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; 21 22 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED; 23 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; 24 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SEARCH_DISABLED; 25 import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode; 26 27 import android.animation.LayoutTransition; 28 import android.animation.LayoutTransition.TransitionListener; 29 import android.animation.ObjectAnimator; 30 import android.animation.PropertyValuesHolder; 31 import android.animation.TimeInterpolator; 32 import android.animation.ValueAnimator; 33 import android.annotation.DrawableRes; 34 import android.app.StatusBarManager; 35 import android.content.Context; 36 import android.content.res.Configuration; 37 import android.graphics.Canvas; 38 import android.graphics.Point; 39 import android.graphics.Rect; 40 import android.os.Bundle; 41 import android.os.RemoteException; 42 import android.util.AttributeSet; 43 import android.util.Log; 44 import android.util.SparseArray; 45 import android.view.ContextThemeWrapper; 46 import android.view.Display; 47 import android.view.MotionEvent; 48 import android.view.Surface; 49 import android.view.View; 50 import android.view.ViewGroup; 51 import android.view.WindowInsets; 52 import android.view.WindowInsetsController.Behavior; 53 import android.view.WindowManager; 54 import android.view.WindowManagerGlobal; 55 import android.view.accessibility.AccessibilityNodeInfo; 56 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; 57 import android.widget.FrameLayout; 58 59 import androidx.annotation.Nullable; 60 61 import com.android.internal.annotations.VisibleForTesting; 62 import com.android.settingslib.Utils; 63 import com.android.systemui.Gefingerpoken; 64 import com.android.systemui.R; 65 import com.android.systemui.animation.Interpolators; 66 import com.android.systemui.model.SysUiState; 67 import com.android.systemui.navigationbar.buttons.ButtonDispatcher; 68 import com.android.systemui.navigationbar.buttons.ContextualButton; 69 import com.android.systemui.navigationbar.buttons.ContextualButtonGroup; 70 import com.android.systemui.navigationbar.buttons.DeadZone; 71 import com.android.systemui.navigationbar.buttons.KeyButtonDrawable; 72 import com.android.systemui.navigationbar.buttons.NearestTouchFrame; 73 import com.android.systemui.navigationbar.buttons.RotationContextButton; 74 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler; 75 import com.android.systemui.recents.Recents; 76 import com.android.systemui.settings.DisplayTracker; 77 import com.android.systemui.shade.NotificationPanelViewController; 78 import com.android.systemui.shared.rotation.FloatingRotationButton; 79 import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback; 80 import com.android.systemui.shared.rotation.RotationButtonController; 81 import com.android.systemui.shared.system.QuickStepContract; 82 import com.android.systemui.statusbar.phone.AutoHideController; 83 import com.android.systemui.statusbar.phone.CentralSurfaces; 84 import com.android.systemui.statusbar.phone.LightBarTransitionsController; 85 import com.android.wm.shell.back.BackAnimation; 86 import com.android.wm.shell.pip.Pip; 87 88 import java.io.PrintWriter; 89 import java.util.Map; 90 import java.util.Optional; 91 import java.util.concurrent.Executor; 92 import java.util.function.Consumer; 93 94 /** */ 95 public class NavigationBarView extends FrameLayout { 96 final static boolean DEBUG = false; 97 final static String TAG = "NavBarView"; 98 99 final static boolean ALTERNATE_CAR_MODE_UI = false; 100 101 private Executor mBgExecutor; 102 103 // The current view is one of mHorizontal or mVertical depending on the current configuration 104 View mCurrentView = null; 105 private View mVertical; 106 private View mHorizontal; 107 108 /** Indicates that navigation bar is vertical. */ 109 private boolean mIsVertical; 110 private int mCurrentRotation = -1; 111 112 boolean mLongClickableAccessibilityButton; 113 int mDisabledFlags = 0; 114 int mNavigationIconHints = 0; 115 private int mNavBarMode; 116 private boolean mImeDrawsImeNavBar; 117 118 private KeyButtonDrawable mBackIcon; 119 private KeyButtonDrawable mHomeDefaultIcon; 120 private KeyButtonDrawable mRecentIcon; 121 private KeyButtonDrawable mDockedIcon; 122 private Context mLightContext; 123 private int mLightIconColor; 124 private int mDarkIconColor; 125 126 private EdgeBackGestureHandler mEdgeBackGestureHandler; 127 private DisplayTracker mDisplayTracker; 128 private final DeadZone mDeadZone; 129 private NavigationBarTransitions mBarTransitions; 130 @Nullable 131 private AutoHideController mAutoHideController; 132 133 // performs manual animation in sync with layout transitions 134 private final NavTransitionListener mTransitionListener = new NavTransitionListener(); 135 136 private OnVerticalChangedListener mOnVerticalChangedListener; 137 private boolean mLayoutTransitionsEnabled = true; 138 private boolean mWakeAndUnlocking; 139 private boolean mUseCarModeUi = false; 140 private boolean mInCarMode = false; 141 private boolean mDockedStackExists; 142 private boolean mScreenOn = true; 143 144 private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>(); 145 private final ContextualButtonGroup mContextualButtonGroup; 146 private Configuration mConfiguration; 147 private Configuration mTmpLastConfiguration; 148 149 private NavigationBarInflaterView mNavigationInflaterView; 150 private Optional<Recents> mRecentsOptional = Optional.empty(); 151 @Nullable 152 private NotificationPanelViewController mPanelView; 153 private RotationContextButton mRotationContextButton; 154 private FloatingRotationButton mFloatingRotationButton; 155 private RotationButtonController mRotationButtonController; 156 157 /** 158 * Helper that is responsible for showing the right toast when a disallowed activity operation 159 * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in 160 * fully locked mode we only show that unlocking is blocked. 161 */ 162 private ScreenPinningNotify mScreenPinningNotify; 163 private boolean mScreenPinningActive = false; 164 165 /** 166 * {@code true} if the IME can render the back button and the IME switcher button. 167 * 168 * <p>The value must be used when and only when 169 * {@link com.android.systemui.shared.system.QuickStepContract#isGesturalMode(int)} returns 170 * {@code true}</p> 171 * 172 * <p>Cache the value here for better performance.</p> 173 */ 174 private final boolean mImeCanRenderGesturalNavButtons = canImeRenderGesturalNavButtons(); 175 private Gefingerpoken mTouchHandler; 176 private boolean mOverviewProxyEnabled; 177 private boolean mShowSwipeUpUi; 178 private UpdateActiveTouchRegionsCallback mUpdateActiveTouchRegionsCallback; 179 180 private class NavTransitionListener implements TransitionListener { 181 private boolean mBackTransitioning; 182 private boolean mHomeAppearing; 183 private long mStartDelay; 184 private long mDuration; 185 private TimeInterpolator mInterpolator; 186 187 @Override startTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType)188 public void startTransition(LayoutTransition transition, ViewGroup container, 189 View view, int transitionType) { 190 if (view.getId() == R.id.back) { 191 mBackTransitioning = true; 192 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) { 193 mHomeAppearing = true; 194 mStartDelay = transition.getStartDelay(transitionType); 195 mDuration = transition.getDuration(transitionType); 196 mInterpolator = transition.getInterpolator(transitionType); 197 } 198 } 199 200 @Override endTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType)201 public void endTransition(LayoutTransition transition, ViewGroup container, 202 View view, int transitionType) { 203 if (view.getId() == R.id.back) { 204 mBackTransitioning = false; 205 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) { 206 mHomeAppearing = false; 207 } 208 } 209 onBackAltCleared()210 public void onBackAltCleared() { 211 ButtonDispatcher backButton = getBackButton(); 212 213 // When dismissing ime during unlock, force the back button to run the same appearance 214 // animation as home (if we catch this condition early enough). 215 if (!mBackTransitioning && backButton.getVisibility() == VISIBLE 216 && mHomeAppearing && getHomeButton().getAlpha() == 0) { 217 getBackButton().setAlpha(0); 218 ValueAnimator a = ObjectAnimator.ofFloat(backButton, "alpha", 0, 1); 219 a.setStartDelay(mStartDelay); 220 a.setDuration(mDuration); 221 a.setInterpolator(mInterpolator); 222 a.start(); 223 } 224 } 225 } 226 227 private final AccessibilityDelegate mQuickStepAccessibilityDelegate = 228 new AccessibilityDelegate() { 229 private AccessibilityAction mToggleOverviewAction; 230 231 @Override 232 public void onInitializeAccessibilityNodeInfo(View host, 233 AccessibilityNodeInfo info) { 234 super.onInitializeAccessibilityNodeInfo(host, info); 235 if (mToggleOverviewAction == null) { 236 mToggleOverviewAction = new AccessibilityAction( 237 R.id.action_toggle_overview, getContext().getString( 238 R.string.quick_step_accessibility_toggle_overview)); 239 } 240 info.addAction(mToggleOverviewAction); 241 } 242 243 @Override 244 public boolean performAccessibilityAction(View host, int action, Bundle args) { 245 if (action == R.id.action_toggle_overview) { 246 mRecentsOptional.ifPresent(Recents::toggleRecentApps); 247 } else { 248 return super.performAccessibilityAction(host, action, args); 249 } 250 return true; 251 } 252 }; 253 254 private final RotationButtonUpdatesCallback mRotationButtonListener = 255 new RotationButtonUpdatesCallback() { 256 @Override 257 public void onVisibilityChanged(boolean visible) { 258 if (visible && mAutoHideController != null) { 259 // If the button will actually become visible and the navbar is about 260 // to hide, tell the statusbar to keep it around for longer 261 mAutoHideController.touchAutoHide(); 262 } 263 notifyActiveTouchRegions(); 264 } 265 266 @Override 267 public void onPositionChanged() { 268 notifyActiveTouchRegions(); 269 } 270 }; 271 NavigationBarView(Context context, AttributeSet attrs)272 public NavigationBarView(Context context, AttributeSet attrs) { 273 super(context, attrs); 274 275 final Context darkContext = new ContextThemeWrapper(context, 276 Utils.getThemeAttr(context, R.attr.darkIconTheme)); 277 mLightContext = new ContextThemeWrapper(context, 278 Utils.getThemeAttr(context, R.attr.lightIconTheme)); 279 mLightIconColor = Utils.getColorAttrDefaultColor(mLightContext, R.attr.singleToneColor); 280 mDarkIconColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor); 281 mIsVertical = false; 282 mLongClickableAccessibilityButton = false; 283 284 // Set up the context group of buttons 285 mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container); 286 final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher, 287 mLightContext, R.drawable.ic_ime_switcher_default); 288 final ContextualButton accessibilityButton = 289 new ContextualButton(R.id.accessibility_button, mLightContext, 290 R.drawable.ic_sysbar_accessibility_button); 291 mContextualButtonGroup.addButton(imeSwitcherButton); 292 mContextualButtonGroup.addButton(accessibilityButton); 293 mRotationContextButton = new RotationContextButton(R.id.rotate_suggestion, 294 mLightContext, R.drawable.ic_sysbar_rotate_button_ccw_start_0); 295 mFloatingRotationButton = new FloatingRotationButton(mContext, 296 R.string.accessibility_rotate_button, 297 R.layout.rotate_suggestion, 298 R.id.rotate_suggestion, 299 R.dimen.floating_rotation_button_min_margin, 300 R.dimen.rounded_corner_content_padding, 301 R.dimen.floating_rotation_button_taskbar_left_margin, 302 R.dimen.floating_rotation_button_taskbar_bottom_margin, 303 R.dimen.floating_rotation_button_diameter, 304 R.dimen.key_button_ripple_max_width, 305 R.bool.floating_rotation_button_position_left); 306 mRotationButtonController = new RotationButtonController(mLightContext, mLightIconColor, 307 mDarkIconColor, R.drawable.ic_sysbar_rotate_button_ccw_start_0, 308 R.drawable.ic_sysbar_rotate_button_ccw_start_90, 309 R.drawable.ic_sysbar_rotate_button_cw_start_0, 310 R.drawable.ic_sysbar_rotate_button_cw_start_90, 311 () -> mCurrentRotation); 312 313 mConfiguration = new Configuration(); 314 mTmpLastConfiguration = new Configuration(); 315 mConfiguration.updateFrom(context.getResources().getConfiguration()); 316 317 mScreenPinningNotify = new ScreenPinningNotify(mContext); 318 319 mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back)); 320 mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home)); 321 mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle)); 322 mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps)); 323 mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton); 324 mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton); 325 mButtonDispatchers.put(R.id.menu_container, mContextualButtonGroup); 326 mDeadZone = new DeadZone(this); 327 } 328 setEdgeBackGestureHandler(EdgeBackGestureHandler edgeBackGestureHandler)329 public void setEdgeBackGestureHandler(EdgeBackGestureHandler edgeBackGestureHandler) { 330 mEdgeBackGestureHandler = edgeBackGestureHandler; 331 } 332 setBarTransitions(NavigationBarTransitions navigationBarTransitions)333 void setBarTransitions(NavigationBarTransitions navigationBarTransitions) { 334 mBarTransitions = navigationBarTransitions; 335 } 336 setAutoHideController(AutoHideController autoHideController)337 public void setAutoHideController(AutoHideController autoHideController) { 338 mAutoHideController = autoHideController; 339 } 340 getLightTransitionsController()341 public LightBarTransitionsController getLightTransitionsController() { 342 return mBarTransitions.getLightTransitionsController(); 343 } 344 setComponents(Optional<Recents> recentsOptional)345 public void setComponents(Optional<Recents> recentsOptional) { 346 mRecentsOptional = recentsOptional; 347 } 348 setComponents(NotificationPanelViewController panel)349 public void setComponents(NotificationPanelViewController panel) { 350 mPanelView = panel; 351 updatePanelSystemUiStateFlags(); 352 } 353 setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener)354 public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) { 355 mOnVerticalChangedListener = onVerticalChangedListener; 356 notifyVerticalChangedListener(mIsVertical); 357 } 358 setBackgroundExecutor(Executor bgExecutor)359 public void setBackgroundExecutor(Executor bgExecutor) { 360 mBgExecutor = bgExecutor; 361 } 362 setDisplayTracker(DisplayTracker displayTracker)363 public void setDisplayTracker(DisplayTracker displayTracker) { 364 mDisplayTracker = displayTracker; 365 } 366 setTouchHandler(Gefingerpoken touchHandler)367 public void setTouchHandler(Gefingerpoken touchHandler) { 368 mTouchHandler = touchHandler; 369 } 370 371 @Override onInterceptTouchEvent(MotionEvent event)372 public boolean onInterceptTouchEvent(MotionEvent event) { 373 return mTouchHandler.onInterceptTouchEvent(event) || super.onInterceptTouchEvent(event); 374 } 375 376 @Override onTouchEvent(MotionEvent event)377 public boolean onTouchEvent(MotionEvent event) { 378 mTouchHandler.onTouchEvent(event); 379 return super.onTouchEvent(event); 380 } 381 abortCurrentGesture()382 public void abortCurrentGesture() { 383 getHomeButton().abortCurrentGesture(); 384 } 385 getCurrentView()386 public View getCurrentView() { 387 return mCurrentView; 388 } 389 390 /** 391 * Applies {@param consumer} to each of the nav bar views. 392 */ forEachView(Consumer<View> consumer)393 public void forEachView(Consumer<View> consumer) { 394 if (mVertical != null) { 395 consumer.accept(mVertical); 396 } 397 if (mHorizontal != null) { 398 consumer.accept(mHorizontal); 399 } 400 } 401 getRotationButtonController()402 public RotationButtonController getRotationButtonController() { 403 return mRotationButtonController; 404 } 405 getFloatingRotationButton()406 public FloatingRotationButton getFloatingRotationButton() { 407 return mFloatingRotationButton; 408 } 409 getRecentsButton()410 public ButtonDispatcher getRecentsButton() { 411 return mButtonDispatchers.get(R.id.recent_apps); 412 } 413 getBackButton()414 public ButtonDispatcher getBackButton() { 415 return mButtonDispatchers.get(R.id.back); 416 } 417 getHomeButton()418 public ButtonDispatcher getHomeButton() { 419 return mButtonDispatchers.get(R.id.home); 420 } 421 getImeSwitchButton()422 public ButtonDispatcher getImeSwitchButton() { 423 return mButtonDispatchers.get(R.id.ime_switcher); 424 } 425 getAccessibilityButton()426 public ButtonDispatcher getAccessibilityButton() { 427 return mButtonDispatchers.get(R.id.accessibility_button); 428 } 429 getRotateSuggestionButton()430 public RotationContextButton getRotateSuggestionButton() { 431 return (RotationContextButton) mButtonDispatchers.get(R.id.rotate_suggestion); 432 } 433 getHomeHandle()434 public ButtonDispatcher getHomeHandle() { 435 return mButtonDispatchers.get(R.id.home_handle); 436 } 437 getButtonDispatchers()438 public SparseArray<ButtonDispatcher> getButtonDispatchers() { 439 return mButtonDispatchers; 440 } 441 isRecentsButtonVisible()442 public boolean isRecentsButtonVisible() { 443 return getRecentsButton().getVisibility() == View.VISIBLE; 444 } 445 isOverviewEnabled()446 public boolean isOverviewEnabled() { 447 return (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) == 0; 448 } 449 isQuickStepSwipeUpEnabled()450 private boolean isQuickStepSwipeUpEnabled() { 451 return mShowSwipeUpUi && isOverviewEnabled(); 452 } 453 reloadNavIcons()454 private void reloadNavIcons() { 455 updateIcons(Configuration.EMPTY); 456 } 457 updateIcons(Configuration oldConfig)458 private void updateIcons(Configuration oldConfig) { 459 final boolean orientationChange = oldConfig.orientation != mConfiguration.orientation; 460 final boolean densityChange = oldConfig.densityDpi != mConfiguration.densityDpi; 461 final boolean dirChange = oldConfig.getLayoutDirection() != mConfiguration.getLayoutDirection(); 462 463 if (orientationChange || densityChange) { 464 mDockedIcon = getDrawable(R.drawable.ic_sysbar_docked); 465 mHomeDefaultIcon = getHomeDrawable(); 466 } 467 if (densityChange || dirChange) { 468 mRecentIcon = getDrawable(R.drawable.ic_sysbar_recent); 469 mContextualButtonGroup.updateIcons(mLightIconColor, mDarkIconColor); 470 } 471 if (orientationChange || densityChange || dirChange) { 472 mBackIcon = getBackDrawable(); 473 } 474 } 475 476 /** 477 * Updates the rotation button based on the current navigation mode. 478 */ updateRotationButton()479 void updateRotationButton() { 480 if (isGesturalMode(mNavBarMode)) { 481 mContextualButtonGroup.removeButton(R.id.rotate_suggestion); 482 mButtonDispatchers.remove(R.id.rotate_suggestion); 483 mRotationButtonController.setRotationButton(mFloatingRotationButton, 484 mRotationButtonListener); 485 } else if (mContextualButtonGroup.getContextButton(R.id.rotate_suggestion) == null) { 486 mContextualButtonGroup.addButton(mRotationContextButton); 487 mButtonDispatchers.put(R.id.rotate_suggestion, mRotationContextButton); 488 mRotationButtonController.setRotationButton(mRotationContextButton, 489 mRotationButtonListener); 490 } 491 mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers); 492 } 493 getBackDrawable()494 public KeyButtonDrawable getBackDrawable() { 495 KeyButtonDrawable drawable = getDrawable(getBackDrawableRes()); 496 orientBackButton(drawable); 497 return drawable; 498 } 499 getBackDrawableRes()500 public @DrawableRes int getBackDrawableRes() { 501 return chooseNavigationIconDrawableRes(R.drawable.ic_sysbar_back, 502 R.drawable.ic_sysbar_back_quick_step); 503 } 504 getHomeDrawable()505 public KeyButtonDrawable getHomeDrawable() { 506 KeyButtonDrawable drawable = mShowSwipeUpUi 507 ? getDrawable(R.drawable.ic_sysbar_home_quick_step) 508 : getDrawable(R.drawable.ic_sysbar_home); 509 orientHomeButton(drawable); 510 return drawable; 511 } 512 orientBackButton(KeyButtonDrawable drawable)513 private void orientBackButton(KeyButtonDrawable drawable) { 514 final boolean useAltBack = 515 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 516 final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; 517 float degrees = useAltBack ? (isRtl ? 90 : -90) : 0; 518 if (drawable.getRotation() == degrees) { 519 return; 520 } 521 522 if (isGesturalMode(mNavBarMode)) { 523 drawable.setRotation(degrees); 524 return; 525 } 526 527 // Animate the back button's rotation to the new degrees and only in portrait move up the 528 // back button to line up with the other buttons 529 float targetY = !mShowSwipeUpUi && !mIsVertical && useAltBack 530 ? - getResources().getDimension(R.dimen.navbar_back_button_ime_offset) 531 : 0; 532 ObjectAnimator navBarAnimator = ObjectAnimator.ofPropertyValuesHolder(drawable, 533 PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_ROTATE, degrees), 534 PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_TRANSLATE_Y, targetY)); 535 navBarAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 536 navBarAnimator.setDuration(200); 537 navBarAnimator.start(); 538 } 539 orientHomeButton(KeyButtonDrawable drawable)540 private void orientHomeButton(KeyButtonDrawable drawable) { 541 drawable.setRotation(mIsVertical ? 90 : 0); 542 } 543 chooseNavigationIconDrawableRes(@rawableRes int icon, @DrawableRes int quickStepIcon)544 private @DrawableRes int chooseNavigationIconDrawableRes(@DrawableRes int icon, 545 @DrawableRes int quickStepIcon) { 546 return mShowSwipeUpUi ? quickStepIcon : icon; 547 } 548 getDrawable(@rawableRes int icon)549 private KeyButtonDrawable getDrawable(@DrawableRes int icon) { 550 return KeyButtonDrawable.create(mLightContext, mLightIconColor, mDarkIconColor, icon, 551 true /* hasShadow */, null /* ovalBackgroundColor */); 552 } 553 554 /** To be called when screen lock/unlock state changes */ onScreenStateChanged(boolean isScreenOn)555 public void onScreenStateChanged(boolean isScreenOn) { 556 mScreenOn = isScreenOn; 557 } 558 setWindowVisible(boolean visible)559 public void setWindowVisible(boolean visible) { 560 mRotationButtonController.onNavigationBarWindowVisibilityChange(visible); 561 } 562 setBehavior(@ehavior int behavior)563 public void setBehavior(@Behavior int behavior) { 564 mRotationButtonController.onBehaviorChanged(mDisplayTracker.getDefaultDisplayId(), 565 behavior); 566 } 567 568 @Override setLayoutDirection(int layoutDirection)569 public void setLayoutDirection(int layoutDirection) { 570 reloadNavIcons(); 571 572 super.setLayoutDirection(layoutDirection); 573 } 574 setNavigationIconHints(int hints)575 void setNavigationIconHints(int hints) { 576 if (hints == mNavigationIconHints) return; 577 mNavigationIconHints = hints; 578 updateNavButtonIcons(); 579 } 580 onImeVisibilityChanged(boolean visible)581 void onImeVisibilityChanged(boolean visible) { 582 if (!visible) { 583 mTransitionListener.onBackAltCleared(); 584 } 585 mRotationButtonController.getRotationButton().setCanShowRotationButton(!visible); 586 } 587 setDisabledFlags(int disabledFlags, SysUiState sysUiState)588 void setDisabledFlags(int disabledFlags, SysUiState sysUiState) { 589 if (mDisabledFlags == disabledFlags) return; 590 591 final boolean overviewEnabledBefore = isOverviewEnabled(); 592 mDisabledFlags = disabledFlags; 593 594 // Update icons if overview was just enabled to ensure the correct icons are present 595 if (!overviewEnabledBefore && isOverviewEnabled()) { 596 reloadNavIcons(); 597 } 598 599 updateNavButtonIcons(); 600 updateSlippery(); 601 updateDisabledSystemUiStateFlags(sysUiState); 602 } 603 updateNavButtonIcons()604 public void updateNavButtonIcons() { 605 // We have to replace or restore the back and home button icons when exiting or entering 606 // carmode, respectively. Recents are not available in CarMode in nav bar so change 607 // to recent icon is not required. 608 final boolean useAltBack = 609 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 610 KeyButtonDrawable backIcon = mBackIcon; 611 orientBackButton(backIcon); 612 KeyButtonDrawable homeIcon = mHomeDefaultIcon; 613 if (!mUseCarModeUi) { 614 orientHomeButton(homeIcon); 615 } 616 getHomeButton().setImageDrawable(homeIcon); 617 getBackButton().setImageDrawable(backIcon); 618 619 updateRecentsIcon(); 620 621 // Update IME button visibility, a11y and rotate button always overrides the appearance 622 boolean disableImeSwitcher = 623 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN) == 0 624 || isImeRenderingNavButtons(); 625 mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, !disableImeSwitcher); 626 627 mBarTransitions.reapplyDarkIntensity(); 628 629 boolean disableHome = isGesturalMode(mNavBarMode) 630 || ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0); 631 632 // Always disable recents when alternate car mode UI is active and for secondary displays. 633 boolean disableRecent = isRecentsButtonDisabled(); 634 635 // Disable the home handle if both hone and recents are disabled 636 boolean disableHomeHandle = disableRecent 637 && ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0); 638 639 boolean disableBack = !useAltBack && (mEdgeBackGestureHandler.isHandlingGestures() 640 || ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)) 641 || isImeRenderingNavButtons(); 642 643 // When screen pinning, don't hide back and home when connected service or back and 644 // recents buttons when disconnected from launcher service in screen pinning mode, 645 // as they are used for exiting. 646 if (mOverviewProxyEnabled) { 647 // Force disable recents when not in legacy mode 648 disableRecent |= !QuickStepContract.isLegacyMode(mNavBarMode); 649 if (mScreenPinningActive && !QuickStepContract.isGesturalMode(mNavBarMode)) { 650 disableBack = disableHome = false; 651 } 652 } else if (mScreenPinningActive) { 653 disableBack = disableRecent = false; 654 } 655 656 ViewGroup navButtons = getCurrentView().findViewById(R.id.nav_buttons); 657 if (navButtons != null) { 658 LayoutTransition lt = navButtons.getLayoutTransition(); 659 if (lt != null) { 660 if (!lt.getTransitionListeners().contains(mTransitionListener)) { 661 lt.addTransitionListener(mTransitionListener); 662 } 663 } 664 } 665 666 getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE); 667 getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE); 668 getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE); 669 getHomeHandle().setVisibility(disableHomeHandle ? View.INVISIBLE : View.VISIBLE); 670 notifyActiveTouchRegions(); 671 } 672 673 /** 674 * Returns whether the IME is currently visible and drawing the nav buttons. 675 */ isImeRenderingNavButtons()676 boolean isImeRenderingNavButtons() { 677 return mImeDrawsImeNavBar 678 && mImeCanRenderGesturalNavButtons 679 && (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0; 680 } 681 682 @VisibleForTesting isRecentsButtonDisabled()683 boolean isRecentsButtonDisabled() { 684 return mUseCarModeUi || !isOverviewEnabled() 685 || getContext().getDisplayId() != mDisplayTracker.getDefaultDisplayId(); 686 } 687 getContextDisplay()688 private Display getContextDisplay() { 689 return getContext().getDisplay(); 690 } 691 setLayoutTransitionsEnabled(boolean enabled)692 public void setLayoutTransitionsEnabled(boolean enabled) { 693 mLayoutTransitionsEnabled = enabled; 694 updateLayoutTransitionsEnabled(); 695 } 696 setWakeAndUnlocking(boolean wakeAndUnlocking)697 public void setWakeAndUnlocking(boolean wakeAndUnlocking) { 698 setUseFadingAnimations(wakeAndUnlocking); 699 mWakeAndUnlocking = wakeAndUnlocking; 700 updateLayoutTransitionsEnabled(); 701 } 702 updateLayoutTransitionsEnabled()703 private void updateLayoutTransitionsEnabled() { 704 boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled; 705 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons); 706 LayoutTransition lt = navButtons.getLayoutTransition(); 707 if (lt != null) { 708 if (enabled) { 709 lt.enableTransitionType(LayoutTransition.APPEARING); 710 lt.enableTransitionType(LayoutTransition.DISAPPEARING); 711 lt.enableTransitionType(LayoutTransition.CHANGE_APPEARING); 712 lt.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); 713 } else { 714 lt.disableTransitionType(LayoutTransition.APPEARING); 715 lt.disableTransitionType(LayoutTransition.DISAPPEARING); 716 lt.disableTransitionType(LayoutTransition.CHANGE_APPEARING); 717 lt.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); 718 } 719 } 720 } 721 setUseFadingAnimations(boolean useFadingAnimations)722 private void setUseFadingAnimations(boolean useFadingAnimations) { 723 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) ((ViewGroup) getParent()) 724 .getLayoutParams(); 725 if (lp != null) { 726 boolean old = lp.windowAnimations != 0; 727 if (!old && useFadingAnimations) { 728 lp.windowAnimations = R.style.Animation_NavigationBarFadeIn; 729 } else if (old && !useFadingAnimations) { 730 lp.windowAnimations = 0; 731 } else { 732 return; 733 } 734 WindowManager wm = getContext().getSystemService(WindowManager.class); 735 wm.updateViewLayout((View) getParent(), lp); 736 } 737 } 738 onStatusBarPanelStateChanged()739 public void onStatusBarPanelStateChanged() { 740 updateSlippery(); 741 } 742 743 /** */ updateDisabledSystemUiStateFlags(SysUiState sysUiState)744 public void updateDisabledSystemUiStateFlags(SysUiState sysUiState) { 745 int displayId = mContext.getDisplayId(); 746 747 sysUiState.setFlag(SYSUI_STATE_OVERVIEW_DISABLED, 748 (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0) 749 .setFlag(SYSUI_STATE_HOME_DISABLED, 750 (mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0) 751 .setFlag(SYSUI_STATE_SEARCH_DISABLED, 752 (mDisabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0) 753 .commitUpdate(displayId); 754 } 755 setInScreenPinning(boolean active)756 public void setInScreenPinning(boolean active) { 757 mScreenPinningActive = active; 758 } 759 updatePanelSystemUiStateFlags()760 private void updatePanelSystemUiStateFlags() { 761 if (SysUiState.DEBUG) { 762 Log.d(TAG, "Updating panel sysui state flags: panelView=" + mPanelView); 763 } 764 if (mPanelView != null) { 765 mPanelView.updateSystemUiStateFlags(); 766 } 767 } 768 onOverviewProxyConnectionChange(boolean enabled)769 void onOverviewProxyConnectionChange(boolean enabled) { 770 mOverviewProxyEnabled = enabled; 771 } 772 setShouldShowSwipeUpUi(boolean showSwipeUpUi)773 void setShouldShowSwipeUpUi(boolean showSwipeUpUi) { 774 mShowSwipeUpUi = showSwipeUpUi; 775 updateStates(); 776 } 777 778 /** */ updateStates()779 public void updateStates() { 780 if (mNavigationInflaterView != null) { 781 // Reinflate the navbar if needed, no-op unless the swipe up state changes 782 mNavigationInflaterView.onLikelyDefaultLayoutChange(); 783 } 784 785 updateSlippery(); 786 reloadNavIcons(); 787 updateNavButtonIcons(); 788 mBgExecutor.execute(() -> setNavBarVirtualKeyHapticFeedbackEnabled(!mShowSwipeUpUi)); 789 getHomeButton().setAccessibilityDelegate( 790 mShowSwipeUpUi ? mQuickStepAccessibilityDelegate : null); 791 } 792 793 /** 794 * Enable or disable haptic feedback on the navigation bar buttons. 795 */ setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled)796 private void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) { 797 try { 798 WindowManagerGlobal.getWindowManagerService() 799 .setNavBarVirtualKeyHapticFeedbackEnabled(enabled); 800 } catch (RemoteException e) { 801 Log.w(TAG, "Failed to enable or disable navigation bar button haptics: ", e); 802 } 803 } 804 805 /** 806 * Updates the {@link WindowManager.LayoutParams.FLAG_SLIPPERY} state dependent on if swipe up 807 * is enabled, or the notifications is fully opened without being in an animated state. If 808 * slippery is enabled, touch events will leave the nav bar window and enter into the fullscreen 809 * app/home window, if not nav bar will receive a cancelled touch event once gesture leaves bar. 810 */ updateSlippery()811 void updateSlippery() { 812 setSlippery(!isQuickStepSwipeUpEnabled() || 813 (mPanelView != null && mPanelView.isFullyExpanded() && !mPanelView.isCollapsing())); 814 } 815 setSlippery(boolean slippery)816 void setSlippery(boolean slippery) { 817 setWindowFlag(WindowManager.LayoutParams.FLAG_SLIPPERY, slippery); 818 } 819 setWindowFlag(int flags, boolean enable)820 private void setWindowFlag(int flags, boolean enable) { 821 final ViewGroup navbarView = ((ViewGroup) getParent()); 822 if (navbarView == null) { 823 return; 824 } 825 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView.getLayoutParams(); 826 if (lp == null || enable == ((lp.flags & flags) != 0)) { 827 return; 828 } 829 if (enable) { 830 lp.flags |= flags; 831 } else { 832 lp.flags &= ~flags; 833 } 834 WindowManager wm = getContext().getSystemService(WindowManager.class); 835 wm.updateViewLayout(navbarView, lp); 836 } 837 setNavBarMode(int mode, boolean imeDrawsImeNavBar)838 void setNavBarMode(int mode, boolean imeDrawsImeNavBar) { 839 mNavBarMode = mode; 840 mImeDrawsImeNavBar = imeDrawsImeNavBar; 841 mBarTransitions.onNavigationModeChanged(mNavBarMode); 842 mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode); 843 mRotationButtonController.onNavigationModeChanged(mNavBarMode); 844 updateRotationButton(); 845 } 846 setAccessibilityButtonState(final boolean visible, final boolean longClickable)847 public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) { 848 mLongClickableAccessibilityButton = longClickable; 849 getAccessibilityButton().setLongClickable(longClickable); 850 mContextualButtonGroup.setButtonVisibility(R.id.accessibility_button, visible); 851 } 852 853 @Override onFinishInflate()854 public void onFinishInflate() { 855 super.onFinishInflate(); 856 mNavigationInflaterView = findViewById(R.id.navigation_inflater); 857 mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers); 858 859 updateOrientationViews(); 860 reloadNavIcons(); 861 } 862 863 @Override onDraw(Canvas canvas)864 protected void onDraw(Canvas canvas) { 865 mDeadZone.onDraw(canvas); 866 super.onDraw(canvas); 867 } 868 869 @Override onLayout(boolean changed, int left, int top, int right, int bottom)870 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 871 super.onLayout(changed, left, top, right, bottom); 872 873 notifyActiveTouchRegions(); 874 } 875 876 /** 877 * Notifies the overview service of the active touch regions. 878 */ notifyActiveTouchRegions()879 public void notifyActiveTouchRegions() { 880 if (mUpdateActiveTouchRegionsCallback != null) { 881 mUpdateActiveTouchRegionsCallback.update(); 882 } 883 } 884 setUpdateActiveTouchRegionsCallback(UpdateActiveTouchRegionsCallback callback)885 void setUpdateActiveTouchRegionsCallback(UpdateActiveTouchRegionsCallback callback) { 886 mUpdateActiveTouchRegionsCallback = callback; 887 notifyActiveTouchRegions(); 888 } 889 getButtonTouchRegionCache()890 Map<View, Rect> getButtonTouchRegionCache() { 891 FrameLayout navBarLayout = mIsVertical 892 ? mNavigationInflaterView.mVertical 893 : mNavigationInflaterView.mHorizontal; 894 return ((NearestTouchFrame) navBarLayout 895 .findViewById(R.id.nav_buttons)).getFullTouchableChildRegions(); 896 } 897 updateOrientationViews()898 private void updateOrientationViews() { 899 mHorizontal = findViewById(R.id.horizontal); 900 mVertical = findViewById(R.id.vertical); 901 902 updateCurrentView(); 903 } 904 needsReorient(int rotation)905 boolean needsReorient(int rotation) { 906 return mCurrentRotation != rotation; 907 } 908 updateCurrentRotation()909 private void updateCurrentRotation() { 910 final int rotation = mConfiguration.windowConfiguration.getDisplayRotation(); 911 if (mCurrentRotation == rotation) { 912 return; 913 } 914 mCurrentRotation = rotation; 915 mNavigationInflaterView.setAlternativeOrder(mCurrentRotation == Surface.ROTATION_90); 916 mDeadZone.onConfigurationChanged(mCurrentRotation); 917 if (DEBUG) { 918 Log.d(TAG, "updateCurrentRotation(): rot=" + mCurrentRotation); 919 } 920 } 921 updateCurrentView()922 private void updateCurrentView() { 923 resetViews(); 924 mCurrentView = mIsVertical ? mVertical : mHorizontal; 925 mCurrentView.setVisibility(View.VISIBLE); 926 mNavigationInflaterView.setVertical(mIsVertical); 927 mNavigationInflaterView.updateButtonDispatchersCurrentView(); 928 updateLayoutTransitionsEnabled(); 929 updateCurrentRotation(); 930 } 931 resetViews()932 private void resetViews() { 933 mHorizontal.setVisibility(View.GONE); 934 mVertical.setVisibility(View.GONE); 935 } 936 updateRecentsIcon()937 private void updateRecentsIcon() { 938 mDockedIcon.setRotation(mDockedStackExists && mIsVertical ? 90 : 0); 939 getRecentsButton().setImageDrawable(mDockedStackExists ? mDockedIcon : mRecentIcon); 940 mBarTransitions.reapplyDarkIntensity(); 941 } 942 showPinningEnterExitToast(boolean entering)943 public void showPinningEnterExitToast(boolean entering) { 944 if (entering) { 945 mScreenPinningNotify.showPinningStartToast(); 946 } else { 947 mScreenPinningNotify.showPinningExitToast(); 948 } 949 } 950 showPinningEscapeToast()951 public void showPinningEscapeToast() { 952 mScreenPinningNotify.showEscapeToast( 953 mNavBarMode == NAV_BAR_MODE_GESTURAL, isRecentsButtonVisible()); 954 } 955 isVertical()956 public boolean isVertical() { 957 return mIsVertical; 958 } 959 reorient()960 public void reorient() { 961 updateCurrentView(); 962 ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone); 963 964 // force the low profile & disabled states into compliance 965 mBarTransitions.init(); 966 967 // Resolve layout direction if not resolved since components changing layout direction such 968 // as changing languages will recreate this view and the direction will be resolved later 969 if (!isLayoutDirectionResolved()) { 970 resolveLayoutDirection(); 971 } 972 updateNavButtonIcons(); 973 974 getHomeButton().setVertical(mIsVertical); 975 } 976 977 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)978 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 979 int w = MeasureSpec.getSize(widthMeasureSpec); 980 int h = MeasureSpec.getSize(heightMeasureSpec); 981 if (DEBUG) Log.d(TAG, String.format( 982 "onMeasure: (%dx%d) old: (%dx%d)", w, h, getMeasuredWidth(), getMeasuredHeight())); 983 984 final boolean newVertical = w > 0 && h > w 985 && !isGesturalMode(mNavBarMode); 986 if (newVertical != mIsVertical) { 987 mIsVertical = newVertical; 988 if (DEBUG) { 989 Log.d(TAG, String.format("onMeasure: h=%d, w=%d, vert=%s", h, w, 990 mIsVertical ? "y" : "n")); 991 } 992 reorient(); 993 notifyVerticalChangedListener(newVertical); 994 } 995 996 if (isGesturalMode(mNavBarMode)) { 997 // Update the nav bar background to match the height of the visible nav bar 998 int height = mIsVertical 999 ? getResources().getDimensionPixelSize( 1000 com.android.internal.R.dimen.navigation_bar_height_landscape) 1001 : getResources().getDimensionPixelSize( 1002 com.android.internal.R.dimen.navigation_bar_height); 1003 int frameHeight = getResources().getDimensionPixelSize( 1004 com.android.internal.R.dimen.navigation_bar_frame_height); 1005 mBarTransitions.setBackgroundFrame(new Rect(0, frameHeight - height, w, h)); 1006 } else { 1007 mBarTransitions.setBackgroundFrame(null); 1008 } 1009 1010 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 1011 } 1012 getNavBarHeight()1013 int getNavBarHeight() { 1014 return mIsVertical 1015 ? getResources().getDimensionPixelSize( 1016 com.android.internal.R.dimen.navigation_bar_height_landscape) 1017 : getResources().getDimensionPixelSize( 1018 com.android.internal.R.dimen.navigation_bar_height); 1019 } 1020 notifyVerticalChangedListener(boolean newVertical)1021 private void notifyVerticalChangedListener(boolean newVertical) { 1022 if (mOnVerticalChangedListener != null) { 1023 mOnVerticalChangedListener.onVerticalChanged(newVertical); 1024 } 1025 } 1026 1027 @Override onConfigurationChanged(Configuration newConfig)1028 protected void onConfigurationChanged(Configuration newConfig) { 1029 super.onConfigurationChanged(newConfig); 1030 mTmpLastConfiguration.updateFrom(mConfiguration); 1031 final int changes = mConfiguration.updateFrom(newConfig); 1032 mFloatingRotationButton.onConfigurationChanged(changes); 1033 1034 boolean uiCarModeChanged = updateCarMode(); 1035 updateIcons(mTmpLastConfiguration); 1036 updateRecentsIcon(); 1037 updateCurrentRotation(); 1038 mEdgeBackGestureHandler.onConfigurationChanged(mConfiguration); 1039 if (uiCarModeChanged || mTmpLastConfiguration.densityDpi != mConfiguration.densityDpi 1040 || mTmpLastConfiguration.getLayoutDirection() != mConfiguration.getLayoutDirection()) { 1041 // If car mode or density changes, we need to reset the icons. 1042 updateNavButtonIcons(); 1043 } 1044 } 1045 1046 /** 1047 * If the configuration changed, update the carmode and return that it was updated. 1048 */ updateCarMode()1049 private boolean updateCarMode() { 1050 boolean uiCarModeChanged = false; 1051 if (mConfiguration != null) { 1052 int uiMode = mConfiguration.uiMode & Configuration.UI_MODE_TYPE_MASK; 1053 final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR); 1054 1055 if (isCarMode != mInCarMode) { 1056 mInCarMode = isCarMode; 1057 if (ALTERNATE_CAR_MODE_UI) { 1058 mUseCarModeUi = isCarMode; 1059 uiCarModeChanged = true; 1060 } else { 1061 // Don't use car mode behavior if ALTERNATE_CAR_MODE_UI not set. 1062 mUseCarModeUi = false; 1063 } 1064 } 1065 } 1066 return uiCarModeChanged; 1067 } 1068 getResourceName(int resId)1069 private String getResourceName(int resId) { 1070 if (resId != 0) { 1071 final android.content.res.Resources res = getContext().getResources(); 1072 try { 1073 return res.getResourceName(resId); 1074 } catch (android.content.res.Resources.NotFoundException ex) { 1075 return "(unknown)"; 1076 } 1077 } else { 1078 return "(null)"; 1079 } 1080 } 1081 visibilityToString(int vis)1082 private static String visibilityToString(int vis) { 1083 switch (vis) { 1084 case View.INVISIBLE: 1085 return "INVISIBLE"; 1086 case View.GONE: 1087 return "GONE"; 1088 default: 1089 return "VISIBLE"; 1090 } 1091 } 1092 1093 @Override onAttachedToWindow()1094 protected void onAttachedToWindow() { 1095 super.onAttachedToWindow(); 1096 requestApplyInsets(); 1097 reorient(); 1098 if (mRotationButtonController != null) { 1099 mRotationButtonController.registerListeners(false /* registerRotationWatcher */); 1100 } 1101 1102 updateNavButtonIcons(); 1103 } 1104 1105 @Override onDetachedFromWindow()1106 protected void onDetachedFromWindow() { 1107 super.onDetachedFromWindow(); 1108 for (int i = 0; i < mButtonDispatchers.size(); ++i) { 1109 mButtonDispatchers.valueAt(i).onDestroy(); 1110 } 1111 if (mRotationButtonController != null) { 1112 mFloatingRotationButton.hide(); 1113 mRotationButtonController.unregisterListeners(); 1114 } 1115 } 1116 dump(PrintWriter pw)1117 void dump(PrintWriter pw) { 1118 final Rect r = new Rect(); 1119 final Point size = new Point(); 1120 getContextDisplay().getRealSize(size); 1121 1122 pw.println("NavigationBarView:"); 1123 pw.println(String.format(" this: " + CentralSurfaces.viewInfo(this) 1124 + " " + visibilityToString(getVisibility()))); 1125 1126 getWindowVisibleDisplayFrame(r); 1127 final boolean offscreen = r.right > size.x || r.bottom > size.y; 1128 pw.println(" window: " 1129 + r.toShortString() 1130 + " " + visibilityToString(getWindowVisibility()) 1131 + (offscreen ? " OFFSCREEN!" : "")); 1132 1133 pw.println(String.format(" mCurrentView: id=%s (%dx%d) %s %f", 1134 getResourceName(getCurrentView().getId()), 1135 getCurrentView().getWidth(), getCurrentView().getHeight(), 1136 visibilityToString(getCurrentView().getVisibility()), 1137 getCurrentView().getAlpha())); 1138 1139 pw.println(String.format(" disabled=0x%08x vertical=%s darkIntensity=%.2f", 1140 mDisabledFlags, 1141 mIsVertical ? "true" : "false", 1142 getLightTransitionsController().getCurrentDarkIntensity())); 1143 1144 pw.println(" mScreenOn: " + mScreenOn); 1145 1146 1147 dumpButton(pw, "back", getBackButton()); 1148 dumpButton(pw, "home", getHomeButton()); 1149 dumpButton(pw, "handle", getHomeHandle()); 1150 dumpButton(pw, "rcnt", getRecentsButton()); 1151 dumpButton(pw, "rota", getRotateSuggestionButton()); 1152 dumpButton(pw, "a11y", getAccessibilityButton()); 1153 dumpButton(pw, "ime", getImeSwitchButton()); 1154 1155 if (mNavigationInflaterView != null) { 1156 mNavigationInflaterView.dump(pw); 1157 } 1158 mBarTransitions.dump(pw); 1159 mContextualButtonGroup.dump(pw); 1160 mEdgeBackGestureHandler.dump(pw); 1161 } 1162 1163 @Override onApplyWindowInsets(WindowInsets insets)1164 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 1165 int leftInset = insets.getSystemWindowInsetLeft(); 1166 int rightInset = insets.getSystemWindowInsetRight(); 1167 setPadding(leftInset, insets.getSystemWindowInsetTop(), rightInset, 1168 insets.getSystemWindowInsetBottom()); 1169 // we're passing the insets onto the gesture handler since the back arrow is only 1170 // conditionally added and doesn't always get all the insets. 1171 mEdgeBackGestureHandler.setInsets(leftInset, rightInset); 1172 1173 // this allows assist handle to be drawn outside its bound so that it can align screen 1174 // bottom by translating its y position. 1175 final boolean shouldClip = 1176 !isGesturalMode(mNavBarMode) || insets.getSystemWindowInsetBottom() == 0; 1177 setClipChildren(shouldClip); 1178 setClipToPadding(shouldClip); 1179 1180 return super.onApplyWindowInsets(insets); 1181 } 1182 addPipExclusionBoundsChangeListener(Pip pip)1183 void addPipExclusionBoundsChangeListener(Pip pip) { 1184 pip.addPipExclusionBoundsChangeListener(mPipListener); 1185 } 1186 removePipExclusionBoundsChangeListener(Pip pip)1187 void removePipExclusionBoundsChangeListener(Pip pip) { 1188 pip.removePipExclusionBoundsChangeListener(mPipListener); 1189 } 1190 registerBackAnimation(BackAnimation backAnimation)1191 void registerBackAnimation(BackAnimation backAnimation) { 1192 mEdgeBackGestureHandler.setBackAnimation(backAnimation); 1193 } 1194 dumpButton(PrintWriter pw, String caption, ButtonDispatcher button)1195 private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) { 1196 pw.print(" " + caption + ": "); 1197 if (button == null) { 1198 pw.print("null"); 1199 } else { 1200 pw.print(visibilityToString(button.getVisibility()) 1201 + " alpha=" + button.getAlpha() 1202 ); 1203 } 1204 pw.println(); 1205 } 1206 1207 public interface OnVerticalChangedListener { onVerticalChanged(boolean isVertical)1208 void onVerticalChanged(boolean isVertical); 1209 } 1210 1211 private final Consumer<Boolean> mDockedListener = exists -> post(() -> { 1212 mDockedStackExists = exists; 1213 updateRecentsIcon(); 1214 }); 1215 1216 private final Consumer<Rect> mPipListener = bounds -> post(() -> { 1217 mEdgeBackGestureHandler.setPipStashExclusionBounds(bounds); 1218 }); 1219 1220 1221 interface UpdateActiveTouchRegionsCallback { update()1222 void update(); 1223 } 1224 } 1225