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.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;
52 import java.io.FileDescriptor;
53 import java.io.PrintWriter;
55 public class NavigationBarView extends LinearLayout {
56 final static boolean DEBUG = false;
57 final static String TAG = "StatusBar/NavBarView";
59 // slippery nav bar when everything is disabled, e.g. during setup
60 final static boolean SLIPPERY_WHEN_DISABLED = true;
62 final Display mDisplay;
63 View mCurrentView = null;
64 View[] mRotatedViews = new View[4];
68 private int mCurrentRotation = -1;
71 int mDisabledFlags = 0;
72 int mNavigationIconHints = 0;
74 private Drawable mBackIcon, mBackLandIcon, mBackAltIcon, mBackAltLandIcon;
75 private Drawable mBackCarModeIcon, mBackLandCarModeIcon;
76 private Drawable mBackAltCarModeIcon, mBackAltLandCarModeIcon;
77 private Drawable mHomeDefaultIcon, mHomeCarModeIcon;
78 private Drawable mRecentIcon;
79 private Drawable mDockedIcon;
80 private Drawable mImeIcon;
81 private Drawable mMenuIcon;
83 private NavigationBarGestureHelper mGestureHelper;
84 private DeadZone mDeadZone;
85 private final NavigationBarTransitions mBarTransitions;
87 // workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288)
88 final static boolean WORKAROUND_INVALID_LAYOUT = true;
89 final static int MSG_CHECK_INVALID_LAYOUT = 8686;
91 // performs manual animation in sync with layout transitions
92 private final NavTransitionListener mTransitionListener = new NavTransitionListener();
94 private OnVerticalChangedListener mOnVerticalChangedListener;
95 private boolean mLayoutTransitionsEnabled = true;
96 private boolean mWakeAndUnlocking;
97 private boolean mCarMode = false;
98 private boolean mDockedStackExists;
100 private final SparseArray<ButtonDispatcher> mButtonDisatchers = new SparseArray<>();
101 private Configuration mConfiguration;
103 private NavigationBarInflaterView mNavigationInflaterView;
105 private class NavTransitionListener implements TransitionListener {
106 private boolean mBackTransitioning;
107 private boolean mHomeAppearing;
108 private long mStartDelay;
109 private long mDuration;
110 private TimeInterpolator mInterpolator;
113 public void startTransition(LayoutTransition transition, ViewGroup container,
114 View view, int transitionType) {
115 if (view.getId() == R.id.back) {
116 mBackTransitioning = true;
117 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) {
118 mHomeAppearing = true;
119 mStartDelay = transition.getStartDelay(transitionType);
120 mDuration = transition.getDuration(transitionType);
121 mInterpolator = transition.getInterpolator(transitionType);
126 public void endTransition(LayoutTransition transition, ViewGroup container,
127 View view, int transitionType) {
128 if (view.getId() == R.id.back) {
129 mBackTransitioning = false;
130 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) {
131 mHomeAppearing = false;
135 public void onBackAltCleared() {
136 ButtonDispatcher backButton = getBackButton();
138 // When dismissing ime during unlock, force the back button to run the same appearance
139 // animation as home (if we catch this condition early enough).
140 if (!mBackTransitioning && backButton.getVisibility() == VISIBLE
141 && mHomeAppearing && getHomeButton().getAlpha() == 0) {
142 getBackButton().setAlpha(0);
143 ValueAnimator a = ObjectAnimator.ofFloat(backButton, "alpha", 0, 1);
144 a.setStartDelay(mStartDelay);
145 a.setDuration(mDuration);
146 a.setInterpolator(mInterpolator);
152 private final OnClickListener mImeSwitcherClickListener = new OnClickListener() {
154 public void onClick(View view) {
155 mContext.getSystemService(InputMethodManager.class)
156 .showInputMethodPicker(true /* showAuxiliarySubtypes */);
160 private class H extends Handler {
161 public void handleMessage(Message m) {
163 case MSG_CHECK_INVALID_LAYOUT:
164 final String how = "" + m.obj;
165 final int w = getWidth();
166 final int h = getHeight();
167 final int vw = getCurrentView().getWidth();
168 final int vh = getCurrentView().getHeight();
170 if (h != vh || w != vw) {
171 Log.w(TAG, String.format(
172 "*** Invalid layout in navigation bar (%s this=%dx%d cur=%dx%d)",
174 if (WORKAROUND_INVALID_LAYOUT) {
183 public NavigationBarView(Context context, AttributeSet attrs) {
184 super(context, attrs);
186 mDisplay = ((WindowManager) context.getSystemService(
187 Context.WINDOW_SERVICE)).getDefaultDisplay();
191 mGestureHelper = new NavigationBarGestureHelper(context);
193 mConfiguration = new Configuration();
194 mConfiguration.updateFrom(context.getResources().getConfiguration());
195 updateIcons(context, Configuration.EMPTY, mConfiguration);
197 mBarTransitions = new NavigationBarTransitions(this);
199 mButtonDisatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
200 mButtonDisatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
201 mButtonDisatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
202 mButtonDisatchers.put(R.id.menu, new ButtonDispatcher(R.id.menu));
203 mButtonDisatchers.put(R.id.ime_switcher, new ButtonDispatcher(R.id.ime_switcher));
206 public BarTransitions getBarTransitions() {
207 return mBarTransitions;
210 public void setComponents(RecentsComponent recentsComponent, Divider divider) {
211 mGestureHelper.setComponents(recentsComponent, divider, this);
214 public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) {
215 mOnVerticalChangedListener = onVerticalChangedListener;
216 notifyVerticalChangedListener(mVertical);
220 public boolean onTouchEvent(MotionEvent event) {
221 if (mGestureHelper.onTouchEvent(event)) {
224 if (mDeadZone != null && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
225 mDeadZone.poke(event);
227 return super.onTouchEvent(event);
231 public boolean onInterceptTouchEvent(MotionEvent event) {
232 return mGestureHelper.onInterceptTouchEvent(event);
235 public void abortCurrentGesture() {
236 getHomeButton().abortCurrentGesture();
239 private H mHandler = new H();
241 public View getCurrentView() {
245 public View[] getAllViews() {
246 return mRotatedViews;
249 public ButtonDispatcher getRecentsButton() {
250 return mButtonDisatchers.get(R.id.recent_apps);
253 public ButtonDispatcher getMenuButton() {
254 return mButtonDisatchers.get(R.id.menu);
257 public ButtonDispatcher getBackButton() {
258 return mButtonDisatchers.get(R.id.back);
261 public ButtonDispatcher getHomeButton() {
262 return mButtonDisatchers.get(R.id.home);
265 public ButtonDispatcher getImeSwitchButton() {
266 return mButtonDisatchers.get(R.id.ime_switcher);
269 private void updateCarModeIcons(Context ctx) {
270 mBackCarModeIcon = ctx.getDrawable(R.drawable.ic_sysbar_back_carmode);
271 mBackLandCarModeIcon = mBackCarModeIcon;
272 mBackAltCarModeIcon = ctx.getDrawable(R.drawable.ic_sysbar_back_ime_carmode);
273 mBackAltLandCarModeIcon = mBackAltCarModeIcon;
274 mHomeCarModeIcon = ctx.getDrawable(R.drawable.ic_sysbar_home_carmode);
277 private void updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig) {
278 if (oldConfig.orientation != newConfig.orientation
279 || oldConfig.densityDpi != newConfig.densityDpi) {
280 mDockedIcon = ctx.getDrawable(R.drawable.ic_sysbar_docked);
282 if (oldConfig.densityDpi != newConfig.densityDpi) {
283 mBackIcon = ctx.getDrawable(R.drawable.ic_sysbar_back);
284 mBackLandIcon = mBackIcon;
285 mBackAltIcon = ctx.getDrawable(R.drawable.ic_sysbar_back_ime);
286 mBackAltLandIcon = mBackAltIcon;
288 mHomeDefaultIcon = ctx.getDrawable(R.drawable.ic_sysbar_home);
289 mRecentIcon = ctx.getDrawable(R.drawable.ic_sysbar_recent);
290 mMenuIcon = ctx.getDrawable(R.drawable.ic_sysbar_menu);
291 mImeIcon = ctx.getDrawable(R.drawable.ic_ime_switcher_default);
293 updateCarModeIcons(ctx);
298 public void setLayoutDirection(int layoutDirection) {
299 // Reload all the icons
300 updateIcons(getContext(), Configuration.EMPTY, mConfiguration);
302 super.setLayoutDirection(layoutDirection);
305 public void notifyScreenOn(boolean screenOn) {
306 mScreenOn = screenOn;
307 setDisabledFlags(mDisabledFlags, true);
310 public void setNavigationIconHints(int hints) {
311 setNavigationIconHints(hints, false);
314 private Drawable getBackIconWithAlt(boolean carMode, boolean landscape) {
316 ? carMode ? mBackAltLandCarModeIcon : mBackAltLandIcon
317 : carMode ? mBackAltCarModeIcon : mBackAltIcon;
320 private Drawable getBackIcon(boolean carMode, boolean landscape) {
322 ? carMode ? mBackLandCarModeIcon : mBackLandIcon
323 : carMode ? mBackCarModeIcon : mBackIcon;
326 public void setNavigationIconHints(int hints, boolean force) {
327 if (!force && hints == mNavigationIconHints) return;
328 final boolean backAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
329 if ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0 && !backAlt) {
330 mTransitionListener.onBackAltCleared();
333 android.widget.Toast.makeText(getContext(),
334 "Navigation icon hints = " + hints,
338 mNavigationIconHints = hints;
340 // We have to replace or restore the back and home button icons when exiting or entering
341 // carmode, respectively. Recents are not available in CarMode in nav bar so change
342 // to recent icon is not required.
343 Drawable backIcon = (backAlt)
344 ? getBackIconWithAlt(mCarMode, mVertical)
345 : getBackIcon(mCarMode, mVertical);
347 getBackButton().setImageDrawable(backIcon);
352 getHomeButton().setImageDrawable(mHomeCarModeIcon);
354 getHomeButton().setImageDrawable(mHomeDefaultIcon);
357 final boolean showImeButton = ((hints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);
358 getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE);
359 getImeSwitchButton().setImageDrawable(mImeIcon);
361 // Update menu button in case the IME state has changed.
362 setMenuVisibility(mShowMenu, true);
363 getMenuButton().setImageDrawable(mMenuIcon);
365 setDisabledFlags(mDisabledFlags, true);
368 public void setDisabledFlags(int disabledFlags) {
369 setDisabledFlags(disabledFlags, false);
372 public void setDisabledFlags(int disabledFlags, boolean force) {
373 if (!force && mDisabledFlags == disabledFlags) return;
375 mDisabledFlags = disabledFlags;
377 final boolean disableHome = ((disabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
379 // Disable recents always in car mode.
380 boolean disableRecent = (
381 mCarMode || (disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0);
382 final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)
383 && ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) == 0);
384 final boolean disableSearch = ((disabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0);
386 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
387 if (navButtons != null) {
388 LayoutTransition lt = navButtons.getLayoutTransition();
390 if (!lt.getTransitionListeners().contains(mTransitionListener)) {
391 lt.addTransitionListener(mTransitionListener);
395 if (inLockTask() && disableRecent && !disableHome) {
396 // Don't hide recents when in lock task, it is used for exiting.
397 // Unless home is hidden, then in DPM locked mode and no exit available.
398 disableRecent = false;
401 getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE);
402 getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
403 getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
406 private boolean inLockTask() {
408 return ActivityManagerNative.getDefault().isInLockTaskMode();
409 } catch (RemoteException e) {
414 public void setLayoutTransitionsEnabled(boolean enabled) {
415 mLayoutTransitionsEnabled = enabled;
416 updateLayoutTransitionsEnabled();
419 public void setWakeAndUnlocking(boolean wakeAndUnlocking) {
420 setUseFadingAnimations(wakeAndUnlocking);
421 mWakeAndUnlocking = wakeAndUnlocking;
422 updateLayoutTransitionsEnabled();
425 private void updateLayoutTransitionsEnabled() {
426 boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled;
427 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
428 LayoutTransition lt = navButtons.getLayoutTransition();
431 lt.enableTransitionType(LayoutTransition.APPEARING);
432 lt.enableTransitionType(LayoutTransition.DISAPPEARING);
433 lt.enableTransitionType(LayoutTransition.CHANGE_APPEARING);
434 lt.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
436 lt.disableTransitionType(LayoutTransition.APPEARING);
437 lt.disableTransitionType(LayoutTransition.DISAPPEARING);
438 lt.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
439 lt.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
444 private void setUseFadingAnimations(boolean useFadingAnimations) {
445 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
447 boolean old = lp.windowAnimations != 0;
448 if (!old && useFadingAnimations) {
449 lp.windowAnimations = R.style.Animation_NavigationBarFadeIn;
450 } else if (old && !useFadingAnimations) {
451 lp.windowAnimations = 0;
455 WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE);
456 wm.updateViewLayout(this, lp);
460 public void setMenuVisibility(final boolean show) {
461 setMenuVisibility(show, false);
464 public void setMenuVisibility(final boolean show, final boolean force) {
465 if (!force && mShowMenu == show) return;
469 // Only show Menu if IME switcher not shown.
470 final boolean shouldShow = mShowMenu &&
471 ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0);
473 getMenuButton().setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE);
477 public void onFinishInflate() {
478 mNavigationInflaterView = (NavigationBarInflaterView) findViewById(
479 R.id.navigation_inflater);
480 updateRotatedViews();
481 mNavigationInflaterView.setButtonDispatchers(mButtonDisatchers);
483 getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
486 WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(new Stub() {
488 public void onDividerVisibilityChanged(boolean visible) throws RemoteException {
492 public void onDockedStackExistsChanged(final boolean exists) throws RemoteException {
493 mHandler.post(new Runnable() {
496 mDockedStackExists = exists;
503 public void onDockedStackMinimizedChanged(boolean minimized, long animDuration)
504 throws RemoteException {
508 public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration)
509 throws RemoteException {
513 public void onDockSideChanged(int newDockSide) throws RemoteException {
516 } catch (RemoteException e) {
517 Log.e(TAG, "Failed registering docked stack exists listener", e);
521 void updateRotatedViews() {
522 mRotatedViews[Surface.ROTATION_0] =
523 mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
524 mRotatedViews[Surface.ROTATION_270] =
525 mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);
530 public boolean needsReorient(int rotation) {
531 return mCurrentRotation != rotation;
534 private void updateCurrentView() {
535 final int rot = mDisplay.getRotation();
536 for (int i=0; i<4; i++) {
537 mRotatedViews[i].setVisibility(View.GONE);
539 mCurrentView = mRotatedViews[rot];
540 mCurrentView.setVisibility(View.VISIBLE);
541 mNavigationInflaterView.setAlternativeOrder(rot == Surface.ROTATION_90);
542 for (int i = 0; i < mButtonDisatchers.size(); i++) {
543 mButtonDisatchers.valueAt(i).setCurrentView(mCurrentView);
545 updateLayoutTransitionsEnabled();
546 mCurrentRotation = rot;
549 private void updateRecentsIcon() {
550 getRecentsButton().setImageDrawable(mDockedStackExists ? mDockedIcon : mRecentIcon);
553 public boolean isVertical() {
557 public void reorient() {
560 getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
562 mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone);
564 // force the low profile & disabled states into compliance
565 mBarTransitions.init();
566 setDisabledFlags(mDisabledFlags, true /* force */);
567 setMenuVisibility(mShowMenu, true /* force */);
570 Log.d(TAG, "reorient(): rot=" + mCurrentRotation);
573 updateTaskSwitchHelper();
574 setNavigationIconHints(mNavigationIconHints, true);
577 private void updateTaskSwitchHelper() {
578 boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
579 mGestureHelper.setBarState(mVertical, isRtl);
583 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
584 if (DEBUG) Log.d(TAG, String.format(
585 "onSizeChanged: (%dx%d) old: (%dx%d)", w, h, oldw, oldh));
587 final boolean newVertical = w > 0 && h > w;
588 if (newVertical != mVertical) {
589 mVertical = newVertical;
590 //Log.v(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w, mVertical?"y":"n"));
592 notifyVerticalChangedListener(newVertical);
595 postCheckForInvalidLayout("sizeChanged");
596 super.onSizeChanged(w, h, oldw, oldh);
599 private void notifyVerticalChangedListener(boolean newVertical) {
600 if (mOnVerticalChangedListener != null) {
601 mOnVerticalChangedListener.onVerticalChanged(newVertical);
606 protected void onConfigurationChanged(Configuration newConfig) {
607 super.onConfigurationChanged(newConfig);
608 boolean uiCarModeChanged = updateCarMode(newConfig);
609 updateTaskSwitchHelper();
610 updateIcons(getContext(), mConfiguration, newConfig);
612 if (uiCarModeChanged || mConfiguration.densityDpi != newConfig.densityDpi) {
613 // If car mode or density changes, we need to reset the icons.
614 setNavigationIconHints(mNavigationIconHints, true);
616 mConfiguration.updateFrom(newConfig);
620 * If the configuration changed, update the carmode and return that it was updated.
622 private boolean updateCarMode(Configuration newConfig) {
623 boolean uiCarModeChanged = false;
624 if (newConfig != null) {
625 int uiMode = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK;
626 if (mCarMode && uiMode != Configuration.UI_MODE_TYPE_CAR) {
628 uiCarModeChanged = true;
629 getHomeButton().setCarMode(mCarMode);
630 } else if (uiMode == Configuration.UI_MODE_TYPE_CAR) {
632 uiCarModeChanged = true;
633 getHomeButton().setCarMode(mCarMode);
636 return uiCarModeChanged;
641 protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
642 if (DEBUG) Log.d(TAG, String.format(
643 "onLayout: %s (%d,%d,%d,%d)",
644 changed?"changed":"notchanged", left, top, right, bottom));
645 super.onLayout(changed, left, top, right, bottom);
648 // uncomment this for extra defensiveness in WORKAROUND_INVALID_LAYOUT situations: if all else
649 // fails, any touch on the display will fix the layout.
651 public boolean onInterceptTouchEvent(MotionEvent ev) {
652 if (DEBUG) Log.d(TAG, "onInterceptTouchEvent: " + ev.toString());
653 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
654 postCheckForInvalidLayout("touch");
656 return super.onInterceptTouchEvent(ev);
661 private String getResourceName(int resId) {
663 final android.content.res.Resources res = getContext().getResources();
665 return res.getResourceName(resId);
666 } catch (android.content.res.Resources.NotFoundException ex) {
674 private void postCheckForInvalidLayout(final String how) {
675 mHandler.obtainMessage(MSG_CHECK_INVALID_LAYOUT, 0, 0, how).sendToTarget();
678 private static String visibilityToString(int vis) {
689 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
690 pw.println("NavigationBarView {");
691 final Rect r = new Rect();
692 final Point size = new Point();
693 mDisplay.getRealSize(size);
695 pw.println(String.format(" this: " + PhoneStatusBar.viewInfo(this)
696 + " " + visibilityToString(getVisibility())));
698 getWindowVisibleDisplayFrame(r);
699 final boolean offscreen = r.right > size.x || r.bottom > size.y;
700 pw.println(" window: "
702 + " " + visibilityToString(getWindowVisibility())
703 + (offscreen ? " OFFSCREEN!" : ""));
705 pw.println(String.format(" mCurrentView: id=%s (%dx%d) %s",
706 getResourceName(getCurrentView().getId()),
707 getCurrentView().getWidth(), getCurrentView().getHeight(),
708 visibilityToString(getCurrentView().getVisibility())));
710 pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s",
712 mVertical ? "true" : "false",
713 mShowMenu ? "true" : "false"));
715 dumpButton(pw, "back", getBackButton());
716 dumpButton(pw, "home", getHomeButton());
717 dumpButton(pw, "rcnt", getRecentsButton());
718 dumpButton(pw, "menu", getMenuButton());
723 private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) {
724 pw.print(" " + caption + ": ");
725 if (button == null) {
728 pw.print(visibilityToString(button.getVisibility())
729 + " alpha=" + button.getAlpha()
735 public interface OnVerticalChangedListener {
736 void onVerticalChanged(boolean isVertical);