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];
78 private int mCurrentRotation = -1;
81 boolean mShowAccessibilityButton;
82 boolean mLongClickableAccessibilityButton;
83 int mDisabledFlags = 0;
84 int mNavigationIconHints = 0;
86 private KeyButtonDrawable mBackIcon, mBackLandIcon, mBackAltIcon, mBackAltLandIcon;
87 private KeyButtonDrawable mBackCarModeIcon, mBackLandCarModeIcon;
88 private KeyButtonDrawable mBackAltCarModeIcon, mBackAltLandCarModeIcon;
89 private KeyButtonDrawable mHomeDefaultIcon, mHomeCarModeIcon;
90 private KeyButtonDrawable mRecentIcon;
91 private KeyButtonDrawable mDockedIcon;
92 private KeyButtonDrawable mImeIcon;
93 private KeyButtonDrawable mMenuIcon;
94 private KeyButtonDrawable mAccessibilityIcon;
96 private GestureHelper mGestureHelper;
97 private DeadZone mDeadZone;
98 private final NavigationBarTransitions mBarTransitions;
100 // workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288)
101 final static boolean WORKAROUND_INVALID_LAYOUT = true;
102 final static int MSG_CHECK_INVALID_LAYOUT = 8686;
104 // performs manual animation in sync with layout transitions
105 private final NavTransitionListener mTransitionListener = new NavTransitionListener();
107 private OnVerticalChangedListener mOnVerticalChangedListener;
108 private boolean mLayoutTransitionsEnabled = true;
109 private boolean mWakeAndUnlocking;
110 private boolean mUseCarModeUi = false;
111 private boolean mInCarMode = false;
112 private boolean mDockedStackExists;
114 private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>();
115 private Configuration mConfiguration;
117 private NavigationBarInflaterView mNavigationInflaterView;
118 private RecentsComponent mRecentsComponent;
119 private Divider mDivider;
121 private class NavTransitionListener implements TransitionListener {
122 private boolean mBackTransitioning;
123 private boolean mHomeAppearing;
124 private long mStartDelay;
125 private long mDuration;
126 private TimeInterpolator mInterpolator;
129 public void startTransition(LayoutTransition transition, ViewGroup container,
130 View view, int transitionType) {
131 if (view.getId() == R.id.back) {
132 mBackTransitioning = true;
133 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) {
134 mHomeAppearing = true;
135 mStartDelay = transition.getStartDelay(transitionType);
136 mDuration = transition.getDuration(transitionType);
137 mInterpolator = transition.getInterpolator(transitionType);
142 public void endTransition(LayoutTransition transition, ViewGroup container,
143 View view, int transitionType) {
144 if (view.getId() == R.id.back) {
145 mBackTransitioning = false;
146 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) {
147 mHomeAppearing = false;
151 public void onBackAltCleared() {
152 ButtonDispatcher backButton = getBackButton();
154 // When dismissing ime during unlock, force the back button to run the same appearance
155 // animation as home (if we catch this condition early enough).
156 if (!mBackTransitioning && backButton.getVisibility() == VISIBLE
157 && mHomeAppearing && getHomeButton().getAlpha() == 0) {
158 getBackButton().setAlpha(0);
159 ValueAnimator a = ObjectAnimator.ofFloat(backButton, "alpha", 0, 1);
160 a.setStartDelay(mStartDelay);
161 a.setDuration(mDuration);
162 a.setInterpolator(mInterpolator);
168 private final OnClickListener mImeSwitcherClickListener = new OnClickListener() {
170 public void onClick(View view) {
171 mContext.getSystemService(InputMethodManager.class)
172 .showInputMethodPicker(true /* showAuxiliarySubtypes */);
176 private class H extends Handler {
177 public void handleMessage(Message m) {
179 case MSG_CHECK_INVALID_LAYOUT:
180 final String how = "" + m.obj;
181 final int w = getWidth();
182 final int h = getHeight();
183 final int vw = getCurrentView().getWidth();
184 final int vh = getCurrentView().getHeight();
186 if (h != vh || w != vw) {
187 Log.w(TAG, String.format(
188 "*** Invalid layout in navigation bar (%s this=%dx%d cur=%dx%d)",
190 if (WORKAROUND_INVALID_LAYOUT) {
199 public NavigationBarView(Context context, AttributeSet attrs) {
200 super(context, attrs);
202 mDisplay = ((WindowManager) context.getSystemService(
203 Context.WINDOW_SERVICE)).getDefaultDisplay();
208 mShowAccessibilityButton = false;
209 mLongClickableAccessibilityButton = false;
211 mConfiguration = new Configuration();
212 mConfiguration.updateFrom(context.getResources().getConfiguration());
213 updateIcons(context, Configuration.EMPTY, mConfiguration);
215 mBarTransitions = new NavigationBarTransitions(this);
217 mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
218 mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
219 mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
220 mButtonDispatchers.put(R.id.menu, new ButtonDispatcher(R.id.menu));
221 mButtonDispatchers.put(R.id.ime_switcher, new ButtonDispatcher(R.id.ime_switcher));
222 mButtonDispatchers.put(R.id.accessibility_button,
223 new ButtonDispatcher(R.id.accessibility_button));
226 public BarTransitions getBarTransitions() {
227 return mBarTransitions;
230 public LightBarTransitionsController getLightTransitionsController() {
231 return mBarTransitions.getLightTransitionsController();
234 public void setComponents(RecentsComponent recentsComponent, Divider divider) {
235 mRecentsComponent = recentsComponent;
237 if (mGestureHelper instanceof NavigationBarGestureHelper) {
238 ((NavigationBarGestureHelper) mGestureHelper).setComponents(
239 recentsComponent, divider, this);
243 public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) {
244 mOnVerticalChangedListener = onVerticalChangedListener;
245 notifyVerticalChangedListener(mVertical);
249 public boolean onTouchEvent(MotionEvent event) {
250 if (mGestureHelper.onTouchEvent(event)) {
253 return super.onTouchEvent(event);
257 public boolean onInterceptTouchEvent(MotionEvent event) {
258 return mGestureHelper.onInterceptTouchEvent(event);
261 public void abortCurrentGesture() {
262 getHomeButton().abortCurrentGesture();
265 private H mHandler = new H();
267 public View getCurrentView() {
271 public View[] getAllViews() {
272 return mRotatedViews;
275 public ButtonDispatcher getRecentsButton() {
276 return mButtonDispatchers.get(R.id.recent_apps);
279 public ButtonDispatcher getMenuButton() {
280 return mButtonDispatchers.get(R.id.menu);
283 public ButtonDispatcher getBackButton() {
284 return mButtonDispatchers.get(R.id.back);
287 public ButtonDispatcher getHomeButton() {
288 return mButtonDispatchers.get(R.id.home);
291 public ButtonDispatcher getImeSwitchButton() {
292 return mButtonDispatchers.get(R.id.ime_switcher);
295 public ButtonDispatcher getAccessibilityButton() {
296 return mButtonDispatchers.get(R.id.accessibility_button);
299 public SparseArray<ButtonDispatcher> getButtonDispatchers() {
300 return mButtonDispatchers;
303 private void updateCarModeIcons(Context ctx) {
304 mBackCarModeIcon = getDrawable(ctx,
305 R.drawable.ic_sysbar_back_carmode, R.drawable.ic_sysbar_back_carmode);
306 mBackLandCarModeIcon = mBackCarModeIcon;
307 mBackAltCarModeIcon = getDrawable(ctx,
308 R.drawable.ic_sysbar_back_ime_carmode, R.drawable.ic_sysbar_back_ime_carmode);
309 mBackAltLandCarModeIcon = mBackAltCarModeIcon;
310 mHomeCarModeIcon = getDrawable(ctx,
311 R.drawable.ic_sysbar_home_carmode, R.drawable.ic_sysbar_home_carmode);
314 private void updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig) {
315 if (oldConfig.orientation != newConfig.orientation
316 || oldConfig.densityDpi != newConfig.densityDpi) {
317 mDockedIcon = getDrawable(ctx,
318 R.drawable.ic_sysbar_docked, R.drawable.ic_sysbar_docked_dark);
320 if (oldConfig.densityDpi != newConfig.densityDpi) {
321 mBackIcon = getDrawable(ctx, R.drawable.ic_sysbar_back, R.drawable.ic_sysbar_back_dark);
322 mBackLandIcon = mBackIcon;
323 mBackAltIcon = getDrawable(ctx,
324 R.drawable.ic_sysbar_back_ime, R.drawable.ic_sysbar_back_ime_dark);
325 mBackAltLandIcon = mBackAltIcon;
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(boolean screenOn) {
368 mScreenOn = screenOn;
369 setDisabledFlags(mDisabledFlags, true);
372 public void setNavigationIconHints(int hints) {
373 setNavigationIconHints(hints, false);
376 private KeyButtonDrawable getBackIconWithAlt(boolean carMode, boolean landscape) {
378 ? carMode ? mBackAltLandCarModeIcon : mBackAltLandIcon
379 : carMode ? mBackAltCarModeIcon : mBackAltIcon;
382 private KeyButtonDrawable getBackIcon(boolean carMode, boolean landscape) {
384 ? carMode ? mBackLandCarModeIcon : mBackLandIcon
385 : carMode ? mBackCarModeIcon : mBackIcon;
388 public void setNavigationIconHints(int hints, boolean force) {
389 if (!force && hints == mNavigationIconHints) return;
390 final boolean backAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
391 if ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0 && !backAlt) {
392 mTransitionListener.onBackAltCleared();
395 android.widget.Toast.makeText(getContext(),
396 "Navigation icon hints = " + hints,
400 mNavigationIconHints = hints;
402 // We have to replace or restore the back and home button icons when exiting or entering
403 // carmode, respectively. Recents are not available in CarMode in nav bar so change
404 // to recent icon is not required.
405 KeyButtonDrawable backIcon = (backAlt)
406 ? getBackIconWithAlt(mUseCarModeUi, mVertical)
407 : getBackIcon(mUseCarModeUi, mVertical);
409 getBackButton().setImageDrawable(backIcon);
414 getHomeButton().setImageDrawable(mHomeCarModeIcon);
416 getHomeButton().setImageDrawable(mHomeDefaultIcon);
419 // The Accessibility button always overrides the appearance of the IME switcher
420 final boolean showImeButton =
421 !mShowAccessibilityButton && ((hints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN)
423 getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE);
424 getImeSwitchButton().setImageDrawable(mImeIcon);
426 // Update menu button in case the IME state has changed.
427 setMenuVisibility(mShowMenu, true);
428 getMenuButton().setImageDrawable(mMenuIcon);
430 setAccessibilityButtonState(mShowAccessibilityButton, mLongClickableAccessibilityButton);
431 getAccessibilityButton().setImageDrawable(mAccessibilityIcon);
433 setDisabledFlags(mDisabledFlags, true);
435 mBarTransitions.reapplyDarkIntensity();
438 public void setDisabledFlags(int disabledFlags) {
439 setDisabledFlags(disabledFlags, false);
442 public void setDisabledFlags(int disabledFlags, boolean force) {
443 if (!force && mDisabledFlags == disabledFlags) return;
445 mDisabledFlags = disabledFlags;
447 final boolean disableHome = ((disabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
449 // Always disable recents when alternate car mode UI is active.
450 boolean disableRecent = mUseCarModeUi
451 || ((disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0);
452 final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)
453 && ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) == 0);
455 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
456 if (navButtons != null) {
457 LayoutTransition lt = navButtons.getLayoutTransition();
459 if (!lt.getTransitionListeners().contains(mTransitionListener)) {
460 lt.addTransitionListener(mTransitionListener);
464 if (inLockTask() && disableRecent && !disableHome) {
465 // Don't hide recents when in lock task, it is used for exiting.
466 // Unless home is hidden, then in DPM locked mode and no exit available.
467 disableRecent = false;
470 getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE);
471 getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
472 getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
475 private boolean inLockTask() {
477 return ActivityManager.getService().isInLockTaskMode();
478 } catch (RemoteException e) {
483 public void setLayoutTransitionsEnabled(boolean enabled) {
484 mLayoutTransitionsEnabled = enabled;
485 updateLayoutTransitionsEnabled();
488 public void setWakeAndUnlocking(boolean wakeAndUnlocking) {
489 setUseFadingAnimations(wakeAndUnlocking);
490 mWakeAndUnlocking = wakeAndUnlocking;
491 updateLayoutTransitionsEnabled();
494 private void updateLayoutTransitionsEnabled() {
495 boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled;
496 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
497 LayoutTransition lt = navButtons.getLayoutTransition();
500 lt.enableTransitionType(LayoutTransition.APPEARING);
501 lt.enableTransitionType(LayoutTransition.DISAPPEARING);
502 lt.enableTransitionType(LayoutTransition.CHANGE_APPEARING);
503 lt.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
505 lt.disableTransitionType(LayoutTransition.APPEARING);
506 lt.disableTransitionType(LayoutTransition.DISAPPEARING);
507 lt.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
508 lt.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
513 private void setUseFadingAnimations(boolean useFadingAnimations) {
514 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) ((ViewGroup) getParent())
517 boolean old = lp.windowAnimations != 0;
518 if (!old && useFadingAnimations) {
519 lp.windowAnimations = R.style.Animation_NavigationBarFadeIn;
520 } else if (old && !useFadingAnimations) {
521 lp.windowAnimations = 0;
525 WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE);
526 wm.updateViewLayout((View) getParent(), lp);
530 public void setMenuVisibility(final boolean show) {
531 setMenuVisibility(show, false);
534 public void setMenuVisibility(final boolean show, final boolean force) {
535 if (!force && mShowMenu == show) return;
539 // Only show Menu if IME switcher and Accessibility button not shown.
540 final boolean shouldShow = mShowMenu && !mShowAccessibilityButton &&
541 ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0);
543 getMenuButton().setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE);
546 public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
547 mShowAccessibilityButton = visible;
548 mLongClickableAccessibilityButton = longClickable;
550 // Accessibility button overrides Menu and IME switcher buttons.
551 setMenuVisibility(false, true);
552 getImeSwitchButton().setVisibility(View.INVISIBLE);
555 getAccessibilityButton().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
556 getAccessibilityButton().setLongClickable(longClickable);
560 public void onFinishInflate() {
561 mNavigationInflaterView = (NavigationBarInflaterView) findViewById(
562 R.id.navigation_inflater);
563 updateRotatedViews();
564 mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers);
566 getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
568 DockedStackExistsListener.register(exists -> mHandler.post(() -> {
569 mDockedStackExists = exists;
574 void updateRotatedViews() {
575 mRotatedViews[Surface.ROTATION_0] =
576 mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
577 mRotatedViews[Surface.ROTATION_270] =
578 mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);
583 public boolean needsReorient(int rotation) {
584 return mCurrentRotation != rotation;
587 private void updateCurrentView() {
588 final int rot = mDisplay.getRotation();
589 for (int i=0; i<4; i++) {
590 mRotatedViews[i].setVisibility(View.GONE);
592 mCurrentView = mRotatedViews[rot];
593 mCurrentView.setVisibility(View.VISIBLE);
594 mNavigationInflaterView.setAlternativeOrder(rot == Surface.ROTATION_90);
595 for (int i = 0; i < mButtonDispatchers.size(); i++) {
596 mButtonDispatchers.valueAt(i).setCurrentView(mCurrentView);
598 updateLayoutTransitionsEnabled();
599 mCurrentRotation = rot;
602 private void updateRecentsIcon() {
603 getRecentsButton().setImageDrawable(mDockedStackExists ? mDockedIcon : mRecentIcon);
604 mBarTransitions.reapplyDarkIntensity();
607 public boolean isVertical() {
611 public void reorient() {
614 mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone);
615 ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone);
616 mDeadZone.setDisplayRotation(mCurrentRotation);
618 // force the low profile & disabled states into compliance
619 mBarTransitions.init();
620 setDisabledFlags(mDisabledFlags, true /* force */);
621 setMenuVisibility(mShowMenu, true /* force */);
624 Log.d(TAG, "reorient(): rot=" + mCurrentRotation);
627 updateTaskSwitchHelper();
628 setNavigationIconHints(mNavigationIconHints, true);
630 getHomeButton().setVertical(mVertical);
633 public void onKeyguardOccludedChanged(boolean keyguardOccluded) {
636 private void updateTaskSwitchHelper() {
637 if (mGestureHelper == null) return;
638 boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
639 mGestureHelper.setBarState(mVertical, isRtl);
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));
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"));
652 notifyVerticalChangedListener(newVertical);
655 postCheckForInvalidLayout("sizeChanged");
656 super.onSizeChanged(w, h, oldw, oldh);
659 private void notifyVerticalChangedListener(boolean newVertical) {
660 if (mOnVerticalChangedListener != null) {
661 mOnVerticalChangedListener.onVerticalChanged(newVertical);
666 protected void onConfigurationChanged(Configuration newConfig) {
667 super.onConfigurationChanged(newConfig);
668 boolean uiCarModeChanged = updateCarMode(newConfig);
669 updateTaskSwitchHelper();
670 updateIcons(getContext(), mConfiguration, newConfig);
672 if (uiCarModeChanged || mConfiguration.densityDpi != newConfig.densityDpi) {
673 // If car mode or density changes, we need to reset the icons.
674 setNavigationIconHints(mNavigationIconHints, true);
676 mConfiguration.updateFrom(newConfig);
680 * If the configuration changed, update the carmode and return that it was updated.
682 private boolean updateCarMode(Configuration newConfig) {
683 boolean uiCarModeChanged = false;
684 if (newConfig != null) {
685 int uiMode = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK;
686 final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR);
688 if (isCarMode != mInCarMode) {
689 mInCarMode = isCarMode;
690 getHomeButton().setCarMode(isCarMode);
692 if (ALTERNATE_CAR_MODE_UI) {
693 mUseCarModeUi = isCarMode;
694 uiCarModeChanged = true;
696 // Don't use car mode behavior if ALTERNATE_CAR_MODE_UI not set.
697 mUseCarModeUi = false;
701 return uiCarModeChanged;
706 protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
707 if (DEBUG) Log.d(TAG, String.format(
708 "onLayout: %s (%d,%d,%d,%d)",
709 changed?"changed":"notchanged", left, top, right, bottom));
710 super.onLayout(changed, left, top, right, bottom);
713 // uncomment this for extra defensiveness in WORKAROUND_INVALID_LAYOUT situations: if all else
714 // fails, any touch on the display will fix the layout.
716 public boolean onInterceptTouchEvent(MotionEvent ev) {
717 if (DEBUG) Log.d(TAG, "onInterceptTouchEvent: " + ev.toString());
718 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
719 postCheckForInvalidLayout("touch");
721 return super.onInterceptTouchEvent(ev);
726 private String getResourceName(int resId) {
728 final android.content.res.Resources res = getContext().getResources();
730 return res.getResourceName(resId);
731 } catch (android.content.res.Resources.NotFoundException ex) {
739 private void postCheckForInvalidLayout(final String how) {
740 mHandler.obtainMessage(MSG_CHECK_INVALID_LAYOUT, 0, 0, how).sendToTarget();
743 private static String visibilityToString(int vis) {
755 protected void onAttachedToWindow() {
756 super.onAttachedToWindow();
757 onPluginDisconnected(null); // Create default gesture helper
758 Dependency.get(PluginManager.class).addPluginListener(this,
759 NavGesture.class, false /* Only one */);
763 protected void onDetachedFromWindow() {
764 super.onDetachedFromWindow();
765 Dependency.get(PluginManager.class).removePluginListener(this);
766 if (mGestureHelper != null) {
767 mGestureHelper.destroy();
772 public void onPluginConnected(NavGesture plugin, Context context) {
773 mGestureHelper = plugin.getGestureHelper();
774 updateTaskSwitchHelper();
778 public void onPluginDisconnected(NavGesture plugin) {
779 NavigationBarGestureHelper defaultHelper = new NavigationBarGestureHelper(getContext());
780 defaultHelper.setComponents(mRecentsComponent, mDivider, this);
781 if (mGestureHelper != null) {
782 mGestureHelper.destroy();
784 mGestureHelper = defaultHelper;
785 updateTaskSwitchHelper();
788 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
789 pw.println("NavigationBarView {");
790 final Rect r = new Rect();
791 final Point size = new Point();
792 mDisplay.getRealSize(size);
794 pw.println(String.format(" this: " + StatusBar.viewInfo(this)
795 + " " + visibilityToString(getVisibility())));
797 getWindowVisibleDisplayFrame(r);
798 final boolean offscreen = r.right > size.x || r.bottom > size.y;
799 pw.println(" window: "
801 + " " + visibilityToString(getWindowVisibility())
802 + (offscreen ? " OFFSCREEN!" : ""));
804 pw.println(String.format(" mCurrentView: id=%s (%dx%d) %s",
805 getResourceName(getCurrentView().getId()),
806 getCurrentView().getWidth(), getCurrentView().getHeight(),
807 visibilityToString(getCurrentView().getVisibility())));
809 pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s",
811 mVertical ? "true" : "false",
812 mShowMenu ? "true" : "false"));
814 dumpButton(pw, "back", getBackButton());
815 dumpButton(pw, "home", getHomeButton());
816 dumpButton(pw, "rcnt", getRecentsButton());
817 dumpButton(pw, "menu", getMenuButton());
818 dumpButton(pw, "a11y", getAccessibilityButton());
823 private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) {
824 pw.print(" " + caption + ": ");
825 if (button == null) {
828 pw.print(visibilityToString(button.getVisibility())
829 + " alpha=" + button.getAlpha()
835 public interface OnVerticalChangedListener {
836 void onVerticalChanged(boolean isVertical);