OSDN Git Service

Merge "Import translations. DO NOT MERGE" into nyc-mr2-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.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
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
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
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 {
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
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
226     public BarTransitions getBarTransitions() {
227         return mBarTransitions;
228     }
229
230     public LightBarTransitionsController getLightTransitionsController() {
231         return mBarTransitions.getLightTransitionsController();
232     }
233
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
243     public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) {
244         mOnVerticalChangedListener = onVerticalChangedListener;
245         notifyVerticalChangedListener(mVertical);
246     }
247
248     @Override
249     public boolean onTouchEvent(MotionEvent event) {
250         if (mGestureHelper.onTouchEvent(event)) {
251             return true;
252         }
253         return super.onTouchEvent(event);
254     }
255
256     @Override
257     public boolean onInterceptTouchEvent(MotionEvent event) {
258         return mGestureHelper.onInterceptTouchEvent(event);
259     }
260
261     public void abortCurrentGesture() {
262         getHomeButton().abortCurrentGesture();
263     }
264
265     private H mHandler = new H();
266
267     public View getCurrentView() {
268         return mCurrentView;
269     }
270
271     public View[] getAllViews() {
272         return mRotatedViews;
273     }
274
275     public ButtonDispatcher getRecentsButton() {
276         return mButtonDispatchers.get(R.id.recent_apps);
277     }
278
279     public ButtonDispatcher getMenuButton() {
280         return mButtonDispatchers.get(R.id.menu);
281     }
282
283     public ButtonDispatcher getBackButton() {
284         return mButtonDispatchers.get(R.id.back);
285     }
286
287     public ButtonDispatcher getHomeButton() {
288         return mButtonDispatchers.get(R.id.home);
289     }
290
291     public ButtonDispatcher getImeSwitchButton() {
292         return mButtonDispatchers.get(R.id.ime_switcher);
293     }
294
295     public ButtonDispatcher getAccessibilityButton() {
296         return mButtonDispatchers.get(R.id.accessibility_button);
297     }
298
299     public SparseArray<ButtonDispatcher> getButtonDispatchers() {
300         return mButtonDispatchers;
301     }
302
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
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
348     private KeyButtonDrawable getDrawable(Context ctx, @DrawableRes int lightIcon,
349             @DrawableRes int darkIcon) {
350         return getDrawable(ctx, ctx, lightIcon, darkIcon);
351     }
352
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
360     public void setLayoutDirection(int layoutDirection) {
361         // Reload all the icons
362         updateIcons(getContext(), Configuration.EMPTY, mConfiguration);
363
364         super.setLayoutDirection(layoutDirection);
365     }
366
367     public void notifyScreenOn(boolean screenOn) {
368         mScreenOn = screenOn;
369         setDisabledFlags(mDisabledFlags, true);
370     }
371
372     public void setNavigationIconHints(int hints) {
373         setNavigationIconHints(hints, false);
374     }
375
376     private KeyButtonDrawable getBackIconWithAlt(boolean carMode, boolean landscape) {
377         return landscape
378                 ? carMode ? mBackAltLandCarModeIcon : mBackAltLandIcon
379                 : carMode ? mBackAltCarModeIcon : mBackAltIcon;
380     }
381
382     private KeyButtonDrawable getBackIcon(boolean carMode, boolean landscape) {
383         return landscape
384                 ? carMode ? mBackLandCarModeIcon : mBackLandIcon
385                 : carMode ? mBackCarModeIcon : mBackIcon;
386     }
387
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
438     public void setDisabledFlags(int disabledFlags) {
439         setDisabledFlags(disabledFlags, false);
440     }
441
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
475     private boolean inLockTask() {
476         try {
477             return ActivityManager.getService().isInLockTaskMode();
478         } catch (RemoteException e) {
479             return false;
480         }
481     }
482
483     public void setLayoutTransitionsEnabled(boolean enabled) {
484         mLayoutTransitionsEnabled = enabled;
485         updateLayoutTransitionsEnabled();
486     }
487
488     public void setWakeAndUnlocking(boolean wakeAndUnlocking) {
489         setUseFadingAnimations(wakeAndUnlocking);
490         mWakeAndUnlocking = wakeAndUnlocking;
491         updateLayoutTransitionsEnabled();
492     }
493
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
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
530     public void setMenuVisibility(final boolean show) {
531         setMenuVisibility(show, false);
532     }
533
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
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
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
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
583     public boolean needsReorient(int rotation) {
584         return mCurrentRotation != rotation;
585     }
586
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
602     private void updateRecentsIcon() {
603         getRecentsButton().setImageDrawable(mDockedStackExists ? mDockedIcon : mRecentIcon);
604         mBarTransitions.reapplyDarkIntensity();
605     }
606
607     public boolean isVertical() {
608         return mVertical;
609     }
610
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
633     public void onKeyguardOccludedChanged(boolean keyguardOccluded) {
634     }
635
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
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
659     private void notifyVerticalChangedListener(boolean newVertical) {
660         if (mOnVerticalChangedListener != null) {
661             mOnVerticalChangedListener.onVerticalChanged(newVertical);
662         }
663     }
664
665     @Override
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      */
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
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
739     private void postCheckForInvalidLayout(final String how) {
740         mHandler.obtainMessage(MSG_CHECK_INVALID_LAYOUT, 0, 0, how).sendToTarget();
741     }
742
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
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
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
772     public void onPluginConnected(NavGesture plugin, Context context) {
773         mGestureHelper = plugin.getGestureHelper();
774         updateTaskSwitchHelper();
775     }
776
777     @Override
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
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
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 {
836         void onVerticalChanged(boolean isVertical);
837     }
838
839 }