OSDN Git Service

DO NOT MERGE. Grant MMS Uri permissions as the calling UID.
[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 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 }