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.app.animation.Interpolators; 62 import com.android.internal.annotations.VisibleForTesting; 63 import com.android.settingslib.Utils; 64 import com.android.systemui.Gefingerpoken; 65 import com.android.systemui.R; 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.ShadeViewController; 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 ShadeViewController 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 349 /** */ setComponents(ShadeViewController panel)350 public void setComponents(ShadeViewController panel) { 351 mPanelView = panel; 352 updatePanelSystemUiStateFlags(); 353 } 354 setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener)355 public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) { 356 mOnVerticalChangedListener = onVerticalChangedListener; 357 notifyVerticalChangedListener(mIsVertical); 358 } 359 setBackgroundExecutor(Executor bgExecutor)360 public void setBackgroundExecutor(Executor bgExecutor) { 361 mBgExecutor = bgExecutor; 362 mRotationButtonController.setBgExecutor(bgExecutor); 363 } 364 setDisplayTracker(DisplayTracker displayTracker)365 public void setDisplayTracker(DisplayTracker displayTracker) { 366 mDisplayTracker = displayTracker; 367 } 368 setTouchHandler(Gefingerpoken touchHandler)369 public void setTouchHandler(Gefingerpoken touchHandler) { 370 mTouchHandler = touchHandler; 371 } 372 373 @Override onInterceptTouchEvent(MotionEvent event)374 public boolean onInterceptTouchEvent(MotionEvent event) { 375 return mTouchHandler.onInterceptTouchEvent(event) || super.onInterceptTouchEvent(event); 376 } 377 378 @Override onTouchEvent(MotionEvent event)379 public boolean onTouchEvent(MotionEvent event) { 380 mTouchHandler.onTouchEvent(event); 381 return super.onTouchEvent(event); 382 } 383 abortCurrentGesture()384 public void abortCurrentGesture() { 385 getHomeButton().abortCurrentGesture(); 386 } 387 getCurrentView()388 public View getCurrentView() { 389 return mCurrentView; 390 } 391 392 /** 393 * Applies {@param consumer} to each of the nav bar views. 394 */ forEachView(Consumer<View> consumer)395 public void forEachView(Consumer<View> consumer) { 396 if (mVertical != null) { 397 consumer.accept(mVertical); 398 } 399 if (mHorizontal != null) { 400 consumer.accept(mHorizontal); 401 } 402 } 403 getRotationButtonController()404 public RotationButtonController getRotationButtonController() { 405 return mRotationButtonController; 406 } 407 getFloatingRotationButton()408 public FloatingRotationButton getFloatingRotationButton() { 409 return mFloatingRotationButton; 410 } 411 getRecentsButton()412 public ButtonDispatcher getRecentsButton() { 413 return mButtonDispatchers.get(R.id.recent_apps); 414 } 415 getBackButton()416 public ButtonDispatcher getBackButton() { 417 return mButtonDispatchers.get(R.id.back); 418 } 419 getHomeButton()420 public ButtonDispatcher getHomeButton() { 421 return mButtonDispatchers.get(R.id.home); 422 } 423 getImeSwitchButton()424 public ButtonDispatcher getImeSwitchButton() { 425 return mButtonDispatchers.get(R.id.ime_switcher); 426 } 427 getAccessibilityButton()428 public ButtonDispatcher getAccessibilityButton() { 429 return mButtonDispatchers.get(R.id.accessibility_button); 430 } 431 getRotateSuggestionButton()432 public RotationContextButton getRotateSuggestionButton() { 433 return (RotationContextButton) mButtonDispatchers.get(R.id.rotate_suggestion); 434 } 435 getHomeHandle()436 public ButtonDispatcher getHomeHandle() { 437 return mButtonDispatchers.get(R.id.home_handle); 438 } 439 getButtonDispatchers()440 public SparseArray<ButtonDispatcher> getButtonDispatchers() { 441 return mButtonDispatchers; 442 } 443 isRecentsButtonVisible()444 public boolean isRecentsButtonVisible() { 445 return getRecentsButton().getVisibility() == View.VISIBLE; 446 } 447 isOverviewEnabled()448 public boolean isOverviewEnabled() { 449 return (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) == 0; 450 } 451 isQuickStepSwipeUpEnabled()452 private boolean isQuickStepSwipeUpEnabled() { 453 return mShowSwipeUpUi && isOverviewEnabled(); 454 } 455 reloadNavIcons()456 private void reloadNavIcons() { 457 updateIcons(Configuration.EMPTY); 458 } 459 updateIcons(Configuration oldConfig)460 private void updateIcons(Configuration oldConfig) { 461 final boolean orientationChange = oldConfig.orientation != mConfiguration.orientation; 462 final boolean densityChange = oldConfig.densityDpi != mConfiguration.densityDpi; 463 final boolean dirChange = oldConfig.getLayoutDirection() != mConfiguration.getLayoutDirection(); 464 465 if (orientationChange || densityChange) { 466 mDockedIcon = getDrawable(R.drawable.ic_sysbar_docked); 467 mHomeDefaultIcon = getHomeDrawable(); 468 } 469 if (densityChange || dirChange) { 470 mRecentIcon = getDrawable(R.drawable.ic_sysbar_recent); 471 mContextualButtonGroup.updateIcons(mLightIconColor, mDarkIconColor); 472 } 473 if (orientationChange || densityChange || dirChange) { 474 mBackIcon = getBackDrawable(); 475 } 476 } 477 478 /** 479 * Updates the rotation button based on the current navigation mode. 480 */ updateRotationButton()481 void updateRotationButton() { 482 if (isGesturalMode(mNavBarMode)) { 483 mContextualButtonGroup.removeButton(R.id.rotate_suggestion); 484 mButtonDispatchers.remove(R.id.rotate_suggestion); 485 mRotationButtonController.setRotationButton(mFloatingRotationButton, 486 mRotationButtonListener); 487 } else if (mContextualButtonGroup.getContextButton(R.id.rotate_suggestion) == null) { 488 mContextualButtonGroup.addButton(mRotationContextButton); 489 mButtonDispatchers.put(R.id.rotate_suggestion, mRotationContextButton); 490 mRotationButtonController.setRotationButton(mRotationContextButton, 491 mRotationButtonListener); 492 } 493 mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers); 494 } 495 getBackDrawable()496 public KeyButtonDrawable getBackDrawable() { 497 KeyButtonDrawable drawable = getDrawable(getBackDrawableRes()); 498 orientBackButton(drawable); 499 return drawable; 500 } 501 getBackDrawableRes()502 public @DrawableRes int getBackDrawableRes() { 503 return chooseNavigationIconDrawableRes(R.drawable.ic_sysbar_back, 504 R.drawable.ic_sysbar_back_quick_step); 505 } 506 getHomeDrawable()507 public KeyButtonDrawable getHomeDrawable() { 508 KeyButtonDrawable drawable = mShowSwipeUpUi 509 ? getDrawable(R.drawable.ic_sysbar_home_quick_step) 510 : getDrawable(R.drawable.ic_sysbar_home); 511 orientHomeButton(drawable); 512 return drawable; 513 } 514 orientBackButton(KeyButtonDrawable drawable)515 private void orientBackButton(KeyButtonDrawable drawable) { 516 final boolean useAltBack = 517 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 518 final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; 519 float degrees = useAltBack ? (isRtl ? 90 : -90) : 0; 520 if (drawable.getRotation() == degrees) { 521 return; 522 } 523 524 if (isGesturalMode(mNavBarMode)) { 525 drawable.setRotation(degrees); 526 return; 527 } 528 529 // Animate the back button's rotation to the new degrees and only in portrait move up the 530 // back button to line up with the other buttons 531 float targetY = !mShowSwipeUpUi && !mIsVertical && useAltBack 532 ? - getResources().getDimension(R.dimen.navbar_back_button_ime_offset) 533 : 0; 534 ObjectAnimator navBarAnimator = ObjectAnimator.ofPropertyValuesHolder(drawable, 535 PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_ROTATE, degrees), 536 PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_TRANSLATE_Y, targetY)); 537 navBarAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 538 navBarAnimator.setDuration(200); 539 navBarAnimator.start(); 540 } 541 orientHomeButton(KeyButtonDrawable drawable)542 private void orientHomeButton(KeyButtonDrawable drawable) { 543 drawable.setRotation(mIsVertical ? 90 : 0); 544 } 545 chooseNavigationIconDrawableRes(@rawableRes int icon, @DrawableRes int quickStepIcon)546 private @DrawableRes int chooseNavigationIconDrawableRes(@DrawableRes int icon, 547 @DrawableRes int quickStepIcon) { 548 return mShowSwipeUpUi ? quickStepIcon : icon; 549 } 550 getDrawable(@rawableRes int icon)551 private KeyButtonDrawable getDrawable(@DrawableRes int icon) { 552 return KeyButtonDrawable.create(mLightContext, mLightIconColor, mDarkIconColor, icon, 553 true /* hasShadow */, null /* ovalBackgroundColor */); 554 } 555 556 /** To be called when screen lock/unlock state changes */ onScreenStateChanged(boolean isScreenOn)557 public void onScreenStateChanged(boolean isScreenOn) { 558 mScreenOn = isScreenOn; 559 } 560 setWindowVisible(boolean visible)561 public void setWindowVisible(boolean visible) { 562 mRotationButtonController.onNavigationBarWindowVisibilityChange(visible); 563 } 564 setBehavior(@ehavior int behavior)565 public void setBehavior(@Behavior int behavior) { 566 mRotationButtonController.onBehaviorChanged(mDisplayTracker.getDefaultDisplayId(), 567 behavior); 568 } 569 570 @Override setLayoutDirection(int layoutDirection)571 public void setLayoutDirection(int layoutDirection) { 572 reloadNavIcons(); 573 574 super.setLayoutDirection(layoutDirection); 575 } 576 setNavigationIconHints(int hints)577 void setNavigationIconHints(int hints) { 578 if (hints == mNavigationIconHints) return; 579 mNavigationIconHints = hints; 580 updateNavButtonIcons(); 581 } 582 onImeVisibilityChanged(boolean visible)583 void onImeVisibilityChanged(boolean visible) { 584 if (!visible) { 585 mTransitionListener.onBackAltCleared(); 586 } 587 } 588 setDisabledFlags(int disabledFlags, SysUiState sysUiState)589 void setDisabledFlags(int disabledFlags, SysUiState sysUiState) { 590 if (mDisabledFlags == disabledFlags) return; 591 592 final boolean overviewEnabledBefore = isOverviewEnabled(); 593 mDisabledFlags = disabledFlags; 594 595 // Update icons if overview was just enabled to ensure the correct icons are present 596 if (!overviewEnabledBefore && isOverviewEnabled()) { 597 reloadNavIcons(); 598 } 599 600 updateNavButtonIcons(); 601 updateSlippery(); 602 updateDisabledSystemUiStateFlags(sysUiState); 603 } 604 updateNavButtonIcons()605 public void updateNavButtonIcons() { 606 // We have to replace or restore the back and home button icons when exiting or entering 607 // carmode, respectively. Recents are not available in CarMode in nav bar so change 608 // to recent icon is not required. 609 final boolean useAltBack = 610 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 611 KeyButtonDrawable backIcon = mBackIcon; 612 orientBackButton(backIcon); 613 KeyButtonDrawable homeIcon = mHomeDefaultIcon; 614 if (!mUseCarModeUi) { 615 orientHomeButton(homeIcon); 616 } 617 getHomeButton().setImageDrawable(homeIcon); 618 getBackButton().setImageDrawable(backIcon); 619 620 updateRecentsIcon(); 621 622 // Update IME button visibility, a11y and rotate button always overrides the appearance 623 boolean disableImeSwitcher = 624 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN) == 0 625 || isImeRenderingNavButtons(); 626 mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, !disableImeSwitcher); 627 628 mBarTransitions.reapplyDarkIntensity(); 629 630 boolean disableHome = isGesturalMode(mNavBarMode) 631 || ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0); 632 633 // Always disable recents when alternate car mode UI is active and for secondary displays. 634 boolean disableRecent = isRecentsButtonDisabled(); 635 636 // Disable the home handle if both hone and recents are disabled 637 boolean disableHomeHandle = disableRecent 638 && ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0); 639 640 boolean disableBack = !useAltBack && (mEdgeBackGestureHandler.isHandlingGestures() 641 || ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)) 642 || isImeRenderingNavButtons(); 643 644 // When screen pinning, don't hide back and home when connected service or back and 645 // recents buttons when disconnected from launcher service in screen pinning mode, 646 // as they are used for exiting. 647 if (mOverviewProxyEnabled) { 648 // Force disable recents when not in legacy mode 649 disableRecent |= !QuickStepContract.isLegacyMode(mNavBarMode); 650 if (mScreenPinningActive && !QuickStepContract.isGesturalMode(mNavBarMode)) { 651 disableBack = disableHome = false; 652 } 653 } else if (mScreenPinningActive) { 654 disableBack = disableRecent = false; 655 } 656 657 ViewGroup navButtons = getCurrentView().findViewById(R.id.nav_buttons); 658 if (navButtons != null) { 659 LayoutTransition lt = navButtons.getLayoutTransition(); 660 if (lt != null) { 661 if (!lt.getTransitionListeners().contains(mTransitionListener)) { 662 lt.addTransitionListener(mTransitionListener); 663 } 664 } 665 } 666 667 getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE); 668 getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE); 669 getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE); 670 getHomeHandle().setVisibility(disableHomeHandle ? View.INVISIBLE : View.VISIBLE); 671 notifyActiveTouchRegions(); 672 } 673 674 /** 675 * Returns whether the IME is currently visible and drawing the nav buttons. 676 */ isImeRenderingNavButtons()677 boolean isImeRenderingNavButtons() { 678 return mImeDrawsImeNavBar 679 && mImeCanRenderGesturalNavButtons 680 && (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0; 681 } 682 683 @VisibleForTesting isRecentsButtonDisabled()684 boolean isRecentsButtonDisabled() { 685 return mUseCarModeUi || !isOverviewEnabled() 686 || getContext().getDisplayId() != mDisplayTracker.getDefaultDisplayId(); 687 } 688 getContextDisplay()689 private Display getContextDisplay() { 690 return getContext().getDisplay(); 691 } 692 setLayoutTransitionsEnabled(boolean enabled)693 public void setLayoutTransitionsEnabled(boolean enabled) { 694 mLayoutTransitionsEnabled = enabled; 695 updateLayoutTransitionsEnabled(); 696 } 697 setWakeAndUnlocking(boolean wakeAndUnlocking)698 public void setWakeAndUnlocking(boolean wakeAndUnlocking) { 699 setUseFadingAnimations(wakeAndUnlocking); 700 mWakeAndUnlocking = wakeAndUnlocking; 701 updateLayoutTransitionsEnabled(); 702 } 703 updateLayoutTransitionsEnabled()704 private void updateLayoutTransitionsEnabled() { 705 boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled; 706 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons); 707 LayoutTransition lt = navButtons.getLayoutTransition(); 708 if (lt != null) { 709 if (enabled) { 710 lt.enableTransitionType(LayoutTransition.APPEARING); 711 lt.enableTransitionType(LayoutTransition.DISAPPEARING); 712 lt.enableTransitionType(LayoutTransition.CHANGE_APPEARING); 713 lt.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); 714 } else { 715 lt.disableTransitionType(LayoutTransition.APPEARING); 716 lt.disableTransitionType(LayoutTransition.DISAPPEARING); 717 lt.disableTransitionType(LayoutTransition.CHANGE_APPEARING); 718 lt.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); 719 } 720 } 721 } 722 setUseFadingAnimations(boolean useFadingAnimations)723 private void setUseFadingAnimations(boolean useFadingAnimations) { 724 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) ((ViewGroup) getParent()) 725 .getLayoutParams(); 726 if (lp != null) { 727 boolean old = lp.windowAnimations != 0; 728 if (!old && useFadingAnimations) { 729 lp.windowAnimations = R.style.Animation_NavigationBarFadeIn; 730 } else if (old && !useFadingAnimations) { 731 lp.windowAnimations = 0; 732 } else { 733 return; 734 } 735 WindowManager wm = getContext().getSystemService(WindowManager.class); 736 wm.updateViewLayout((View) getParent(), lp); 737 } 738 } 739 onStatusBarPanelStateChanged()740 public void onStatusBarPanelStateChanged() { 741 updateSlippery(); 742 } 743 744 /** */ updateDisabledSystemUiStateFlags(SysUiState sysUiState)745 public void updateDisabledSystemUiStateFlags(SysUiState sysUiState) { 746 int displayId = mContext.getDisplayId(); 747 748 sysUiState.setFlag(SYSUI_STATE_OVERVIEW_DISABLED, 749 (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0) 750 .setFlag(SYSUI_STATE_HOME_DISABLED, 751 (mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0) 752 .setFlag(SYSUI_STATE_SEARCH_DISABLED, 753 (mDisabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0) 754 .commitUpdate(displayId); 755 } 756 setInScreenPinning(boolean active)757 public void setInScreenPinning(boolean active) { 758 mScreenPinningActive = active; 759 } 760 updatePanelSystemUiStateFlags()761 private void updatePanelSystemUiStateFlags() { 762 if (SysUiState.DEBUG) { 763 Log.d(TAG, "Updating panel sysui state flags: panelView=" + mPanelView); 764 } 765 if (mPanelView != null) { 766 mPanelView.updateSystemUiStateFlags(); 767 } 768 } 769 onOverviewProxyConnectionChange(boolean enabled)770 void onOverviewProxyConnectionChange(boolean enabled) { 771 mOverviewProxyEnabled = enabled; 772 } 773 setShouldShowSwipeUpUi(boolean showSwipeUpUi)774 void setShouldShowSwipeUpUi(boolean showSwipeUpUi) { 775 mShowSwipeUpUi = showSwipeUpUi; 776 updateStates(); 777 } 778 779 /** */ updateStates()780 public void updateStates() { 781 if (mNavigationInflaterView != null) { 782 // Reinflate the navbar if needed, no-op unless the swipe up state changes 783 mNavigationInflaterView.onLikelyDefaultLayoutChange(); 784 } 785 786 updateSlippery(); 787 reloadNavIcons(); 788 updateNavButtonIcons(); 789 mBgExecutor.execute(() -> setNavBarVirtualKeyHapticFeedbackEnabled(!mShowSwipeUpUi)); 790 getHomeButton().setAccessibilityDelegate( 791 mShowSwipeUpUi ? mQuickStepAccessibilityDelegate : null); 792 } 793 794 /** 795 * Enable or disable haptic feedback on the navigation bar buttons. 796 */ setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled)797 private void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) { 798 try { 799 WindowManagerGlobal.getWindowManagerService() 800 .setNavBarVirtualKeyHapticFeedbackEnabled(enabled); 801 } catch (RemoteException e) { 802 Log.w(TAG, "Failed to enable or disable navigation bar button haptics: ", e); 803 } 804 } 805 806 /** 807 * Updates the {@link WindowManager.LayoutParams.FLAG_SLIPPERY} state dependent on if swipe up 808 * is enabled, or the notifications is fully opened without being in an animated state. If 809 * slippery is enabled, touch events will leave the nav bar window and enter into the fullscreen 810 * app/home window, if not nav bar will receive a cancelled touch event once gesture leaves bar. 811 */ updateSlippery()812 void updateSlippery() { 813 setSlippery(!isQuickStepSwipeUpEnabled() || 814 (mPanelView != null && mPanelView.isFullyExpanded() && !mPanelView.isCollapsing())); 815 } 816 setSlippery(boolean slippery)817 void setSlippery(boolean slippery) { 818 setWindowFlag(WindowManager.LayoutParams.FLAG_SLIPPERY, slippery); 819 } 820 setWindowFlag(int flags, boolean enable)821 private void setWindowFlag(int flags, boolean enable) { 822 final ViewGroup navbarView = ((ViewGroup) getParent()); 823 if (navbarView == null) { 824 return; 825 } 826 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView.getLayoutParams(); 827 if (lp == null || enable == ((lp.flags & flags) != 0)) { 828 return; 829 } 830 if (enable) { 831 lp.flags |= flags; 832 } else { 833 lp.flags &= ~flags; 834 } 835 WindowManager wm = getContext().getSystemService(WindowManager.class); 836 wm.updateViewLayout(navbarView, lp); 837 } 838 setNavBarMode(int mode, boolean imeDrawsImeNavBar)839 void setNavBarMode(int mode, boolean imeDrawsImeNavBar) { 840 mNavBarMode = mode; 841 mImeDrawsImeNavBar = imeDrawsImeNavBar; 842 mBarTransitions.onNavigationModeChanged(mNavBarMode); 843 mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode); 844 mRotationButtonController.onNavigationModeChanged(mNavBarMode); 845 updateRotationButton(); 846 } 847 setAccessibilityButtonState(final boolean visible, final boolean longClickable)848 public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) { 849 mLongClickableAccessibilityButton = longClickable; 850 getAccessibilityButton().setLongClickable(longClickable); 851 mContextualButtonGroup.setButtonVisibility(R.id.accessibility_button, visible); 852 } 853 854 @Override onFinishInflate()855 public void onFinishInflate() { 856 super.onFinishInflate(); 857 mNavigationInflaterView = findViewById(R.id.navigation_inflater); 858 mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers); 859 860 updateOrientationViews(); 861 reloadNavIcons(); 862 } 863 864 @Override onDraw(Canvas canvas)865 protected void onDraw(Canvas canvas) { 866 mDeadZone.onDraw(canvas); 867 super.onDraw(canvas); 868 } 869 870 @Override onLayout(boolean changed, int left, int top, int right, int bottom)871 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 872 super.onLayout(changed, left, top, right, bottom); 873 874 notifyActiveTouchRegions(); 875 } 876 877 /** 878 * Notifies the overview service of the active touch regions. 879 */ notifyActiveTouchRegions()880 public void notifyActiveTouchRegions() { 881 if (mUpdateActiveTouchRegionsCallback != null) { 882 mUpdateActiveTouchRegionsCallback.update(); 883 } 884 } 885 setUpdateActiveTouchRegionsCallback(UpdateActiveTouchRegionsCallback callback)886 void setUpdateActiveTouchRegionsCallback(UpdateActiveTouchRegionsCallback callback) { 887 mUpdateActiveTouchRegionsCallback = callback; 888 notifyActiveTouchRegions(); 889 } 890 getButtonTouchRegionCache()891 Map<View, Rect> getButtonTouchRegionCache() { 892 FrameLayout navBarLayout = mIsVertical 893 ? mNavigationInflaterView.mVertical 894 : mNavigationInflaterView.mHorizontal; 895 return ((NearestTouchFrame) navBarLayout 896 .findViewById(R.id.nav_buttons)).getFullTouchableChildRegions(); 897 } 898 updateOrientationViews()899 private void updateOrientationViews() { 900 mHorizontal = findViewById(R.id.horizontal); 901 mVertical = findViewById(R.id.vertical); 902 903 updateCurrentView(); 904 } 905 needsReorient(int rotation)906 boolean needsReorient(int rotation) { 907 return mCurrentRotation != rotation; 908 } 909 updateCurrentRotation()910 private void updateCurrentRotation() { 911 final int rotation = mConfiguration.windowConfiguration.getDisplayRotation(); 912 if (mCurrentRotation == rotation) { 913 return; 914 } 915 mCurrentRotation = rotation; 916 mNavigationInflaterView.setAlternativeOrder(mCurrentRotation == Surface.ROTATION_90); 917 mDeadZone.onConfigurationChanged(mCurrentRotation); 918 if (DEBUG) { 919 Log.d(TAG, "updateCurrentRotation(): rot=" + mCurrentRotation); 920 } 921 } 922 updateCurrentView()923 private void updateCurrentView() { 924 resetViews(); 925 mCurrentView = mIsVertical ? mVertical : mHorizontal; 926 mCurrentView.setVisibility(View.VISIBLE); 927 mNavigationInflaterView.setVertical(mIsVertical); 928 mNavigationInflaterView.updateButtonDispatchersCurrentView(); 929 updateLayoutTransitionsEnabled(); 930 updateCurrentRotation(); 931 } 932 resetViews()933 private void resetViews() { 934 mHorizontal.setVisibility(View.GONE); 935 mVertical.setVisibility(View.GONE); 936 } 937 updateRecentsIcon()938 private void updateRecentsIcon() { 939 mDockedIcon.setRotation(mDockedStackExists && mIsVertical ? 90 : 0); 940 getRecentsButton().setImageDrawable(mDockedStackExists ? mDockedIcon : mRecentIcon); 941 mBarTransitions.reapplyDarkIntensity(); 942 } 943 showPinningEnterExitToast(boolean entering)944 public void showPinningEnterExitToast(boolean entering) { 945 if (entering) { 946 mScreenPinningNotify.showPinningStartToast(); 947 } else { 948 mScreenPinningNotify.showPinningExitToast(); 949 } 950 } 951 showPinningEscapeToast()952 public void showPinningEscapeToast() { 953 mScreenPinningNotify.showEscapeToast( 954 mNavBarMode == NAV_BAR_MODE_GESTURAL, isRecentsButtonVisible()); 955 } 956 isVertical()957 public boolean isVertical() { 958 return mIsVertical; 959 } 960 reorient()961 public void reorient() { 962 updateCurrentView(); 963 ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone); 964 965 // force the low profile & disabled states into compliance 966 mBarTransitions.init(); 967 968 // Resolve layout direction if not resolved since components changing layout direction such 969 // as changing languages will recreate this view and the direction will be resolved later 970 if (!isLayoutDirectionResolved()) { 971 resolveLayoutDirection(); 972 } 973 updateNavButtonIcons(); 974 975 getHomeButton().setVertical(mIsVertical); 976 } 977 978 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)979 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 980 int w = MeasureSpec.getSize(widthMeasureSpec); 981 int h = MeasureSpec.getSize(heightMeasureSpec); 982 if (DEBUG) Log.d(TAG, String.format( 983 "onMeasure: (%dx%d) old: (%dx%d)", w, h, getMeasuredWidth(), getMeasuredHeight())); 984 985 final boolean newVertical = w > 0 && h > w 986 && !isGesturalMode(mNavBarMode); 987 if (newVertical != mIsVertical) { 988 mIsVertical = newVertical; 989 if (DEBUG) { 990 Log.d(TAG, String.format("onMeasure: h=%d, w=%d, vert=%s", h, w, 991 mIsVertical ? "y" : "n")); 992 } 993 reorient(); 994 notifyVerticalChangedListener(newVertical); 995 } 996 997 if (isGesturalMode(mNavBarMode)) { 998 // Update the nav bar background to match the height of the visible nav bar 999 int height = mIsVertical 1000 ? getResources().getDimensionPixelSize( 1001 com.android.internal.R.dimen.navigation_bar_height_landscape) 1002 : getResources().getDimensionPixelSize( 1003 com.android.internal.R.dimen.navigation_bar_height); 1004 int frameHeight = getResources().getDimensionPixelSize( 1005 com.android.internal.R.dimen.navigation_bar_frame_height); 1006 mBarTransitions.setBackgroundFrame(new Rect(0, frameHeight - height, w, h)); 1007 } else { 1008 mBarTransitions.setBackgroundFrame(null); 1009 } 1010 1011 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 1012 } 1013 getNavBarHeight()1014 int getNavBarHeight() { 1015 return mIsVertical 1016 ? getResources().getDimensionPixelSize( 1017 com.android.internal.R.dimen.navigation_bar_height_landscape) 1018 : getResources().getDimensionPixelSize( 1019 com.android.internal.R.dimen.navigation_bar_height); 1020 } 1021 notifyVerticalChangedListener(boolean newVertical)1022 private void notifyVerticalChangedListener(boolean newVertical) { 1023 if (mOnVerticalChangedListener != null) { 1024 mOnVerticalChangedListener.onVerticalChanged(newVertical); 1025 } 1026 } 1027 1028 @Override onConfigurationChanged(Configuration newConfig)1029 protected void onConfigurationChanged(Configuration newConfig) { 1030 super.onConfigurationChanged(newConfig); 1031 mTmpLastConfiguration.updateFrom(mConfiguration); 1032 final int changes = mConfiguration.updateFrom(newConfig); 1033 mFloatingRotationButton.onConfigurationChanged(changes); 1034 1035 boolean uiCarModeChanged = updateCarMode(); 1036 updateIcons(mTmpLastConfiguration); 1037 updateRecentsIcon(); 1038 updateCurrentRotation(); 1039 mEdgeBackGestureHandler.onConfigurationChanged(mConfiguration); 1040 if (uiCarModeChanged || mTmpLastConfiguration.densityDpi != mConfiguration.densityDpi 1041 || mTmpLastConfiguration.getLayoutDirection() != mConfiguration.getLayoutDirection()) { 1042 // If car mode or density changes, we need to reset the icons. 1043 updateNavButtonIcons(); 1044 } 1045 } 1046 1047 /** 1048 * If the configuration changed, update the carmode and return that it was updated. 1049 */ updateCarMode()1050 private boolean updateCarMode() { 1051 boolean uiCarModeChanged = false; 1052 if (mConfiguration != null) { 1053 int uiMode = mConfiguration.uiMode & Configuration.UI_MODE_TYPE_MASK; 1054 final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR); 1055 1056 if (isCarMode != mInCarMode) { 1057 mInCarMode = isCarMode; 1058 if (ALTERNATE_CAR_MODE_UI) { 1059 mUseCarModeUi = isCarMode; 1060 uiCarModeChanged = true; 1061 } else { 1062 // Don't use car mode behavior if ALTERNATE_CAR_MODE_UI not set. 1063 mUseCarModeUi = false; 1064 } 1065 } 1066 } 1067 return uiCarModeChanged; 1068 } 1069 getResourceName(int resId)1070 private String getResourceName(int resId) { 1071 if (resId != 0) { 1072 final android.content.res.Resources res = getContext().getResources(); 1073 try { 1074 return res.getResourceName(resId); 1075 } catch (android.content.res.Resources.NotFoundException ex) { 1076 return "(unknown)"; 1077 } 1078 } else { 1079 return "(null)"; 1080 } 1081 } 1082 visibilityToString(int vis)1083 private static String visibilityToString(int vis) { 1084 switch (vis) { 1085 case View.INVISIBLE: 1086 return "INVISIBLE"; 1087 case View.GONE: 1088 return "GONE"; 1089 default: 1090 return "VISIBLE"; 1091 } 1092 } 1093 1094 @Override onAttachedToWindow()1095 protected void onAttachedToWindow() { 1096 super.onAttachedToWindow(); 1097 requestApplyInsets(); 1098 reorient(); 1099 if (mRotationButtonController != null) { 1100 mRotationButtonController.registerListeners(false /* registerRotationWatcher */); 1101 } 1102 1103 updateNavButtonIcons(); 1104 } 1105 1106 @Override onDetachedFromWindow()1107 protected void onDetachedFromWindow() { 1108 super.onDetachedFromWindow(); 1109 for (int i = 0; i < mButtonDispatchers.size(); ++i) { 1110 mButtonDispatchers.valueAt(i).onDestroy(); 1111 } 1112 if (mRotationButtonController != null) { 1113 mFloatingRotationButton.hide(); 1114 mRotationButtonController.unregisterListeners(); 1115 } 1116 } 1117 dump(PrintWriter pw)1118 void dump(PrintWriter pw) { 1119 final Rect r = new Rect(); 1120 final Point size = new Point(); 1121 getContextDisplay().getRealSize(size); 1122 1123 pw.println("NavigationBarView:"); 1124 pw.println(String.format(" this: " + CentralSurfaces.viewInfo(this) 1125 + " " + visibilityToString(getVisibility()))); 1126 1127 getWindowVisibleDisplayFrame(r); 1128 final boolean offscreen = r.right > size.x || r.bottom > size.y; 1129 pw.println(" window: " 1130 + r.toShortString() 1131 + " " + visibilityToString(getWindowVisibility()) 1132 + (offscreen ? " OFFSCREEN!" : "")); 1133 1134 pw.println(String.format(" mCurrentView: id=%s (%dx%d) %s %f", 1135 getResourceName(getCurrentView().getId()), 1136 getCurrentView().getWidth(), getCurrentView().getHeight(), 1137 visibilityToString(getCurrentView().getVisibility()), 1138 getCurrentView().getAlpha())); 1139 1140 pw.println(String.format(" disabled=0x%08x vertical=%s darkIntensity=%.2f", 1141 mDisabledFlags, 1142 mIsVertical ? "true" : "false", 1143 getLightTransitionsController().getCurrentDarkIntensity())); 1144 1145 pw.println(" mScreenOn: " + mScreenOn); 1146 1147 1148 dumpButton(pw, "back", getBackButton()); 1149 dumpButton(pw, "home", getHomeButton()); 1150 dumpButton(pw, "handle", getHomeHandle()); 1151 dumpButton(pw, "rcnt", getRecentsButton()); 1152 dumpButton(pw, "rota", getRotateSuggestionButton()); 1153 dumpButton(pw, "a11y", getAccessibilityButton()); 1154 dumpButton(pw, "ime", getImeSwitchButton()); 1155 1156 if (mNavigationInflaterView != null) { 1157 mNavigationInflaterView.dump(pw); 1158 } 1159 mBarTransitions.dump(pw); 1160 mContextualButtonGroup.dump(pw); 1161 mEdgeBackGestureHandler.dump(pw); 1162 } 1163 1164 @Override onApplyWindowInsets(WindowInsets insets)1165 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 1166 int leftInset = insets.getSystemWindowInsetLeft(); 1167 int rightInset = insets.getSystemWindowInsetRight(); 1168 setPadding(leftInset, insets.getSystemWindowInsetTop(), rightInset, 1169 insets.getSystemWindowInsetBottom()); 1170 // we're passing the insets onto the gesture handler since the back arrow is only 1171 // conditionally added and doesn't always get all the insets. 1172 mEdgeBackGestureHandler.setInsets(leftInset, rightInset); 1173 1174 // this allows assist handle to be drawn outside its bound so that it can align screen 1175 // bottom by translating its y position. 1176 final boolean shouldClip = 1177 !isGesturalMode(mNavBarMode) || insets.getSystemWindowInsetBottom() == 0; 1178 setClipChildren(shouldClip); 1179 setClipToPadding(shouldClip); 1180 1181 return super.onApplyWindowInsets(insets); 1182 } 1183 addPipExclusionBoundsChangeListener(Pip pip)1184 void addPipExclusionBoundsChangeListener(Pip pip) { 1185 pip.addPipExclusionBoundsChangeListener(mPipListener); 1186 } 1187 removePipExclusionBoundsChangeListener(Pip pip)1188 void removePipExclusionBoundsChangeListener(Pip pip) { 1189 pip.removePipExclusionBoundsChangeListener(mPipListener); 1190 } 1191 registerBackAnimation(BackAnimation backAnimation)1192 void registerBackAnimation(BackAnimation backAnimation) { 1193 mEdgeBackGestureHandler.setBackAnimation(backAnimation); 1194 } 1195 dumpButton(PrintWriter pw, String caption, ButtonDispatcher button)1196 private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) { 1197 pw.print(" " + caption + ": "); 1198 if (button == null) { 1199 pw.print("null"); 1200 } else { 1201 pw.print(visibilityToString(button.getVisibility()) 1202 + " alpha=" + button.getAlpha() 1203 ); 1204 } 1205 pw.println(); 1206 } 1207 1208 public interface OnVerticalChangedListener { onVerticalChanged(boolean isVertical)1209 void onVerticalChanged(boolean isVertical); 1210 } 1211 1212 private final Consumer<Boolean> mDockedListener = exists -> post(() -> { 1213 mDockedStackExists = exists; 1214 updateRecentsIcon(); 1215 }); 1216 1217 private final Consumer<Rect> mPipListener = bounds -> post(() -> { 1218 mEdgeBackGestureHandler.setPipStashExclusionBounds(bounds); 1219 }); 1220 1221 1222 interface UpdateActiveTouchRegionsCallback { update()1223 void update(); 1224 } 1225 } 1226