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