OSDN Git Service

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