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