OSDN Git Service

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