2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.systemui.statusbar.phone;
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;
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;
60 import java.io.FileDescriptor;
61 import java.io.PrintWriter;
63 public class NavigationBarView extends FrameLayout implements PluginListener<NavGesture> {
64 final static boolean DEBUG = false;
65 final static String TAG = "StatusBar/NavBarView";
67 // slippery nav bar when everything is disabled, e.g. during setup
68 final static boolean SLIPPERY_WHEN_DISABLED = true;
70 final static boolean ALTERNATE_CAR_MODE_UI = false;
72 final Display mDisplay;
73 View mCurrentView = null;
74 View[] mRotatedViews = new View[4];
77 private int mCurrentRotation = -1;
80 boolean mShowAccessibilityButton;
81 boolean mLongClickableAccessibilityButton;
82 int mDisabledFlags = 0;
83 int mNavigationIconHints = 0;
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;
95 private GestureHelper mGestureHelper;
96 private DeadZone mDeadZone;
97 private final NavigationBarTransitions mBarTransitions;
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;
103 // performs manual animation in sync with layout transitions
104 private final NavTransitionListener mTransitionListener = new NavTransitionListener();
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;
113 private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>();
114 private Configuration mConfiguration;
116 private NavigationBarInflaterView mNavigationInflaterView;
117 private RecentsComponent mRecentsComponent;
118 private Divider mDivider;
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;
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);
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;
150 public void onBackAltCleared() {
151 ButtonDispatcher backButton = getBackButton();
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);
167 private final OnClickListener mImeSwitcherClickListener = new OnClickListener() {
169 public void onClick(View view) {
170 mContext.getSystemService(InputMethodManager.class)
171 .showInputMethodPicker(true /* showAuxiliarySubtypes */);
175 private class H extends Handler {
176 public void handleMessage(Message m) {
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();
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)",
189 if (WORKAROUND_INVALID_LAYOUT) {
198 public NavigationBarView(Context context, AttributeSet attrs) {
199 super(context, attrs);
201 mDisplay = ((WindowManager) context.getSystemService(
202 Context.WINDOW_SERVICE)).getDefaultDisplay();
207 mShowAccessibilityButton = false;
208 mLongClickableAccessibilityButton = false;
210 mConfiguration = new Configuration();
211 mConfiguration.updateFrom(context.getResources().getConfiguration());
212 updateIcons(context, Configuration.EMPTY, mConfiguration);
214 mBarTransitions = new NavigationBarTransitions(this);
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));
225 public BarTransitions getBarTransitions() {
226 return mBarTransitions;
229 public LightBarTransitionsController getLightTransitionsController() {
230 return mBarTransitions.getLightTransitionsController();
233 public void setComponents(RecentsComponent recentsComponent, Divider divider) {
234 mRecentsComponent = recentsComponent;
236 if (mGestureHelper instanceof NavigationBarGestureHelper) {
237 ((NavigationBarGestureHelper) mGestureHelper).setComponents(
238 recentsComponent, divider, this);
242 public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) {
243 mOnVerticalChangedListener = onVerticalChangedListener;
244 notifyVerticalChangedListener(mVertical);
248 public boolean onTouchEvent(MotionEvent event) {
249 if (mGestureHelper.onTouchEvent(event)) {
252 return super.onTouchEvent(event);
256 public boolean onInterceptTouchEvent(MotionEvent event) {
257 return mGestureHelper.onInterceptTouchEvent(event);
260 public void abortCurrentGesture() {
261 getHomeButton().abortCurrentGesture();
264 private H mHandler = new H();
266 public View getCurrentView() {
270 public View[] getAllViews() {
271 return mRotatedViews;
274 public ButtonDispatcher getRecentsButton() {
275 return mButtonDispatchers.get(R.id.recent_apps);
278 public ButtonDispatcher getMenuButton() {
279 return mButtonDispatchers.get(R.id.menu);
282 public ButtonDispatcher getBackButton() {
283 return mButtonDispatchers.get(R.id.back);
286 public ButtonDispatcher getHomeButton() {
287 return mButtonDispatchers.get(R.id.home);
290 public ButtonDispatcher getImeSwitchButton() {
291 return mButtonDispatchers.get(R.id.ime_switcher);
294 public ButtonDispatcher getAccessibilityButton() {
295 return mButtonDispatchers.get(R.id.accessibility_button);
298 public SparseArray<ButtonDispatcher> getButtonDispatchers() {
299 return mButtonDispatchers;
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);
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);
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;
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);
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);
342 if (ALTERNATE_CAR_MODE_UI) {
343 updateCarModeIcons(ctx);
348 private KeyButtonDrawable getDrawable(Context ctx, @DrawableRes int lightIcon,
349 @DrawableRes int darkIcon) {
350 return getDrawable(ctx, ctx, lightIcon, darkIcon);
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));
360 public void setLayoutDirection(int layoutDirection) {
361 // Reload all the icons
362 updateIcons(getContext(), Configuration.EMPTY, mConfiguration);
364 super.setLayoutDirection(layoutDirection);
367 public void notifyScreenOn() {
368 setDisabledFlags(mDisabledFlags, true);
371 public void setNavigationIconHints(int hints) {
372 setNavigationIconHints(hints, false);
375 private KeyButtonDrawable getBackIconWithAlt(boolean carMode, boolean landscape) {
377 ? carMode ? mBackAltLandCarModeIcon : mBackAltLandIcon
378 : carMode ? mBackAltCarModeIcon : mBackAltIcon;
381 private KeyButtonDrawable getBackIcon(boolean carMode, boolean landscape) {
383 ? carMode ? mBackLandCarModeIcon : mBackLandIcon
384 : carMode ? mBackCarModeIcon : mBackIcon;
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();
394 android.widget.Toast.makeText(getContext(),
395 "Navigation icon hints = " + hints,
399 mNavigationIconHints = hints;
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);
408 getBackButton().setImageDrawable(backIcon);
413 getHomeButton().setImageDrawable(mHomeCarModeIcon);
415 getHomeButton().setImageDrawable(mHomeDefaultIcon);
418 // The Accessibility button always overrides the appearance of the IME switcher
419 final boolean showImeButton =
420 !mShowAccessibilityButton && ((hints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN)
422 getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE);
423 getImeSwitchButton().setImageDrawable(mImeIcon);
425 // Update menu button in case the IME state has changed.
426 setMenuVisibility(mShowMenu, true);
427 getMenuButton().setImageDrawable(mMenuIcon);
429 setAccessibilityButtonState(mShowAccessibilityButton, mLongClickableAccessibilityButton);
430 getAccessibilityButton().setImageDrawable(mAccessibilityIcon);
432 setDisabledFlags(mDisabledFlags, true);
434 mBarTransitions.reapplyDarkIntensity();
437 public void setDisabledFlags(int disabledFlags) {
438 setDisabledFlags(disabledFlags, false);
441 public void setDisabledFlags(int disabledFlags, boolean force) {
442 if (!force && mDisabledFlags == disabledFlags) return;
444 mDisabledFlags = disabledFlags;
446 final boolean disableHome = ((disabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
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);
454 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
455 if (navButtons != null) {
456 LayoutTransition lt = navButtons.getLayoutTransition();
458 if (!lt.getTransitionListeners().contains(mTransitionListener)) {
459 lt.addTransitionListener(mTransitionListener);
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;
469 getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE);
470 getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
471 getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
474 private boolean inLockTask() {
476 return ActivityManager.getService().isInLockTaskMode();
477 } catch (RemoteException e) {
482 public void setLayoutTransitionsEnabled(boolean enabled) {
483 mLayoutTransitionsEnabled = enabled;
484 updateLayoutTransitionsEnabled();
487 public void setWakeAndUnlocking(boolean wakeAndUnlocking) {
488 setUseFadingAnimations(wakeAndUnlocking);
489 mWakeAndUnlocking = wakeAndUnlocking;
490 updateLayoutTransitionsEnabled();
493 private void updateLayoutTransitionsEnabled() {
494 boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled;
495 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
496 LayoutTransition lt = navButtons.getLayoutTransition();
499 lt.enableTransitionType(LayoutTransition.APPEARING);
500 lt.enableTransitionType(LayoutTransition.DISAPPEARING);
501 lt.enableTransitionType(LayoutTransition.CHANGE_APPEARING);
502 lt.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
504 lt.disableTransitionType(LayoutTransition.APPEARING);
505 lt.disableTransitionType(LayoutTransition.DISAPPEARING);
506 lt.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
507 lt.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
512 private void setUseFadingAnimations(boolean useFadingAnimations) {
513 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) ((ViewGroup) getParent())
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;
524 WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE);
525 wm.updateViewLayout((View) getParent(), lp);
529 public void setMenuVisibility(final boolean show) {
530 setMenuVisibility(show, false);
533 public void setMenuVisibility(final boolean show, final boolean force) {
534 if (!force && mShowMenu == show) return;
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);
542 getMenuButton().setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE);
545 public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
546 mShowAccessibilityButton = visible;
547 mLongClickableAccessibilityButton = longClickable;
549 // Accessibility button overrides Menu and IME switcher buttons.
550 setMenuVisibility(false, true);
551 getImeSwitchButton().setVisibility(View.INVISIBLE);
554 getAccessibilityButton().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
555 getAccessibilityButton().setLongClickable(longClickable);
559 public void onFinishInflate() {
560 mNavigationInflaterView = (NavigationBarInflaterView) findViewById(
561 R.id.navigation_inflater);
562 updateRotatedViews();
563 mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers);
565 getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
567 DockedStackExistsListener.register(exists -> mHandler.post(() -> {
568 mDockedStackExists = exists;
573 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);
579 mCurrentRotation = -1;
583 public boolean needsReorient(int rotation) {
584 return mCurrentRotation != rotation;
587 private boolean updateCurrentView() {
588 final int rot = mDisplay.getRotation();
589 if (rot == mCurrentRotation) return false;
590 for (int i=0; i<4; i++) {
591 mRotatedViews[i].setVisibility(View.GONE);
593 mCurrentView = mRotatedViews[rot];
594 mCurrentView.setVisibility(View.VISIBLE);
595 mNavigationInflaterView.setAlternativeOrder(rot == Surface.ROTATION_90);
596 for (int i = 0; i < mButtonDispatchers.size(); i++) {
597 mButtonDispatchers.valueAt(i).setCurrentView(mCurrentView);
599 updateLayoutTransitionsEnabled();
600 mCurrentRotation = rot;
604 private void updateRecentsIcon() {
605 getRecentsButton().setImageDrawable(mDockedStackExists ? mDockedIcon : mRecentIcon);
606 mBarTransitions.reapplyDarkIntensity();
609 public boolean isVertical() {
613 public void reorient() {
614 if (!updateCurrentView()) {
618 mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone);
619 if (getRootView() instanceof NavigationBarFrame) {
620 ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone);
622 mDeadZone.setDisplayRotation(mCurrentRotation);
624 // force the low profile & disabled states into compliance
625 mBarTransitions.init();
626 setDisabledFlags(mDisabledFlags, true /* force */);
627 setMenuVisibility(mShowMenu, true /* force */);
630 Log.d(TAG, "reorient(): rot=" + mCurrentRotation);
633 updateTaskSwitchHelper();
634 setNavigationIconHints(mNavigationIconHints, true);
636 getHomeButton().setVertical(mVertical);
639 private void updateTaskSwitchHelper() {
640 if (mGestureHelper == null) return;
641 boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
642 mGestureHelper.setBarState(mVertical, isRtl);
646 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
647 if (DEBUG) Log.d(TAG, String.format(
648 "onSizeChanged: (%dx%d) old: (%dx%d)", w, h, oldw, oldh));
650 final boolean newVertical = w > 0 && h > w;
651 if (newVertical != mVertical) {
652 mVertical = newVertical;
653 //Log.v(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w, mVertical?"y":"n"));
655 getHomeButton().setVertical(mVertical);
656 notifyVerticalChangedListener(newVertical);
659 postCheckForInvalidLayout("sizeChanged");
660 super.onSizeChanged(w, h, oldw, oldh);
663 private void notifyVerticalChangedListener(boolean newVertical) {
664 if (mOnVerticalChangedListener != null) {
665 mOnVerticalChangedListener.onVerticalChanged(newVertical);
670 protected void onConfigurationChanged(Configuration newConfig) {
671 super.onConfigurationChanged(newConfig);
672 boolean uiCarModeChanged = updateCarMode(newConfig);
673 updateTaskSwitchHelper();
674 updateIcons(getContext(), mConfiguration, newConfig);
676 if (uiCarModeChanged || mConfiguration.densityDpi != newConfig.densityDpi
677 || mConfiguration.getLayoutDirection() != newConfig.getLayoutDirection()) {
678 // If car mode or density changes, we need to reset the icons.
679 setNavigationIconHints(mNavigationIconHints, true);
681 mConfiguration.updateFrom(newConfig);
685 * If the configuration changed, update the carmode and return that it was updated.
687 private boolean updateCarMode(Configuration newConfig) {
688 boolean uiCarModeChanged = false;
689 if (newConfig != null) {
690 int uiMode = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK;
691 final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR);
693 if (isCarMode != mInCarMode) {
694 mInCarMode = isCarMode;
695 getHomeButton().setCarMode(isCarMode);
697 if (ALTERNATE_CAR_MODE_UI) {
698 mUseCarModeUi = isCarMode;
699 uiCarModeChanged = true;
701 // Don't use car mode behavior if ALTERNATE_CAR_MODE_UI not set.
702 mUseCarModeUi = false;
706 return uiCarModeChanged;
711 protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
712 if (DEBUG) Log.d(TAG, String.format(
713 "onLayout: %s (%d,%d,%d,%d)",
714 changed?"changed":"notchanged", left, top, right, bottom));
715 super.onLayout(changed, left, top, right, bottom);
718 // uncomment this for extra defensiveness in WORKAROUND_INVALID_LAYOUT situations: if all else
719 // fails, any touch on the display will fix the layout.
721 public boolean onInterceptTouchEvent(MotionEvent ev) {
722 if (DEBUG) Log.d(TAG, "onInterceptTouchEvent: " + ev.toString());
723 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
724 postCheckForInvalidLayout("touch");
726 return super.onInterceptTouchEvent(ev);
731 private String getResourceName(int resId) {
733 final android.content.res.Resources res = getContext().getResources();
735 return res.getResourceName(resId);
736 } catch (android.content.res.Resources.NotFoundException ex) {
744 private void postCheckForInvalidLayout(final String how) {
745 mHandler.obtainMessage(MSG_CHECK_INVALID_LAYOUT, 0, 0, how).sendToTarget();
748 private static String visibilityToString(int vis) {
760 protected void onAttachedToWindow() {
761 super.onAttachedToWindow();
762 onPluginDisconnected(null); // Create default gesture helper
763 Dependency.get(PluginManager.class).addPluginListener(this,
764 NavGesture.class, false /* Only one */);
768 protected void onDetachedFromWindow() {
769 super.onDetachedFromWindow();
770 Dependency.get(PluginManager.class).removePluginListener(this);
771 if (mGestureHelper != null) {
772 mGestureHelper.destroy();
777 public void onPluginConnected(NavGesture plugin, Context context) {
778 mGestureHelper = plugin.getGestureHelper();
779 updateTaskSwitchHelper();
783 public void onPluginDisconnected(NavGesture plugin) {
784 NavigationBarGestureHelper defaultHelper = new NavigationBarGestureHelper(getContext());
785 defaultHelper.setComponents(mRecentsComponent, mDivider, this);
786 if (mGestureHelper != null) {
787 mGestureHelper.destroy();
789 mGestureHelper = defaultHelper;
790 updateTaskSwitchHelper();
793 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
794 pw.println("NavigationBarView {");
795 final Rect r = new Rect();
796 final Point size = new Point();
797 mDisplay.getRealSize(size);
799 pw.println(String.format(" this: " + StatusBar.viewInfo(this)
800 + " " + visibilityToString(getVisibility())));
802 getWindowVisibleDisplayFrame(r);
803 final boolean offscreen = r.right > size.x || r.bottom > size.y;
804 pw.println(" window: "
806 + " " + visibilityToString(getWindowVisibility())
807 + (offscreen ? " OFFSCREEN!" : ""));
809 pw.println(String.format(" mCurrentView: id=%s (%dx%d) %s",
810 getResourceName(getCurrentView().getId()),
811 getCurrentView().getWidth(), getCurrentView().getHeight(),
812 visibilityToString(getCurrentView().getVisibility())));
814 pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s",
816 mVertical ? "true" : "false",
817 mShowMenu ? "true" : "false"));
819 dumpButton(pw, "back", getBackButton());
820 dumpButton(pw, "home", getHomeButton());
821 dumpButton(pw, "rcnt", getRecentsButton());
822 dumpButton(pw, "menu", getMenuButton());
823 dumpButton(pw, "a11y", getAccessibilityButton());
828 private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) {
829 pw.print(" " + caption + ": ");
830 if (button == null) {
833 pw.print(visibilityToString(button.getVisibility())
834 + " alpha=" + button.getAlpha()
840 public interface OnVerticalChangedListener {
841 void onVerticalChanged(boolean isVertical);