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 android.animation.LayoutTransition; 20 import android.animation.LayoutTransition.TransitionListener; 21 import android.animation.ObjectAnimator; 22 import android.animation.TimeInterpolator; 23 import android.animation.ValueAnimator; 24 import android.annotation.DrawableRes; 25 import android.app.ActivityManager; 26 import android.app.StatusBarManager; 27 import android.content.Context; 28 import android.content.res.Configuration; 29 import android.graphics.Point; 30 import android.graphics.Rect; 31 import android.os.Handler; 32 import android.os.Message; 33 import android.os.RemoteException; 34 import android.util.AttributeSet; 35 import android.util.Log; 36 import android.util.SparseArray; 37 import android.view.ContextThemeWrapper; 38 import android.view.Display; 39 import android.view.MotionEvent; 40 import android.view.Surface; 41 import android.view.View; 42 import android.view.ViewGroup; 43 import android.view.WindowManager; 44 import android.view.inputmethod.InputMethodManager; 45 import android.widget.FrameLayout; 46 47 import com.android.settingslib.Utils; 48 import com.android.systemui.Dependency; 49 import com.android.systemui.DockedStackExistsListener; 50 import com.android.systemui.R; 51 import com.android.systemui.RecentsComponent; 52 import com.android.systemui.plugins.PluginListener; 53 import com.android.systemui.plugins.PluginManager; 54 import com.android.systemui.plugins.statusbar.phone.NavGesture; 55 import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper; 56 import com.android.systemui.stackdivider.Divider; 57 import com.android.systemui.statusbar.policy.DeadZone; 58 import com.android.systemui.statusbar.policy.KeyButtonDrawable; 59 60 import java.io.FileDescriptor; 61 import java.io.PrintWriter; 62 63 public class NavigationBarView extends FrameLayout implements PluginListener<NavGesture> { 64 final static boolean DEBUG = false; 65 final static String TAG = "StatusBar/NavBarView"; 66 67 // slippery nav bar when everything is disabled, e.g. during setup 68 final static boolean SLIPPERY_WHEN_DISABLED = true; 69 70 final static boolean ALTERNATE_CAR_MODE_UI = false; 71 72 final Display mDisplay; 73 View mCurrentView = null; 74 View[] mRotatedViews = new View[4]; 75 76 boolean mVertical; 77 boolean mScreenOn; 78 private int mCurrentRotation = -1; 79 80 boolean mShowMenu; 81 boolean mShowAccessibilityButton; 82 boolean mLongClickableAccessibilityButton; 83 int mDisabledFlags = 0; 84 int mNavigationIconHints = 0; 85 86 private KeyButtonDrawable mBackIcon, mBackLandIcon, mBackAltIcon, mBackAltLandIcon; 87 private KeyButtonDrawable mBackCarModeIcon, mBackLandCarModeIcon; 88 private KeyButtonDrawable mBackAltCarModeIcon, mBackAltLandCarModeIcon; 89 private KeyButtonDrawable mHomeDefaultIcon, mHomeCarModeIcon; 90 private KeyButtonDrawable mRecentIcon; 91 private KeyButtonDrawable mDockedIcon; 92 private KeyButtonDrawable mImeIcon; 93 private KeyButtonDrawable mMenuIcon; 94 private KeyButtonDrawable mAccessibilityIcon; 95 96 private GestureHelper mGestureHelper; 97 private DeadZone mDeadZone; 98 private final NavigationBarTransitions mBarTransitions; 99 100 // workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288) 101 final static boolean WORKAROUND_INVALID_LAYOUT = true; 102 final static int MSG_CHECK_INVALID_LAYOUT = 8686; 103 104 // performs manual animation in sync with layout transitions 105 private final NavTransitionListener mTransitionListener = new NavTransitionListener(); 106 107 private OnVerticalChangedListener mOnVerticalChangedListener; 108 private boolean mLayoutTransitionsEnabled = true; 109 private boolean mWakeAndUnlocking; 110 private boolean mUseCarModeUi = false; 111 private boolean mInCarMode = false; 112 private boolean mDockedStackExists; 113 114 private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>(); 115 private Configuration mConfiguration; 116 117 private NavigationBarInflaterView mNavigationInflaterView; 118 private RecentsComponent mRecentsComponent; 119 private Divider mDivider; 120 121 private class NavTransitionListener implements TransitionListener { 122 private boolean mBackTransitioning; 123 private boolean mHomeAppearing; 124 private long mStartDelay; 125 private long mDuration; 126 private TimeInterpolator mInterpolator; 127 128 @Override startTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType)129 public void startTransition(LayoutTransition transition, ViewGroup container, 130 View view, int transitionType) { 131 if (view.getId() == R.id.back) { 132 mBackTransitioning = true; 133 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) { 134 mHomeAppearing = true; 135 mStartDelay = transition.getStartDelay(transitionType); 136 mDuration = transition.getDuration(transitionType); 137 mInterpolator = transition.getInterpolator(transitionType); 138 } 139 } 140 141 @Override endTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType)142 public void endTransition(LayoutTransition transition, ViewGroup container, 143 View view, int transitionType) { 144 if (view.getId() == R.id.back) { 145 mBackTransitioning = false; 146 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) { 147 mHomeAppearing = false; 148 } 149 } 150 onBackAltCleared()151 public void onBackAltCleared() { 152 ButtonDispatcher backButton = getBackButton(); 153 154 // When dismissing ime during unlock, force the back button to run the same appearance 155 // animation as home (if we catch this condition early enough). 156 if (!mBackTransitioning && backButton.getVisibility() == VISIBLE 157 && mHomeAppearing && getHomeButton().getAlpha() == 0) { 158 getBackButton().setAlpha(0); 159 ValueAnimator a = ObjectAnimator.ofFloat(backButton, "alpha", 0, 1); 160 a.setStartDelay(mStartDelay); 161 a.setDuration(mDuration); 162 a.setInterpolator(mInterpolator); 163 a.start(); 164 } 165 } 166 } 167 168 private final OnClickListener mImeSwitcherClickListener = new OnClickListener() { 169 @Override 170 public void onClick(View view) { 171 mContext.getSystemService(InputMethodManager.class) 172 .showInputMethodPicker(true /* showAuxiliarySubtypes */); 173 } 174 }; 175 176 private class H extends Handler { handleMessage(Message m)177 public void handleMessage(Message m) { 178 switch (m.what) { 179 case MSG_CHECK_INVALID_LAYOUT: 180 final String how = "" + m.obj; 181 final int w = getWidth(); 182 final int h = getHeight(); 183 final int vw = getCurrentView().getWidth(); 184 final int vh = getCurrentView().getHeight(); 185 186 if (h != vh || w != vw) { 187 Log.w(TAG, String.format( 188 "*** Invalid layout in navigation bar (%s this=%dx%d cur=%dx%d)", 189 how, w, h, vw, vh)); 190 if (WORKAROUND_INVALID_LAYOUT) { 191 requestLayout(); 192 } 193 } 194 break; 195 } 196 } 197 } 198 NavigationBarView(Context context, AttributeSet attrs)199 public NavigationBarView(Context context, AttributeSet attrs) { 200 super(context, attrs); 201 202 mDisplay = ((WindowManager) context.getSystemService( 203 Context.WINDOW_SERVICE)).getDefaultDisplay(); 204 205 mVertical = false; 206 mShowMenu = false; 207 208 mShowAccessibilityButton = false; 209 mLongClickableAccessibilityButton = false; 210 211 mConfiguration = new Configuration(); 212 mConfiguration.updateFrom(context.getResources().getConfiguration()); 213 updateIcons(context, Configuration.EMPTY, mConfiguration); 214 215 mBarTransitions = new NavigationBarTransitions(this); 216 217 mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back)); 218 mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home)); 219 mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps)); 220 mButtonDispatchers.put(R.id.menu, new ButtonDispatcher(R.id.menu)); 221 mButtonDispatchers.put(R.id.ime_switcher, new ButtonDispatcher(R.id.ime_switcher)); 222 mButtonDispatchers.put(R.id.accessibility_button, 223 new ButtonDispatcher(R.id.accessibility_button)); 224 } 225 getBarTransitions()226 public BarTransitions getBarTransitions() { 227 return mBarTransitions; 228 } 229 getLightTransitionsController()230 public LightBarTransitionsController getLightTransitionsController() { 231 return mBarTransitions.getLightTransitionsController(); 232 } 233 setComponents(RecentsComponent recentsComponent, Divider divider)234 public void setComponents(RecentsComponent recentsComponent, Divider divider) { 235 mRecentsComponent = recentsComponent; 236 mDivider = divider; 237 if (mGestureHelper instanceof NavigationBarGestureHelper) { 238 ((NavigationBarGestureHelper) mGestureHelper).setComponents( 239 recentsComponent, divider, this); 240 } 241 } 242 setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener)243 public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) { 244 mOnVerticalChangedListener = onVerticalChangedListener; 245 notifyVerticalChangedListener(mVertical); 246 } 247 248 @Override onTouchEvent(MotionEvent event)249 public boolean onTouchEvent(MotionEvent event) { 250 if (mGestureHelper.onTouchEvent(event)) { 251 return true; 252 } 253 return super.onTouchEvent(event); 254 } 255 256 @Override onInterceptTouchEvent(MotionEvent event)257 public boolean onInterceptTouchEvent(MotionEvent event) { 258 return mGestureHelper.onInterceptTouchEvent(event); 259 } 260 abortCurrentGesture()261 public void abortCurrentGesture() { 262 getHomeButton().abortCurrentGesture(); 263 } 264 265 private H mHandler = new H(); 266 getCurrentView()267 public View getCurrentView() { 268 return mCurrentView; 269 } 270 getAllViews()271 public View[] getAllViews() { 272 return mRotatedViews; 273 } 274 getRecentsButton()275 public ButtonDispatcher getRecentsButton() { 276 return mButtonDispatchers.get(R.id.recent_apps); 277 } 278 getMenuButton()279 public ButtonDispatcher getMenuButton() { 280 return mButtonDispatchers.get(R.id.menu); 281 } 282 getBackButton()283 public ButtonDispatcher getBackButton() { 284 return mButtonDispatchers.get(R.id.back); 285 } 286 getHomeButton()287 public ButtonDispatcher getHomeButton() { 288 return mButtonDispatchers.get(R.id.home); 289 } 290 getImeSwitchButton()291 public ButtonDispatcher getImeSwitchButton() { 292 return mButtonDispatchers.get(R.id.ime_switcher); 293 } 294 getAccessibilityButton()295 public ButtonDispatcher getAccessibilityButton() { 296 return mButtonDispatchers.get(R.id.accessibility_button); 297 } 298 getButtonDispatchers()299 public SparseArray<ButtonDispatcher> getButtonDispatchers() { 300 return mButtonDispatchers; 301 } 302 updateCarModeIcons(Context ctx)303 private void updateCarModeIcons(Context ctx) { 304 mBackCarModeIcon = getDrawable(ctx, 305 R.drawable.ic_sysbar_back_carmode, R.drawable.ic_sysbar_back_carmode); 306 mBackLandCarModeIcon = mBackCarModeIcon; 307 mBackAltCarModeIcon = getDrawable(ctx, 308 R.drawable.ic_sysbar_back_ime_carmode, R.drawable.ic_sysbar_back_ime_carmode); 309 mBackAltLandCarModeIcon = mBackAltCarModeIcon; 310 mHomeCarModeIcon = getDrawable(ctx, 311 R.drawable.ic_sysbar_home_carmode, R.drawable.ic_sysbar_home_carmode); 312 } 313 updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig)314 private void updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig) { 315 if (oldConfig.orientation != newConfig.orientation 316 || oldConfig.densityDpi != newConfig.densityDpi) { 317 mDockedIcon = getDrawable(ctx, 318 R.drawable.ic_sysbar_docked, R.drawable.ic_sysbar_docked_dark); 319 } 320 if (oldConfig.densityDpi != newConfig.densityDpi) { 321 mBackIcon = getDrawable(ctx, R.drawable.ic_sysbar_back, R.drawable.ic_sysbar_back_dark); 322 mBackLandIcon = mBackIcon; 323 mBackAltIcon = getDrawable(ctx, 324 R.drawable.ic_sysbar_back_ime, R.drawable.ic_sysbar_back_ime_dark); 325 mBackAltLandIcon = mBackAltIcon; 326 327 mHomeDefaultIcon = getDrawable(ctx, 328 R.drawable.ic_sysbar_home, R.drawable.ic_sysbar_home_dark); 329 mRecentIcon = getDrawable(ctx, 330 R.drawable.ic_sysbar_recent, R.drawable.ic_sysbar_recent_dark); 331 mMenuIcon = getDrawable(ctx, R.drawable.ic_sysbar_menu, R.drawable.ic_sysbar_menu_dark); 332 mAccessibilityIcon = getDrawable(ctx, R.drawable.ic_sysbar_accessibility_button, 333 R.drawable.ic_sysbar_accessibility_button_dark); 334 335 int dualToneDarkTheme = Utils.getThemeAttr(ctx, R.attr.darkIconTheme); 336 int dualToneLightTheme = Utils.getThemeAttr(ctx, R.attr.lightIconTheme); 337 Context darkContext = new ContextThemeWrapper(ctx, dualToneDarkTheme); 338 Context lightContext = new ContextThemeWrapper(ctx, dualToneLightTheme); 339 mImeIcon = getDrawable(darkContext, lightContext, 340 R.drawable.ic_ime_switcher_default, R.drawable.ic_ime_switcher_default); 341 342 if (ALTERNATE_CAR_MODE_UI) { 343 updateCarModeIcons(ctx); 344 } 345 } 346 } 347 getDrawable(Context ctx, @DrawableRes int lightIcon, @DrawableRes int darkIcon)348 private KeyButtonDrawable getDrawable(Context ctx, @DrawableRes int lightIcon, 349 @DrawableRes int darkIcon) { 350 return getDrawable(ctx, ctx, lightIcon, darkIcon); 351 } 352 getDrawable(Context darkContext, Context lightContext, @DrawableRes int lightIcon, @DrawableRes int darkIcon)353 private KeyButtonDrawable getDrawable(Context darkContext, Context lightContext, 354 @DrawableRes int lightIcon, @DrawableRes int darkIcon) { 355 return KeyButtonDrawable.create(lightContext.getDrawable(lightIcon), 356 darkContext.getDrawable(darkIcon)); 357 } 358 359 @Override setLayoutDirection(int layoutDirection)360 public void setLayoutDirection(int layoutDirection) { 361 // Reload all the icons 362 updateIcons(getContext(), Configuration.EMPTY, mConfiguration); 363 364 super.setLayoutDirection(layoutDirection); 365 } 366 notifyScreenOn(boolean screenOn)367 public void notifyScreenOn(boolean screenOn) { 368 mScreenOn = screenOn; 369 setDisabledFlags(mDisabledFlags, true); 370 } 371 setNavigationIconHints(int hints)372 public void setNavigationIconHints(int hints) { 373 setNavigationIconHints(hints, false); 374 } 375 getBackIconWithAlt(boolean carMode, boolean landscape)376 private KeyButtonDrawable getBackIconWithAlt(boolean carMode, boolean landscape) { 377 return landscape 378 ? carMode ? mBackAltLandCarModeIcon : mBackAltLandIcon 379 : carMode ? mBackAltCarModeIcon : mBackAltIcon; 380 } 381 getBackIcon(boolean carMode, boolean landscape)382 private KeyButtonDrawable getBackIcon(boolean carMode, boolean landscape) { 383 return landscape 384 ? carMode ? mBackLandCarModeIcon : mBackLandIcon 385 : carMode ? mBackCarModeIcon : mBackIcon; 386 } 387 setNavigationIconHints(int hints, boolean force)388 public void setNavigationIconHints(int hints, boolean force) { 389 if (!force && hints == mNavigationIconHints) return; 390 final boolean backAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; 391 if ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0 && !backAlt) { 392 mTransitionListener.onBackAltCleared(); 393 } 394 if (DEBUG) { 395 android.widget.Toast.makeText(getContext(), 396 "Navigation icon hints = " + hints, 397 500).show(); 398 } 399 400 mNavigationIconHints = hints; 401 402 // We have to replace or restore the back and home button icons when exiting or entering 403 // carmode, respectively. Recents are not available in CarMode in nav bar so change 404 // to recent icon is not required. 405 KeyButtonDrawable backIcon = (backAlt) 406 ? getBackIconWithAlt(mUseCarModeUi, mVertical) 407 : getBackIcon(mUseCarModeUi, mVertical); 408 409 getBackButton().setImageDrawable(backIcon); 410 411 updateRecentsIcon(); 412 413 if (mUseCarModeUi) { 414 getHomeButton().setImageDrawable(mHomeCarModeIcon); 415 } else { 416 getHomeButton().setImageDrawable(mHomeDefaultIcon); 417 } 418 419 // The Accessibility button always overrides the appearance of the IME switcher 420 final boolean showImeButton = 421 !mShowAccessibilityButton && ((hints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) 422 != 0); 423 getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE); 424 getImeSwitchButton().setImageDrawable(mImeIcon); 425 426 // Update menu button in case the IME state has changed. 427 setMenuVisibility(mShowMenu, true); 428 getMenuButton().setImageDrawable(mMenuIcon); 429 430 setAccessibilityButtonState(mShowAccessibilityButton, mLongClickableAccessibilityButton); 431 getAccessibilityButton().setImageDrawable(mAccessibilityIcon); 432 433 setDisabledFlags(mDisabledFlags, true); 434 435 mBarTransitions.reapplyDarkIntensity(); 436 } 437 setDisabledFlags(int disabledFlags)438 public void setDisabledFlags(int disabledFlags) { 439 setDisabledFlags(disabledFlags, false); 440 } 441 setDisabledFlags(int disabledFlags, boolean force)442 public void setDisabledFlags(int disabledFlags, boolean force) { 443 if (!force && mDisabledFlags == disabledFlags) return; 444 445 mDisabledFlags = disabledFlags; 446 447 final boolean disableHome = ((disabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0); 448 449 // Always disable recents when alternate car mode UI is active. 450 boolean disableRecent = mUseCarModeUi 451 || ((disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0); 452 final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0) 453 && ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) == 0); 454 455 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons); 456 if (navButtons != null) { 457 LayoutTransition lt = navButtons.getLayoutTransition(); 458 if (lt != null) { 459 if (!lt.getTransitionListeners().contains(mTransitionListener)) { 460 lt.addTransitionListener(mTransitionListener); 461 } 462 } 463 } 464 if (inLockTask() && disableRecent && !disableHome) { 465 // Don't hide recents when in lock task, it is used for exiting. 466 // Unless home is hidden, then in DPM locked mode and no exit available. 467 disableRecent = false; 468 } 469 470 getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE); 471 getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE); 472 getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE); 473 } 474 inLockTask()475 private boolean inLockTask() { 476 try { 477 return ActivityManager.getService().isInLockTaskMode(); 478 } catch (RemoteException e) { 479 return false; 480 } 481 } 482 setLayoutTransitionsEnabled(boolean enabled)483 public void setLayoutTransitionsEnabled(boolean enabled) { 484 mLayoutTransitionsEnabled = enabled; 485 updateLayoutTransitionsEnabled(); 486 } 487 setWakeAndUnlocking(boolean wakeAndUnlocking)488 public void setWakeAndUnlocking(boolean wakeAndUnlocking) { 489 setUseFadingAnimations(wakeAndUnlocking); 490 mWakeAndUnlocking = wakeAndUnlocking; 491 updateLayoutTransitionsEnabled(); 492 } 493 updateLayoutTransitionsEnabled()494 private void updateLayoutTransitionsEnabled() { 495 boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled; 496 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons); 497 LayoutTransition lt = navButtons.getLayoutTransition(); 498 if (lt != null) { 499 if (enabled) { 500 lt.enableTransitionType(LayoutTransition.APPEARING); 501 lt.enableTransitionType(LayoutTransition.DISAPPEARING); 502 lt.enableTransitionType(LayoutTransition.CHANGE_APPEARING); 503 lt.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); 504 } else { 505 lt.disableTransitionType(LayoutTransition.APPEARING); 506 lt.disableTransitionType(LayoutTransition.DISAPPEARING); 507 lt.disableTransitionType(LayoutTransition.CHANGE_APPEARING); 508 lt.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); 509 } 510 } 511 } 512 setUseFadingAnimations(boolean useFadingAnimations)513 private void setUseFadingAnimations(boolean useFadingAnimations) { 514 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) ((ViewGroup) getParent()) 515 .getLayoutParams(); 516 if (lp != null) { 517 boolean old = lp.windowAnimations != 0; 518 if (!old && useFadingAnimations) { 519 lp.windowAnimations = R.style.Animation_NavigationBarFadeIn; 520 } else if (old && !useFadingAnimations) { 521 lp.windowAnimations = 0; 522 } else { 523 return; 524 } 525 WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE); 526 wm.updateViewLayout((View) getParent(), lp); 527 } 528 } 529 setMenuVisibility(final boolean show)530 public void setMenuVisibility(final boolean show) { 531 setMenuVisibility(show, false); 532 } 533 setMenuVisibility(final boolean show, final boolean force)534 public void setMenuVisibility(final boolean show, final boolean force) { 535 if (!force && mShowMenu == show) return; 536 537 mShowMenu = show; 538 539 // Only show Menu if IME switcher and Accessibility button not shown. 540 final boolean shouldShow = mShowMenu && !mShowAccessibilityButton && 541 ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0); 542 543 getMenuButton().setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE); 544 } 545 setAccessibilityButtonState(final boolean visible, final boolean longClickable)546 public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) { 547 mShowAccessibilityButton = visible; 548 mLongClickableAccessibilityButton = longClickable; 549 if (visible) { 550 // Accessibility button overrides Menu and IME switcher buttons. 551 setMenuVisibility(false, true); 552 getImeSwitchButton().setVisibility(View.INVISIBLE); 553 } 554 555 getAccessibilityButton().setVisibility(visible ? View.VISIBLE : View.INVISIBLE); 556 getAccessibilityButton().setLongClickable(longClickable); 557 } 558 559 @Override onFinishInflate()560 public void onFinishInflate() { 561 mNavigationInflaterView = (NavigationBarInflaterView) findViewById( 562 R.id.navigation_inflater); 563 updateRotatedViews(); 564 mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers); 565 566 getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener); 567 568 DockedStackExistsListener.register(exists -> mHandler.post(() -> { 569 mDockedStackExists = exists; 570 updateRecentsIcon(); 571 })); 572 } 573 updateRotatedViews()574 void updateRotatedViews() { 575 mRotatedViews[Surface.ROTATION_0] = 576 mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0); 577 mRotatedViews[Surface.ROTATION_270] = 578 mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90); 579 580 updateCurrentView(); 581 } 582 needsReorient(int rotation)583 public boolean needsReorient(int rotation) { 584 return mCurrentRotation != rotation; 585 } 586 updateCurrentView()587 private void updateCurrentView() { 588 final int rot = mDisplay.getRotation(); 589 for (int i=0; i<4; i++) { 590 mRotatedViews[i].setVisibility(View.GONE); 591 } 592 mCurrentView = mRotatedViews[rot]; 593 mCurrentView.setVisibility(View.VISIBLE); 594 mNavigationInflaterView.setAlternativeOrder(rot == Surface.ROTATION_90); 595 for (int i = 0; i < mButtonDispatchers.size(); i++) { 596 mButtonDispatchers.valueAt(i).setCurrentView(mCurrentView); 597 } 598 updateLayoutTransitionsEnabled(); 599 mCurrentRotation = rot; 600 } 601 updateRecentsIcon()602 private void updateRecentsIcon() { 603 getRecentsButton().setImageDrawable(mDockedStackExists ? mDockedIcon : mRecentIcon); 604 mBarTransitions.reapplyDarkIntensity(); 605 } 606 isVertical()607 public boolean isVertical() { 608 return mVertical; 609 } 610 reorient()611 public void reorient() { 612 updateCurrentView(); 613 614 mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone); 615 ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone); 616 mDeadZone.setDisplayRotation(mCurrentRotation); 617 618 // force the low profile & disabled states into compliance 619 mBarTransitions.init(); 620 setDisabledFlags(mDisabledFlags, true /* force */); 621 setMenuVisibility(mShowMenu, true /* force */); 622 623 if (DEBUG) { 624 Log.d(TAG, "reorient(): rot=" + mCurrentRotation); 625 } 626 627 updateTaskSwitchHelper(); 628 setNavigationIconHints(mNavigationIconHints, true); 629 630 getHomeButton().setVertical(mVertical); 631 } 632 onKeyguardOccludedChanged(boolean keyguardOccluded)633 public void onKeyguardOccludedChanged(boolean keyguardOccluded) { 634 } 635 updateTaskSwitchHelper()636 private void updateTaskSwitchHelper() { 637 if (mGestureHelper == null) return; 638 boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL); 639 mGestureHelper.setBarState(mVertical, isRtl); 640 } 641 642 @Override onSizeChanged(int w, int h, int oldw, int oldh)643 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 644 if (DEBUG) Log.d(TAG, String.format( 645 "onSizeChanged: (%dx%d) old: (%dx%d)", w, h, oldw, oldh)); 646 647 final boolean newVertical = w > 0 && h > w; 648 if (newVertical != mVertical) { 649 mVertical = newVertical; 650 //Log.v(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w, mVertical?"y":"n")); 651 reorient(); 652 notifyVerticalChangedListener(newVertical); 653 } 654 655 postCheckForInvalidLayout("sizeChanged"); 656 super.onSizeChanged(w, h, oldw, oldh); 657 } 658 notifyVerticalChangedListener(boolean newVertical)659 private void notifyVerticalChangedListener(boolean newVertical) { 660 if (mOnVerticalChangedListener != null) { 661 mOnVerticalChangedListener.onVerticalChanged(newVertical); 662 } 663 } 664 665 @Override onConfigurationChanged(Configuration newConfig)666 protected void onConfigurationChanged(Configuration newConfig) { 667 super.onConfigurationChanged(newConfig); 668 boolean uiCarModeChanged = updateCarMode(newConfig); 669 updateTaskSwitchHelper(); 670 updateIcons(getContext(), mConfiguration, newConfig); 671 updateRecentsIcon(); 672 if (uiCarModeChanged || mConfiguration.densityDpi != newConfig.densityDpi) { 673 // If car mode or density changes, we need to reset the icons. 674 setNavigationIconHints(mNavigationIconHints, true); 675 } 676 mConfiguration.updateFrom(newConfig); 677 } 678 679 /** 680 * If the configuration changed, update the carmode and return that it was updated. 681 */ updateCarMode(Configuration newConfig)682 private boolean updateCarMode(Configuration newConfig) { 683 boolean uiCarModeChanged = false; 684 if (newConfig != null) { 685 int uiMode = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK; 686 final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR); 687 688 if (isCarMode != mInCarMode) { 689 mInCarMode = isCarMode; 690 getHomeButton().setCarMode(isCarMode); 691 692 if (ALTERNATE_CAR_MODE_UI) { 693 mUseCarModeUi = isCarMode; 694 uiCarModeChanged = true; 695 } else { 696 // Don't use car mode behavior if ALTERNATE_CAR_MODE_UI not set. 697 mUseCarModeUi = false; 698 } 699 } 700 } 701 return uiCarModeChanged; 702 } 703 704 /* 705 @Override 706 protected void onLayout (boolean changed, int left, int top, int right, int bottom) { 707 if (DEBUG) Log.d(TAG, String.format( 708 "onLayout: %s (%d,%d,%d,%d)", 709 changed?"changed":"notchanged", left, top, right, bottom)); 710 super.onLayout(changed, left, top, right, bottom); 711 } 712 713 // uncomment this for extra defensiveness in WORKAROUND_INVALID_LAYOUT situations: if all else 714 // fails, any touch on the display will fix the layout. 715 @Override 716 public boolean onInterceptTouchEvent(MotionEvent ev) { 717 if (DEBUG) Log.d(TAG, "onInterceptTouchEvent: " + ev.toString()); 718 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 719 postCheckForInvalidLayout("touch"); 720 } 721 return super.onInterceptTouchEvent(ev); 722 } 723 */ 724 725 getResourceName(int resId)726 private String getResourceName(int resId) { 727 if (resId != 0) { 728 final android.content.res.Resources res = getContext().getResources(); 729 try { 730 return res.getResourceName(resId); 731 } catch (android.content.res.Resources.NotFoundException ex) { 732 return "(unknown)"; 733 } 734 } else { 735 return "(null)"; 736 } 737 } 738 postCheckForInvalidLayout(final String how)739 private void postCheckForInvalidLayout(final String how) { 740 mHandler.obtainMessage(MSG_CHECK_INVALID_LAYOUT, 0, 0, how).sendToTarget(); 741 } 742 visibilityToString(int vis)743 private static String visibilityToString(int vis) { 744 switch (vis) { 745 case View.INVISIBLE: 746 return "INVISIBLE"; 747 case View.GONE: 748 return "GONE"; 749 default: 750 return "VISIBLE"; 751 } 752 } 753 754 @Override onAttachedToWindow()755 protected void onAttachedToWindow() { 756 super.onAttachedToWindow(); 757 onPluginDisconnected(null); // Create default gesture helper 758 Dependency.get(PluginManager.class).addPluginListener(this, 759 NavGesture.class, false /* Only one */); 760 } 761 762 @Override onDetachedFromWindow()763 protected void onDetachedFromWindow() { 764 super.onDetachedFromWindow(); 765 Dependency.get(PluginManager.class).removePluginListener(this); 766 if (mGestureHelper != null) { 767 mGestureHelper.destroy(); 768 } 769 } 770 771 @Override onPluginConnected(NavGesture plugin, Context context)772 public void onPluginConnected(NavGesture plugin, Context context) { 773 mGestureHelper = plugin.getGestureHelper(); 774 updateTaskSwitchHelper(); 775 } 776 777 @Override onPluginDisconnected(NavGesture plugin)778 public void onPluginDisconnected(NavGesture plugin) { 779 NavigationBarGestureHelper defaultHelper = new NavigationBarGestureHelper(getContext()); 780 defaultHelper.setComponents(mRecentsComponent, mDivider, this); 781 if (mGestureHelper != null) { 782 mGestureHelper.destroy(); 783 } 784 mGestureHelper = defaultHelper; 785 updateTaskSwitchHelper(); 786 } 787 dump(FileDescriptor fd, PrintWriter pw, String[] args)788 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 789 pw.println("NavigationBarView {"); 790 final Rect r = new Rect(); 791 final Point size = new Point(); 792 mDisplay.getRealSize(size); 793 794 pw.println(String.format(" this: " + StatusBar.viewInfo(this) 795 + " " + visibilityToString(getVisibility()))); 796 797 getWindowVisibleDisplayFrame(r); 798 final boolean offscreen = r.right > size.x || r.bottom > size.y; 799 pw.println(" window: " 800 + r.toShortString() 801 + " " + visibilityToString(getWindowVisibility()) 802 + (offscreen ? " OFFSCREEN!" : "")); 803 804 pw.println(String.format(" mCurrentView: id=%s (%dx%d) %s", 805 getResourceName(getCurrentView().getId()), 806 getCurrentView().getWidth(), getCurrentView().getHeight(), 807 visibilityToString(getCurrentView().getVisibility()))); 808 809 pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s", 810 mDisabledFlags, 811 mVertical ? "true" : "false", 812 mShowMenu ? "true" : "false")); 813 814 dumpButton(pw, "back", getBackButton()); 815 dumpButton(pw, "home", getHomeButton()); 816 dumpButton(pw, "rcnt", getRecentsButton()); 817 dumpButton(pw, "menu", getMenuButton()); 818 dumpButton(pw, "a11y", getAccessibilityButton()); 819 820 pw.println(" }"); 821 } 822 dumpButton(PrintWriter pw, String caption, ButtonDispatcher button)823 private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) { 824 pw.print(" " + caption + ": "); 825 if (button == null) { 826 pw.print("null"); 827 } else { 828 pw.print(visibilityToString(button.getVisibility()) 829 + " alpha=" + button.getAlpha() 830 ); 831 } 832 pw.println(); 833 } 834 835 public interface OnVerticalChangedListener { onVerticalChanged(boolean isVertical)836 void onVerticalChanged(boolean isVertical); 837 } 838 839 } 840