1 /* 2 * Copyright (C) 2008 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.statusbar.phone; 18 19 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; 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_NOTIFICATION_PANEL_EXPANDED; 24 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; 25 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED; 26 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; 27 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SEARCH_DISABLED; 28 import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode; 29 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; 30 31 import android.animation.LayoutTransition; 32 import android.animation.LayoutTransition.TransitionListener; 33 import android.animation.ObjectAnimator; 34 import android.animation.PropertyValuesHolder; 35 import android.animation.TimeInterpolator; 36 import android.animation.ValueAnimator; 37 import android.annotation.DrawableRes; 38 import android.app.StatusBarManager; 39 import android.content.Context; 40 import android.content.res.Configuration; 41 import android.graphics.Canvas; 42 import android.graphics.Point; 43 import android.graphics.Rect; 44 import android.graphics.Region; 45 import android.graphics.Region.Op; 46 import android.os.Bundle; 47 import android.util.AttributeSet; 48 import android.util.Log; 49 import android.util.SparseArray; 50 import android.view.Display; 51 import android.view.MotionEvent; 52 import android.view.Surface; 53 import android.view.View; 54 import android.view.ViewGroup; 55 import android.view.ViewTreeObserver.InternalInsetsInfo; 56 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; 57 import android.view.WindowInsets; 58 import android.view.WindowManager; 59 import android.view.accessibility.AccessibilityNodeInfo; 60 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; 61 import android.view.inputmethod.InputMethodManager; 62 import android.widget.FrameLayout; 63 64 import com.android.internal.annotations.VisibleForTesting; 65 import com.android.systemui.Dependency; 66 import com.android.systemui.DockedStackExistsListener; 67 import com.android.systemui.Interpolators; 68 import com.android.systemui.R; 69 import com.android.systemui.SysUiServiceProvider; 70 import com.android.systemui.assist.AssistManager; 71 import com.android.systemui.recents.OverviewProxyService; 72 import com.android.systemui.recents.Recents; 73 import com.android.systemui.recents.RecentsOnboarding; 74 import com.android.systemui.shared.system.ActivityManagerWrapper; 75 import com.android.systemui.shared.system.QuickStepContract; 76 import com.android.systemui.shared.system.WindowManagerWrapper; 77 import com.android.systemui.statusbar.policy.DeadZone; 78 import com.android.systemui.statusbar.policy.KeyButtonDrawable; 79 80 import java.io.FileDescriptor; 81 import java.io.PrintWriter; 82 import java.util.function.Consumer; 83 84 public class NavigationBarView extends FrameLayout implements 85 NavigationModeController.ModeChangedListener { 86 final static boolean DEBUG = false; 87 final static String TAG = "StatusBar/NavBarView"; 88 89 // slippery nav bar when everything is disabled, e.g. during setup 90 final static boolean SLIPPERY_WHEN_DISABLED = true; 91 92 final static boolean ALTERNATE_CAR_MODE_UI = false; 93 94 View mCurrentView = null; 95 private View mVertical; 96 private View mHorizontal; 97 98 /** Indicates that navigation bar is vertical. */ 99 private boolean mIsVertical; 100 private int mCurrentRotation = -1; 101 102 boolean mLongClickableAccessibilityButton; 103 int mDisabledFlags = 0; 104 int mNavigationIconHints = 0; 105 private int mNavBarMode = NAV_BAR_MODE_3BUTTON; 106 107 private Rect mHomeButtonBounds = new Rect(); 108 private Rect mBackButtonBounds = new Rect(); 109 private Rect mRecentsButtonBounds = new Rect(); 110 private Rect mRotationButtonBounds = new Rect(); 111 private final Region mActiveRegion = new Region(); 112 private int[] mTmpPosition = new int[2]; 113 114 private KeyButtonDrawable mBackIcon; 115 private KeyButtonDrawable mHomeDefaultIcon; 116 private KeyButtonDrawable mRecentIcon; 117 private KeyButtonDrawable mDockedIcon; 118 119 private final EdgeBackGestureHandler mEdgeBackGestureHandler; 120 private final DeadZone mDeadZone; 121 private boolean mDeadZoneConsuming = false; 122 private final NavigationBarTransitions mBarTransitions; 123 private final OverviewProxyService mOverviewProxyService; 124 125 // performs manual animation in sync with layout transitions 126 private final NavTransitionListener mTransitionListener = new NavTransitionListener(); 127 128 private OnVerticalChangedListener mOnVerticalChangedListener; 129 private boolean mLayoutTransitionsEnabled = true; 130 private boolean mWakeAndUnlocking; 131 private boolean mUseCarModeUi = false; 132 private boolean mInCarMode = false; 133 private boolean mDockedStackExists; 134 private boolean mImeVisible; 135 136 private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>(); 137 private final ContextualButtonGroup mContextualButtonGroup; 138 private Configuration mConfiguration; 139 private Configuration mTmpLastConfiguration; 140 141 private NavigationBarInflaterView mNavigationInflaterView; 142 private RecentsOnboarding mRecentsOnboarding; 143 private NotificationPanelView mPanelView; 144 private FloatingRotationButton mFloatingRotationButton; 145 private RotationButtonController mRotationButtonController; 146 147 private NavBarTintController mTintController; 148 149 /** 150 * Helper that is responsible for showing the right toast when a disallowed activity operation 151 * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in 152 * fully locked mode we only show that unlocking is blocked. 153 */ 154 private ScreenPinningNotify mScreenPinningNotify; 155 156 private class NavTransitionListener implements TransitionListener { 157 private boolean mBackTransitioning; 158 private boolean mHomeAppearing; 159 private long mStartDelay; 160 private long mDuration; 161 private TimeInterpolator mInterpolator; 162 163 @Override startTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType)164 public void startTransition(LayoutTransition transition, ViewGroup container, 165 View view, int transitionType) { 166 if (view.getId() == R.id.back) { 167 mBackTransitioning = true; 168 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) { 169 mHomeAppearing = true; 170 mStartDelay = transition.getStartDelay(transitionType); 171 mDuration = transition.getDuration(transitionType); 172 mInterpolator = transition.getInterpolator(transitionType); 173 } 174 } 175 176 @Override endTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType)177 public void endTransition(LayoutTransition transition, ViewGroup container, 178 View view, int transitionType) { 179 if (view.getId() == R.id.back) { 180 mBackTransitioning = false; 181 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) { 182 mHomeAppearing = false; 183 } 184 } 185 onBackAltCleared()186 public void onBackAltCleared() { 187 ButtonDispatcher backButton = getBackButton(); 188 189 // When dismissing ime during unlock, force the back button to run the same appearance 190 // animation as home (if we catch this condition early enough). 191 if (!mBackTransitioning && backButton.getVisibility() == VISIBLE 192 && mHomeAppearing && getHomeButton().getAlpha() == 0) { 193 getBackButton().setAlpha(0); 194 ValueAnimator a = ObjectAnimator.ofFloat(backButton, "alpha", 0, 1); 195 a.setStartDelay(mStartDelay); 196 a.setDuration(mDuration); 197 a.setInterpolator(mInterpolator); 198 a.start(); 199 } 200 } 201 } 202 203 private final OnClickListener mImeSwitcherClickListener = new OnClickListener() { 204 @Override 205 public void onClick(View view) { 206 mContext.getSystemService(InputMethodManager.class).showInputMethodPickerFromSystem( 207 true /* showAuxiliarySubtypes */, getContext().getDisplayId()); 208 } 209 }; 210 211 private final AccessibilityDelegate mQuickStepAccessibilityDelegate 212 = new AccessibilityDelegate() { 213 private AccessibilityAction mToggleOverviewAction; 214 215 @Override 216 public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { 217 super.onInitializeAccessibilityNodeInfo(host, info); 218 if (mToggleOverviewAction == null) { 219 mToggleOverviewAction = new AccessibilityAction(R.id.action_toggle_overview, 220 getContext().getString(R.string.quick_step_accessibility_toggle_overview)); 221 } 222 info.addAction(mToggleOverviewAction); 223 } 224 225 @Override 226 public boolean performAccessibilityAction(View host, int action, Bundle args) { 227 if (action == R.id.action_toggle_overview) { 228 SysUiServiceProvider.getComponent(getContext(), Recents.class) 229 .toggleRecentApps(); 230 } else { 231 return super.performAccessibilityAction(host, action, args); 232 } 233 return true; 234 } 235 }; 236 237 private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener = info -> { 238 // When the nav bar is in 2-button or 3-button mode, or when IME is visible in fully 239 // gestural mode, the entire nav bar should be touchable. 240 if (!isGesturalMode(mNavBarMode) || mImeVisible) { 241 info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); 242 return; 243 } 244 245 info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION); 246 ButtonDispatcher imeSwitchButton = getImeSwitchButton(); 247 if (imeSwitchButton.getVisibility() == VISIBLE) { 248 // If the IME is not up, but the ime switch button is visible, then make sure that 249 // button is touchable 250 int[] loc = new int[2]; 251 View buttonView = imeSwitchButton.getCurrentView(); 252 buttonView.getLocationInWindow(loc); 253 info.touchableRegion.set(loc[0], loc[1], loc[0] + buttonView.getWidth(), 254 loc[1] + buttonView.getHeight()); 255 return; 256 } 257 info.touchableRegion.setEmpty(); 258 }; 259 NavigationBarView(Context context, AttributeSet attrs)260 public NavigationBarView(Context context, AttributeSet attrs) { 261 super(context, attrs); 262 263 mIsVertical = false; 264 mLongClickableAccessibilityButton = false; 265 mNavBarMode = Dependency.get(NavigationModeController.class).addListener(this); 266 boolean isGesturalMode = isGesturalMode(mNavBarMode); 267 268 // Set up the context group of buttons 269 mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container); 270 final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher, 271 R.drawable.ic_ime_switcher_default); 272 final RotationContextButton rotateSuggestionButton = new RotationContextButton( 273 R.id.rotate_suggestion, R.drawable.ic_sysbar_rotate_button); 274 final ContextualButton accessibilityButton = 275 new ContextualButton(R.id.accessibility_button, 276 R.drawable.ic_sysbar_accessibility_button); 277 mContextualButtonGroup.addButton(imeSwitcherButton); 278 if (!isGesturalMode) { 279 mContextualButtonGroup.addButton(rotateSuggestionButton); 280 } 281 mContextualButtonGroup.addButton(accessibilityButton); 282 283 mOverviewProxyService = Dependency.get(OverviewProxyService.class); 284 mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService); 285 mFloatingRotationButton = new FloatingRotationButton(context); 286 mRotationButtonController = new RotationButtonController(context, 287 R.style.RotateButtonCCWStart90, 288 isGesturalMode ? mFloatingRotationButton : rotateSuggestionButton); 289 290 final ContextualButton backButton = new ContextualButton(R.id.back, 0); 291 292 mConfiguration = new Configuration(); 293 mTmpLastConfiguration = new Configuration(); 294 mConfiguration.updateFrom(context.getResources().getConfiguration()); 295 296 mScreenPinningNotify = new ScreenPinningNotify(mContext); 297 mBarTransitions = new NavigationBarTransitions(this); 298 299 mButtonDispatchers.put(R.id.back, backButton); 300 mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home)); 301 mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle)); 302 mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps)); 303 mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton); 304 mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton); 305 mButtonDispatchers.put(R.id.rotate_suggestion, rotateSuggestionButton); 306 mButtonDispatchers.put(R.id.menu_container, mContextualButtonGroup); 307 mDeadZone = new DeadZone(this); 308 309 mEdgeBackGestureHandler = new EdgeBackGestureHandler(context, mOverviewProxyService); 310 mTintController = new NavBarTintController(this, getLightTransitionsController()); 311 } 312 getTintController()313 public NavBarTintController getTintController() { 314 return mTintController; 315 } 316 getBarTransitions()317 public NavigationBarTransitions getBarTransitions() { 318 return mBarTransitions; 319 } 320 getLightTransitionsController()321 public LightBarTransitionsController getLightTransitionsController() { 322 return mBarTransitions.getLightTransitionsController(); 323 } 324 setComponents(NotificationPanelView panel, AssistManager assistManager)325 public void setComponents(NotificationPanelView panel, AssistManager assistManager) { 326 mPanelView = panel; 327 updatePanelSystemUiStateFlags(); 328 } 329 330 @Override dispatchDraw(Canvas canvas)331 protected void dispatchDraw(Canvas canvas) { 332 super.dispatchDraw(canvas); 333 mTintController.onDraw(); 334 } 335 setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener)336 public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) { 337 mOnVerticalChangedListener = onVerticalChangedListener; 338 notifyVerticalChangedListener(mIsVertical); 339 } 340 341 @Override onInterceptTouchEvent(MotionEvent event)342 public boolean onInterceptTouchEvent(MotionEvent event) { 343 return shouldDeadZoneConsumeTouchEvents(event) || super.onInterceptTouchEvent(event); 344 } 345 346 @Override onTouchEvent(MotionEvent event)347 public boolean onTouchEvent(MotionEvent event) { 348 shouldDeadZoneConsumeTouchEvents(event); 349 return super.onTouchEvent(event); 350 } 351 onSystemUiVisibilityChanged(int systemUiVisibility)352 void onSystemUiVisibilityChanged(int systemUiVisibility) { 353 mEdgeBackGestureHandler.onSystemUiVisibilityChanged(systemUiVisibility); 354 } 355 onBarTransition(int newMode)356 void onBarTransition(int newMode) { 357 if (newMode == MODE_OPAQUE) { 358 // If the nav bar background is opaque, stop auto tinting since we know the icons are 359 // showing over a dark background 360 mTintController.stop(); 361 getLightTransitionsController().setIconsDark(false /* dark */, true /* animate */); 362 } else { 363 mTintController.start(); 364 } 365 } 366 shouldDeadZoneConsumeTouchEvents(MotionEvent event)367 private boolean shouldDeadZoneConsumeTouchEvents(MotionEvent event) { 368 int action = event.getActionMasked(); 369 if (action == MotionEvent.ACTION_DOWN) { 370 mDeadZoneConsuming = false; 371 } 372 if (mDeadZone.onTouchEvent(event) || mDeadZoneConsuming) { 373 switch (action) { 374 case MotionEvent.ACTION_DOWN: 375 // Allow gestures starting in the deadzone to be slippery 376 setSlippery(true); 377 mDeadZoneConsuming = true; 378 break; 379 case MotionEvent.ACTION_CANCEL: 380 case MotionEvent.ACTION_UP: 381 // When a gesture started in the deadzone is finished, restore slippery state 382 updateSlippery(); 383 mDeadZoneConsuming = false; 384 break; 385 } 386 return true; 387 } 388 return false; 389 } 390 abortCurrentGesture()391 public void abortCurrentGesture() { 392 getHomeButton().abortCurrentGesture(); 393 } 394 getCurrentView()395 public View getCurrentView() { 396 return mCurrentView; 397 } 398 getRotationButtonController()399 public RotationButtonController getRotationButtonController() { 400 return mRotationButtonController; 401 } 402 getFloatingRotationButton()403 public FloatingRotationButton getFloatingRotationButton() { 404 return mFloatingRotationButton; 405 } 406 getRecentsButton()407 public ButtonDispatcher getRecentsButton() { 408 return mButtonDispatchers.get(R.id.recent_apps); 409 } 410 getBackButton()411 public ButtonDispatcher getBackButton() { 412 return mButtonDispatchers.get(R.id.back); 413 } 414 getHomeButton()415 public ButtonDispatcher getHomeButton() { 416 return mButtonDispatchers.get(R.id.home); 417 } 418 getImeSwitchButton()419 public ButtonDispatcher getImeSwitchButton() { 420 return mButtonDispatchers.get(R.id.ime_switcher); 421 } 422 getAccessibilityButton()423 public ButtonDispatcher getAccessibilityButton() { 424 return mButtonDispatchers.get(R.id.accessibility_button); 425 } 426 getRotateSuggestionButton()427 public RotationContextButton getRotateSuggestionButton() { 428 return (RotationContextButton) mButtonDispatchers.get(R.id.rotate_suggestion); 429 } 430 getHomeHandle()431 public ButtonDispatcher getHomeHandle() { 432 return mButtonDispatchers.get(R.id.home_handle); 433 } 434 getButtonDispatchers()435 public SparseArray<ButtonDispatcher> getButtonDispatchers() { 436 return mButtonDispatchers; 437 } 438 isRecentsButtonVisible()439 public boolean isRecentsButtonVisible() { 440 return getRecentsButton().getVisibility() == View.VISIBLE; 441 } 442 isOverviewEnabled()443 public boolean isOverviewEnabled() { 444 return (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) == 0; 445 } 446 isQuickStepSwipeUpEnabled()447 public boolean isQuickStepSwipeUpEnabled() { 448 return mOverviewProxyService.shouldShowSwipeUpUI() && isOverviewEnabled(); 449 } 450 reloadNavIcons()451 private void reloadNavIcons() { 452 updateIcons(Configuration.EMPTY); 453 } 454 updateIcons(Configuration oldConfig)455 private void updateIcons(Configuration oldConfig) { 456 final boolean orientationChange = oldConfig.orientation != mConfiguration.orientation; 457 final boolean densityChange = oldConfig.densityDpi != mConfiguration.densityDpi; 458 final boolean dirChange = oldConfig.getLayoutDirection() != mConfiguration.getLayoutDirection(); 459 460 if (orientationChange || densityChange) { 461 mDockedIcon = getDrawable(R.drawable.ic_sysbar_docked); 462 mHomeDefaultIcon = getHomeDrawable(); 463 } 464 if (densityChange || dirChange) { 465 mRecentIcon = getDrawable(R.drawable.ic_sysbar_recent); 466 mContextualButtonGroup.updateIcons(); 467 } 468 if (orientationChange || densityChange || dirChange) { 469 mBackIcon = getBackDrawable(); 470 } 471 } 472 getBackDrawable()473 public KeyButtonDrawable getBackDrawable() { 474 KeyButtonDrawable drawable = getDrawable(getBackDrawableRes()); 475 orientBackButton(drawable); 476 return drawable; 477 } 478 getBackDrawableRes()479 public @DrawableRes int getBackDrawableRes() { 480 return chooseNavigationIconDrawableRes(R.drawable.ic_sysbar_back, 481 R.drawable.ic_sysbar_back_quick_step); 482 } 483 getHomeDrawable()484 public KeyButtonDrawable getHomeDrawable() { 485 final boolean quickStepEnabled = mOverviewProxyService.shouldShowSwipeUpUI(); 486 KeyButtonDrawable drawable = quickStepEnabled 487 ? getDrawable(R.drawable.ic_sysbar_home_quick_step) 488 : getDrawable(R.drawable.ic_sysbar_home); 489 orientHomeButton(drawable); 490 return drawable; 491 } 492 orientBackButton(KeyButtonDrawable drawable)493 private void orientBackButton(KeyButtonDrawable drawable) { 494 final boolean useAltBack = 495 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 496 final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; 497 float degrees = useAltBack ? (isRtl ? 90 : -90) : 0; 498 if (drawable.getRotation() == degrees) { 499 return; 500 } 501 502 if (isGesturalMode(mNavBarMode)) { 503 drawable.setRotation(degrees); 504 return; 505 } 506 507 // Animate the back button's rotation to the new degrees and only in portrait move up the 508 // back button to line up with the other buttons 509 float targetY = !mOverviewProxyService.shouldShowSwipeUpUI() && !mIsVertical && useAltBack 510 ? - getResources().getDimension(R.dimen.navbar_back_button_ime_offset) 511 : 0; 512 ObjectAnimator navBarAnimator = ObjectAnimator.ofPropertyValuesHolder(drawable, 513 PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_ROTATE, degrees), 514 PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_TRANSLATE_Y, targetY)); 515 navBarAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 516 navBarAnimator.setDuration(200); 517 navBarAnimator.start(); 518 } 519 orientHomeButton(KeyButtonDrawable drawable)520 private void orientHomeButton(KeyButtonDrawable drawable) { 521 drawable.setRotation(mIsVertical ? 90 : 0); 522 } 523 chooseNavigationIconDrawable(@rawableRes int icon, @DrawableRes int quickStepIcon)524 private KeyButtonDrawable chooseNavigationIconDrawable(@DrawableRes int icon, 525 @DrawableRes int quickStepIcon) { 526 return getDrawable(chooseNavigationIconDrawableRes(icon, quickStepIcon)); 527 } 528 chooseNavigationIconDrawableRes(@rawableRes int icon, @DrawableRes int quickStepIcon)529 private @DrawableRes int chooseNavigationIconDrawableRes(@DrawableRes int icon, 530 @DrawableRes int quickStepIcon) { 531 final boolean quickStepEnabled = mOverviewProxyService.shouldShowSwipeUpUI(); 532 return quickStepEnabled ? quickStepIcon : icon; 533 } 534 getDrawable(@rawableRes int icon)535 private KeyButtonDrawable getDrawable(@DrawableRes int icon) { 536 return KeyButtonDrawable.create(mContext, icon, true /* hasShadow */); 537 } 538 getDrawable(@rawableRes int icon, boolean hasShadow)539 private KeyButtonDrawable getDrawable(@DrawableRes int icon, boolean hasShadow) { 540 return KeyButtonDrawable.create(mContext, icon, hasShadow); 541 } 542 setWindowVisible(boolean visible)543 public void setWindowVisible(boolean visible) { 544 mTintController.setWindowVisible(visible); 545 mRotationButtonController.onNavigationBarWindowVisibilityChange(visible); 546 } 547 548 @Override setLayoutDirection(int layoutDirection)549 public void setLayoutDirection(int layoutDirection) { 550 reloadNavIcons(); 551 552 super.setLayoutDirection(layoutDirection); 553 } 554 setNavigationIconHints(int hints)555 public void setNavigationIconHints(int hints) { 556 if (hints == mNavigationIconHints) return; 557 final boolean newBackAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 558 final boolean oldBackAlt = 559 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 560 if (newBackAlt != oldBackAlt) { 561 onImeVisibilityChanged(newBackAlt); 562 } 563 564 if (DEBUG) { 565 android.widget.Toast.makeText(getContext(), 566 "Navigation icon hints = " + hints, 567 500).show(); 568 } 569 mNavigationIconHints = hints; 570 updateNavButtonIcons(); 571 } 572 onImeVisibilityChanged(boolean visible)573 private void onImeVisibilityChanged(boolean visible) { 574 if (!visible) { 575 mTransitionListener.onBackAltCleared(); 576 } 577 mImeVisible = visible; 578 mRotationButtonController.getRotationButton().setCanShowRotationButton(!mImeVisible); 579 } 580 setDisabledFlags(int disabledFlags)581 public void setDisabledFlags(int disabledFlags) { 582 if (mDisabledFlags == disabledFlags) return; 583 584 final boolean overviewEnabledBefore = isOverviewEnabled(); 585 mDisabledFlags = disabledFlags; 586 587 // Update icons if overview was just enabled to ensure the correct icons are present 588 if (!overviewEnabledBefore && isOverviewEnabled()) { 589 reloadNavIcons(); 590 } 591 592 updateNavButtonIcons(); 593 updateSlippery(); 594 setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled()); 595 updateDisabledSystemUiStateFlags(); 596 } 597 updateNavButtonIcons()598 public void updateNavButtonIcons() { 599 // We have to replace or restore the back and home button icons when exiting or entering 600 // carmode, respectively. Recents are not available in CarMode in nav bar so change 601 // to recent icon is not required. 602 final boolean useAltBack = 603 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 604 KeyButtonDrawable backIcon = mBackIcon; 605 orientBackButton(backIcon); 606 KeyButtonDrawable homeIcon = mHomeDefaultIcon; 607 if (!mUseCarModeUi) { 608 orientHomeButton(homeIcon); 609 } 610 getHomeButton().setImageDrawable(homeIcon); 611 getBackButton().setImageDrawable(backIcon); 612 613 updateRecentsIcon(); 614 615 // Update IME button visibility, a11y and rotate button always overrides the appearance 616 mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, 617 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0); 618 619 mBarTransitions.reapplyDarkIntensity(); 620 621 boolean disableHome = isGesturalMode(mNavBarMode) 622 || ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0); 623 624 // Always disable recents when alternate car mode UI is active and for secondary displays. 625 boolean disableRecent = isRecentsButtonDisabled(); 626 627 // Disable the home handle if both hone and recents are disabled 628 boolean disableHomeHandle = disableRecent 629 && ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0); 630 631 boolean disableBack = !useAltBack && (isGesturalMode(mNavBarMode) 632 || ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)); 633 634 // When screen pinning, don't hide back and home when connected service or back and 635 // recents buttons when disconnected from launcher service in screen pinning mode, 636 // as they are used for exiting. 637 final boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive(); 638 if (mOverviewProxyService.isEnabled()) { 639 // Force disable recents when not in legacy mode 640 disableRecent |= !QuickStepContract.isLegacyMode(mNavBarMode); 641 if (pinningActive && !QuickStepContract.isGesturalMode(mNavBarMode)) { 642 disableBack = disableHome = false; 643 } 644 } else if (pinningActive) { 645 disableBack = disableRecent = false; 646 } 647 648 ViewGroup navButtons = getCurrentView().findViewById(R.id.nav_buttons); 649 if (navButtons != null) { 650 LayoutTransition lt = navButtons.getLayoutTransition(); 651 if (lt != null) { 652 if (!lt.getTransitionListeners().contains(mTransitionListener)) { 653 lt.addTransitionListener(mTransitionListener); 654 } 655 } 656 } 657 658 getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE); 659 getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE); 660 getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE); 661 getHomeHandle().setVisibility(disableHomeHandle ? View.INVISIBLE : View.VISIBLE); 662 } 663 664 @VisibleForTesting isRecentsButtonDisabled()665 boolean isRecentsButtonDisabled() { 666 return mUseCarModeUi || !isOverviewEnabled() 667 || getContext().getDisplayId() != Display.DEFAULT_DISPLAY; 668 } 669 getContextDisplay()670 private Display getContextDisplay() { 671 return getContext().getDisplay(); 672 } 673 setLayoutTransitionsEnabled(boolean enabled)674 public void setLayoutTransitionsEnabled(boolean enabled) { 675 mLayoutTransitionsEnabled = enabled; 676 updateLayoutTransitionsEnabled(); 677 } 678 setWakeAndUnlocking(boolean wakeAndUnlocking)679 public void setWakeAndUnlocking(boolean wakeAndUnlocking) { 680 setUseFadingAnimations(wakeAndUnlocking); 681 mWakeAndUnlocking = wakeAndUnlocking; 682 updateLayoutTransitionsEnabled(); 683 } 684 updateLayoutTransitionsEnabled()685 private void updateLayoutTransitionsEnabled() { 686 boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled; 687 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons); 688 LayoutTransition lt = navButtons.getLayoutTransition(); 689 if (lt != null) { 690 if (enabled) { 691 lt.enableTransitionType(LayoutTransition.APPEARING); 692 lt.enableTransitionType(LayoutTransition.DISAPPEARING); 693 lt.enableTransitionType(LayoutTransition.CHANGE_APPEARING); 694 lt.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); 695 } else { 696 lt.disableTransitionType(LayoutTransition.APPEARING); 697 lt.disableTransitionType(LayoutTransition.DISAPPEARING); 698 lt.disableTransitionType(LayoutTransition.CHANGE_APPEARING); 699 lt.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); 700 } 701 } 702 } 703 setUseFadingAnimations(boolean useFadingAnimations)704 private void setUseFadingAnimations(boolean useFadingAnimations) { 705 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) ((ViewGroup) getParent()) 706 .getLayoutParams(); 707 if (lp != null) { 708 boolean old = lp.windowAnimations != 0; 709 if (!old && useFadingAnimations) { 710 lp.windowAnimations = R.style.Animation_NavigationBarFadeIn; 711 } else if (old && !useFadingAnimations) { 712 lp.windowAnimations = 0; 713 } else { 714 return; 715 } 716 WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE); 717 wm.updateViewLayout((View) getParent(), lp); 718 } 719 } 720 onStatusBarPanelStateChanged()721 public void onStatusBarPanelStateChanged() { 722 updateSlippery(); 723 updatePanelSystemUiStateFlags(); 724 } 725 updateDisabledSystemUiStateFlags()726 public void updateDisabledSystemUiStateFlags() { 727 int displayId = mContext.getDisplayId(); 728 mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_SCREEN_PINNING, 729 ActivityManagerWrapper.getInstance().isScreenPinningActive(), displayId); 730 mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_OVERVIEW_DISABLED, 731 (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0, displayId); 732 mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_HOME_DISABLED, 733 (mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0, displayId); 734 mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_SEARCH_DISABLED, 735 (mDisabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0, displayId); 736 } 737 updatePanelSystemUiStateFlags()738 public void updatePanelSystemUiStateFlags() { 739 int displayId = mContext.getDisplayId(); 740 if (mPanelView != null) { 741 mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED, 742 mPanelView.isFullyExpanded() && !mPanelView.isInSettings(), displayId); 743 mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED, 744 mPanelView.isInSettings(), displayId); 745 } 746 } 747 updateStates()748 public void updateStates() { 749 final boolean showSwipeUpUI = mOverviewProxyService.shouldShowSwipeUpUI(); 750 751 if (mNavigationInflaterView != null) { 752 // Reinflate the navbar if needed, no-op unless the swipe up state changes 753 mNavigationInflaterView.onLikelyDefaultLayoutChange(); 754 } 755 756 updateSlippery(); 757 reloadNavIcons(); 758 updateNavButtonIcons(); 759 setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled()); 760 WindowManagerWrapper.getInstance().setNavBarVirtualKeyHapticFeedbackEnabled(!showSwipeUpUI); 761 getHomeButton().setAccessibilityDelegate( 762 showSwipeUpUI ? mQuickStepAccessibilityDelegate : null); 763 } 764 765 /** 766 * Updates the {@link WindowManager.LayoutParams.FLAG_SLIPPERY} state dependent on if swipe up 767 * is enabled, or the notifications is fully opened without being in an animated state. If 768 * slippery is enabled, touch events will leave the nav bar window and enter into the fullscreen 769 * app/home window, if not nav bar will receive a cancelled touch event once gesture leaves bar. 770 */ updateSlippery()771 public void updateSlippery() { 772 setSlippery(!isQuickStepSwipeUpEnabled() || 773 (mPanelView.isFullyExpanded() && !mPanelView.isCollapsing())); 774 } 775 setSlippery(boolean slippery)776 private void setSlippery(boolean slippery) { 777 setWindowFlag(WindowManager.LayoutParams.FLAG_SLIPPERY, slippery); 778 } 779 setWindowFlag(int flags, boolean enable)780 private void setWindowFlag(int flags, boolean enable) { 781 final ViewGroup navbarView = ((ViewGroup) getParent()); 782 if (navbarView == null) { 783 return; 784 } 785 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView.getLayoutParams(); 786 if (lp == null || enable == ((lp.flags & flags) != 0)) { 787 return; 788 } 789 if (enable) { 790 lp.flags |= flags; 791 } else { 792 lp.flags &= ~flags; 793 } 794 WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); 795 wm.updateViewLayout(navbarView, lp); 796 } 797 798 @Override onNavigationModeChanged(int mode)799 public void onNavigationModeChanged(int mode) { 800 Context curUserCtx = Dependency.get(NavigationModeController.class).getCurrentUserContext(); 801 mNavBarMode = mode; 802 mBarTransitions.onNavigationModeChanged(mNavBarMode); 803 mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode, curUserCtx); 804 mRecentsOnboarding.onNavigationModeChanged(mNavBarMode); 805 getRotateSuggestionButton().onNavigationModeChanged(mNavBarMode); 806 807 // Color adaption is tied with showing home handle, only avaliable if visible 808 mTintController.onNavigationModeChanged(mNavBarMode); 809 if (isGesturalMode(mNavBarMode)) { 810 mTintController.start(); 811 } else { 812 mTintController.stop(); 813 } 814 } 815 setAccessibilityButtonState(final boolean visible, final boolean longClickable)816 public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) { 817 mLongClickableAccessibilityButton = longClickable; 818 getAccessibilityButton().setLongClickable(longClickable); 819 mContextualButtonGroup.setButtonVisibility(R.id.accessibility_button, visible); 820 } 821 hideRecentsOnboarding()822 void hideRecentsOnboarding() { 823 mRecentsOnboarding.hide(true); 824 } 825 826 @Override onFinishInflate()827 public void onFinishInflate() { 828 mNavigationInflaterView = findViewById(R.id.navigation_inflater); 829 mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers); 830 831 getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener); 832 833 DockedStackExistsListener.register(mDockedListener); 834 updateOrientationViews(); 835 reloadNavIcons(); 836 } 837 838 @Override onDraw(Canvas canvas)839 protected void onDraw(Canvas canvas) { 840 mDeadZone.onDraw(canvas); 841 super.onDraw(canvas); 842 } 843 844 @Override onLayout(boolean changed, int left, int top, int right, int bottom)845 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 846 super.onLayout(changed, left, top, right, bottom); 847 848 mActiveRegion.setEmpty(); 849 updateButtonLocation(getBackButton(), mBackButtonBounds, true); 850 updateButtonLocation(getHomeButton(), mHomeButtonBounds, false); 851 updateButtonLocation(getRecentsButton(), mRecentsButtonBounds, false); 852 updateButtonLocation(getRotateSuggestionButton(), mRotationButtonBounds, true); 853 // TODO: Handle button visibility changes 854 mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion); 855 mRecentsOnboarding.setNavBarHeight(getMeasuredHeight()); 856 } 857 updateButtonLocation(ButtonDispatcher button, Rect buttonBounds, boolean isActive)858 private void updateButtonLocation(ButtonDispatcher button, Rect buttonBounds, 859 boolean isActive) { 860 View view = button.getCurrentView(); 861 if (view == null) { 862 buttonBounds.setEmpty(); 863 return; 864 } 865 // Temporarily reset the translation back to origin to get the position in window 866 final float posX = view.getTranslationX(); 867 final float posY = view.getTranslationY(); 868 view.setTranslationX(0); 869 view.setTranslationY(0); 870 871 if (isActive) { 872 view.getLocationOnScreen(mTmpPosition); 873 buttonBounds.set(mTmpPosition[0], mTmpPosition[1], 874 mTmpPosition[0] + view.getMeasuredWidth(), 875 mTmpPosition[1] + view.getMeasuredHeight()); 876 mActiveRegion.op(buttonBounds, Op.UNION); 877 } 878 view.getLocationInWindow(mTmpPosition); 879 buttonBounds.set(mTmpPosition[0], mTmpPosition[1], 880 mTmpPosition[0] + view.getMeasuredWidth(), 881 mTmpPosition[1] + view.getMeasuredHeight()); 882 view.setTranslationX(posX); 883 view.setTranslationY(posY); 884 } 885 updateOrientationViews()886 private void updateOrientationViews() { 887 mHorizontal = findViewById(R.id.horizontal); 888 mVertical = findViewById(R.id.vertical); 889 890 updateCurrentView(); 891 } 892 needsReorient(int rotation)893 boolean needsReorient(int rotation) { 894 return mCurrentRotation != rotation; 895 } 896 updateCurrentView()897 private void updateCurrentView() { 898 resetViews(); 899 mCurrentView = mIsVertical ? mVertical : mHorizontal; 900 mCurrentView.setVisibility(View.VISIBLE); 901 mNavigationInflaterView.setVertical(mIsVertical); 902 mCurrentRotation = getContextDisplay().getRotation(); 903 mNavigationInflaterView.setAlternativeOrder(mCurrentRotation == Surface.ROTATION_90); 904 mNavigationInflaterView.updateButtonDispatchersCurrentView(); 905 updateLayoutTransitionsEnabled(); 906 } 907 resetViews()908 private void resetViews() { 909 mHorizontal.setVisibility(View.GONE); 910 mVertical.setVisibility(View.GONE); 911 } 912 updateRecentsIcon()913 private void updateRecentsIcon() { 914 mDockedIcon.setRotation(mDockedStackExists && mIsVertical ? 90 : 0); 915 getRecentsButton().setImageDrawable(mDockedStackExists ? mDockedIcon : mRecentIcon); 916 mBarTransitions.reapplyDarkIntensity(); 917 } 918 showPinningEnterExitToast(boolean entering)919 public void showPinningEnterExitToast(boolean entering) { 920 if (entering) { 921 mScreenPinningNotify.showPinningStartToast(); 922 } else { 923 mScreenPinningNotify.showPinningExitToast(); 924 } 925 } 926 showPinningEscapeToast()927 public void showPinningEscapeToast() { 928 mScreenPinningNotify.showEscapeToast( 929 mNavBarMode == NAV_BAR_MODE_GESTURAL, isRecentsButtonVisible()); 930 } 931 isVertical()932 public boolean isVertical() { 933 return mIsVertical; 934 } 935 reorient()936 public void reorient() { 937 updateCurrentView(); 938 939 ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone); 940 mDeadZone.onConfigurationChanged(mCurrentRotation); 941 942 // force the low profile & disabled states into compliance 943 mBarTransitions.init(); 944 945 if (DEBUG) { 946 Log.d(TAG, "reorient(): rot=" + mCurrentRotation); 947 } 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 } 989 990 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 991 } 992 notifyVerticalChangedListener(boolean newVertical)993 private void notifyVerticalChangedListener(boolean newVertical) { 994 if (mOnVerticalChangedListener != null) { 995 mOnVerticalChangedListener.onVerticalChanged(newVertical); 996 } 997 } 998 999 @Override onConfigurationChanged(Configuration newConfig)1000 protected void onConfigurationChanged(Configuration newConfig) { 1001 super.onConfigurationChanged(newConfig); 1002 mTmpLastConfiguration.updateFrom(mConfiguration); 1003 mConfiguration.updateFrom(newConfig); 1004 boolean uiCarModeChanged = updateCarMode(); 1005 updateIcons(mTmpLastConfiguration); 1006 updateRecentsIcon(); 1007 mRecentsOnboarding.onConfigurationChanged(mConfiguration); 1008 if (uiCarModeChanged || mTmpLastConfiguration.densityDpi != mConfiguration.densityDpi 1009 || mTmpLastConfiguration.getLayoutDirection() != mConfiguration.getLayoutDirection()) { 1010 // If car mode or density changes, we need to reset the icons. 1011 updateNavButtonIcons(); 1012 } 1013 } 1014 1015 /** 1016 * If the configuration changed, update the carmode and return that it was updated. 1017 */ updateCarMode()1018 private boolean updateCarMode() { 1019 boolean uiCarModeChanged = false; 1020 if (mConfiguration != null) { 1021 int uiMode = mConfiguration.uiMode & Configuration.UI_MODE_TYPE_MASK; 1022 final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR); 1023 1024 if (isCarMode != mInCarMode) { 1025 mInCarMode = isCarMode; 1026 if (ALTERNATE_CAR_MODE_UI) { 1027 mUseCarModeUi = isCarMode; 1028 uiCarModeChanged = true; 1029 } else { 1030 // Don't use car mode behavior if ALTERNATE_CAR_MODE_UI not set. 1031 mUseCarModeUi = false; 1032 } 1033 } 1034 } 1035 return uiCarModeChanged; 1036 } 1037 getResourceName(int resId)1038 private String getResourceName(int resId) { 1039 if (resId != 0) { 1040 final android.content.res.Resources res = getContext().getResources(); 1041 try { 1042 return res.getResourceName(resId); 1043 } catch (android.content.res.Resources.NotFoundException ex) { 1044 return "(unknown)"; 1045 } 1046 } else { 1047 return "(null)"; 1048 } 1049 } 1050 visibilityToString(int vis)1051 private static String visibilityToString(int vis) { 1052 switch (vis) { 1053 case View.INVISIBLE: 1054 return "INVISIBLE"; 1055 case View.GONE: 1056 return "GONE"; 1057 default: 1058 return "VISIBLE"; 1059 } 1060 } 1061 1062 @Override onAttachedToWindow()1063 protected void onAttachedToWindow() { 1064 super.onAttachedToWindow(); 1065 requestApplyInsets(); 1066 reorient(); 1067 onNavigationModeChanged(mNavBarMode); 1068 setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled()); 1069 if (mRotationButtonController != null) { 1070 mRotationButtonController.registerListeners(); 1071 } 1072 1073 mEdgeBackGestureHandler.onNavBarAttached(); 1074 getViewTreeObserver().addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener); 1075 } 1076 1077 @Override onDetachedFromWindow()1078 protected void onDetachedFromWindow() { 1079 super.onDetachedFromWindow(); 1080 Dependency.get(NavigationModeController.class).removeListener(this); 1081 setUpSwipeUpOnboarding(false); 1082 for (int i = 0; i < mButtonDispatchers.size(); ++i) { 1083 mButtonDispatchers.valueAt(i).onDestroy(); 1084 } 1085 if (mRotationButtonController != null) { 1086 mRotationButtonController.unregisterListeners(); 1087 } 1088 1089 mEdgeBackGestureHandler.onNavBarDetached(); 1090 getViewTreeObserver().removeOnComputeInternalInsetsListener( 1091 mOnComputeInternalInsetsListener); 1092 } 1093 setUpSwipeUpOnboarding(boolean connectedToOverviewProxy)1094 private void setUpSwipeUpOnboarding(boolean connectedToOverviewProxy) { 1095 if (connectedToOverviewProxy) { 1096 mRecentsOnboarding.onConnectedToLauncher(); 1097 } else { 1098 mRecentsOnboarding.onDisconnectedFromLauncher(); 1099 } 1100 } 1101 dump(FileDescriptor fd, PrintWriter pw, String[] args)1102 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1103 pw.println("NavigationBarView {"); 1104 final Rect r = new Rect(); 1105 final Point size = new Point(); 1106 getContextDisplay().getRealSize(size); 1107 1108 pw.println(String.format(" this: " + StatusBar.viewInfo(this) 1109 + " " + visibilityToString(getVisibility()))); 1110 1111 getWindowVisibleDisplayFrame(r); 1112 final boolean offscreen = r.right > size.x || r.bottom > size.y; 1113 pw.println(" window: " 1114 + r.toShortString() 1115 + " " + visibilityToString(getWindowVisibility()) 1116 + (offscreen ? " OFFSCREEN!" : "")); 1117 1118 pw.println(String.format(" mCurrentView: id=%s (%dx%d) %s %f", 1119 getResourceName(getCurrentView().getId()), 1120 getCurrentView().getWidth(), getCurrentView().getHeight(), 1121 visibilityToString(getCurrentView().getVisibility()), 1122 getCurrentView().getAlpha())); 1123 1124 pw.println(String.format(" disabled=0x%08x vertical=%s darkIntensity=%.2f", 1125 mDisabledFlags, 1126 mIsVertical ? "true" : "false", 1127 getLightTransitionsController().getCurrentDarkIntensity())); 1128 1129 dumpButton(pw, "back", getBackButton()); 1130 dumpButton(pw, "home", getHomeButton()); 1131 dumpButton(pw, "rcnt", getRecentsButton()); 1132 dumpButton(pw, "rota", getRotateSuggestionButton()); 1133 dumpButton(pw, "a11y", getAccessibilityButton()); 1134 1135 pw.println(" }"); 1136 1137 mContextualButtonGroup.dump(pw); 1138 mRecentsOnboarding.dump(pw); 1139 mTintController.dump(pw); 1140 mEdgeBackGestureHandler.dump(pw); 1141 } 1142 1143 @Override onApplyWindowInsets(WindowInsets insets)1144 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 1145 int leftInset = insets.getSystemWindowInsetLeft(); 1146 int rightInset = insets.getSystemWindowInsetRight(); 1147 setPadding(leftInset, insets.getSystemWindowInsetTop(), rightInset, 1148 insets.getSystemWindowInsetBottom()); 1149 // we're passing the insets onto the gesture handler since the back arrow is only 1150 // conditionally added and doesn't always get all the insets. 1151 mEdgeBackGestureHandler.setInsets(leftInset, rightInset); 1152 return super.onApplyWindowInsets(insets); 1153 } 1154 dumpButton(PrintWriter pw, String caption, ButtonDispatcher button)1155 private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) { 1156 pw.print(" " + caption + ": "); 1157 if (button == null) { 1158 pw.print("null"); 1159 } else { 1160 pw.print(visibilityToString(button.getVisibility()) 1161 + " alpha=" + button.getAlpha() 1162 ); 1163 } 1164 pw.println(); 1165 } 1166 1167 public interface OnVerticalChangedListener { onVerticalChanged(boolean isVertical)1168 void onVerticalChanged(boolean isVertical); 1169 } 1170 1171 private final Consumer<Boolean> mDockedListener = exists -> post(() -> { 1172 mDockedStackExists = exists; 1173 updateRecentsIcon(); 1174 }); 1175 } 1176