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