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