1. If app clears transient flag w/ a gesture, the touch-outside
listener would always win, causing an unsightly hide + immediate
reshow. Instead, give the app some time to clear the flag, then
perform a smooth transition in place.
2. When the transient bars are hidden, we do not know ahead of time
which background will be used on reshow (if transient bars are
revealed, the background is semi-transparent, if transient bars
are cleared, the background is opaque). Window manager is responsible
for showing windows, but sysui is responsible for setting the view
background. Therefore, we need some level of coordination between
the two in this case. Introduce two new non-public sysui flags
that represent the window manager's request to reshow the hidden
bars, but do not reshow until sysui acknowledges (by clearing the flag).
This gives sysui whatever time is necessary to prepare itself for
reshow, avoiding unsightly blip from opaque -> transparent during
the enter animation.
3. When both system bars are hidden, any low-profile changes are
moot. Avoid unsightly low-profile animations during bar reshow
by suppressing the flag in this case.
4. Improve transient bar home -> launcher transition by cancelling
the -> opaque animation. This also fixes a bug where hitting
home from the transient bar would leave you with a semi-transparent
bar in a non-transient state.
Bug:
10284800
Change-Id: I238210561d8d5f70c1a517283b986c9105a1ec75
public static final int WINDOW_STATUS_BAR = 1;
public static final int WINDOW_NAVIGATION_BAR = 2;
+ public static final int WINDOW_STATE_SHOWING = 0;
public static final int WINDOW_STATE_HIDING = 1;
- public static final int WINDOW_STATE_SHOWING = 2;
+ public static final int WINDOW_STATE_HIDDEN = 2;
private Context mContext;
private IStatusBarService mService;
throw new RuntimeException(ex);
}
}
+
+ /** @hide */
+ public static String windowStateToString(int state) {
+ if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING";
+ if (state == WINDOW_STATE_HIDDEN) return "WINDOW_STATE_HIDDEN";
+ if (state == WINDOW_STATE_SHOWING) return "WINDOW_STATE_SHOWING";
+ return "WINDOW_STATE_UNKNOWN";
+ }
}
/**
* @hide
+ *
+ * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
+ * out of the public fields to keep the undefined bits out of the developer's way.
+ *
+ * Flag to specify that the hidden status bar would like to be shown.
+ */
+ public static final int STATUS_BAR_UNHIDE = 0x10000000;
+
+ /**
+ * @hide
+ *
+ * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
+ * out of the public fields to keep the undefined bits out of the developer's way.
+ *
+ * Flag to specify that the hidden navigation bar would like to be shown.
+ */
+ public static final int NAVIGATION_BAR_UNHIDE = 0x20000000;
+
+ /**
+ * @hide
*/
public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x0000FFFF;
public void setWindowState(int window, int state) {
synchronized (mList) {
- mHandler.removeMessages(MSG_SET_WINDOW_STATE);
+ // don't coalesce these
mHandler.obtainMessage(MSG_SET_WINDOW_STATE, window, state, null).sendToTarget();
}
}
public static final int MODE_TRANSPARENT = 2;
private final String mTag;
- private final View mTarget;
- private final int mOpaque;
- private final int mSemiTransparent;
+ protected final View mTarget;
+ protected final int mOpaque;
+ protected final int mSemiTransparent;
protected Drawable mTransparent;
private int mMode;
+ private ValueAnimator mBackgroundColorAnimator;
private final AnimatorUpdateListener mBackgroundColorListener = new AnimatorUpdateListener() {
@Override
}
protected void onTransition(int oldMode, int newMode, boolean animate) {
+ cancelBackgroundColorAnimation();
if (animate && oldMode == MODE_SEMI_TRANSPARENT && newMode == MODE_OPAQUE) {
- startColorAnimation(mSemiTransparent, mOpaque);
+ startBackgroundColorAnimation(mSemiTransparent, mOpaque);
} else if (animate && oldMode == MODE_OPAQUE && newMode == MODE_SEMI_TRANSPARENT) {
- startColorAnimation(mOpaque, mSemiTransparent);
+ startBackgroundColorAnimation(mOpaque, mSemiTransparent);
} else if (newMode == MODE_OPAQUE || newMode == MODE_SEMI_TRANSPARENT) {
mTarget.setBackgroundColor(newMode == MODE_OPAQUE ? mOpaque : mSemiTransparent);
} else {
}
}
- private void startColorAnimation(int from, int to) {
- ValueAnimator anim = ValueAnimator.ofObject(new ArgbEvaluator(), from, to);
- anim.addUpdateListener(mBackgroundColorListener);
- anim.start();
+ private void startBackgroundColorAnimation(int from, int to) {
+ mBackgroundColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), from, to);
+ mBackgroundColorAnimator.addUpdateListener(mBackgroundColorListener);
+ mBackgroundColorAnimator.start();
+ }
+
+ private void cancelBackgroundColorAnimation() {
+ if (mBackgroundColorAnimator != null && mBackgroundColorAnimator.isStarted()) {
+ mBackgroundColorAnimator.cancel();
+ mBackgroundColorAnimator = null;
+ }
}
public static String modeToString(int mode) {
@Override // CommandQueue
public void setWindowState(int window, int state) {
+ boolean showing = state == StatusBarManager.WINDOW_STATE_SHOWING;
if (mStatusBarWindow != null
&& window == StatusBarManager.WINDOW_STATUS_BAR
&& mStatusBarWindowState != state) {
mStatusBarWindowState = state;
- if (DEBUG) Log.d(TAG, "Status bar window " + stateString(state));
- if (state == StatusBarManager.WINDOW_STATE_HIDING) {
- mStatusBarWindow.setEnabled(false);
+ if (DEBUG) Log.d(TAG, "Status bar " + StatusBarManager.windowStateToString(state));
+ mStatusBarWindow.setEnabled(showing);
+ if (!showing) {
mStatusBarView.collapseAllPanels(false);
- } else if (state == StatusBarManager.WINDOW_STATE_SHOWING) {
- mStatusBarWindow.setEnabled(true);
}
}
if (mNavigationBarView != null
&& window == StatusBarManager.WINDOW_NAVIGATION_BAR
&& mNavigationBarWindowState != state) {
mNavigationBarWindowState = state;
- if (DEBUG) Log.d(TAG, "Navigation bar window " + stateString(state));
- if (state == StatusBarManager.WINDOW_STATE_HIDING) {
- mNavigationBarView.setEnabled(false);
- } else if (state == StatusBarManager.WINDOW_STATE_SHOWING) {
- mNavigationBarView.setEnabled(true);
- }
+ if (DEBUG) Log.d(TAG, "Navigation bar " + StatusBarManager.windowStateToString(state));
+ mNavigationBarView.setEnabled(showing);
}
}
- private static String stateString(int state) {
- if (state == StatusBarManager.WINDOW_STATE_HIDING) return "hiding";
- if (state == StatusBarManager.WINDOW_STATE_SHOWING) return "showing";
- return "unknown";
- }
-
@Override // CommandQueue
public void setSystemUiVisibility(int vis, int mask) {
final int oldVal = mSystemUiVisibility;
// update status bar mode
int sbMode = updateBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(),
- View.STATUS_BAR_TRANSIENT, View.SYSTEM_UI_FLAG_TRANSPARENT_STATUS);
+ View.STATUS_BAR_TRANSIENT, View.SYSTEM_UI_FLAG_TRANSPARENT_STATUS,
+ mStatusBarWindowState);
// update navigation bar mode
int nbMode = updateBarMode(oldVal, newVal, mNavigationBarView.getBarTransitions(),
- View.NAVIGATION_BAR_TRANSIENT, View.SYSTEM_UI_FLAG_TRANSPARENT_NAVIGATION);
+ View.NAVIGATION_BAR_TRANSIENT, View.SYSTEM_UI_FLAG_TRANSPARENT_NAVIGATION,
+ mNavigationBarWindowState);
if (sbMode != -1 || nbMode != -1) {
// update transient bar autohide
}
}
+ // ready to unhide
+ if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
+ mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
+ }
+ if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
+ mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
+ }
+
// send updated sysui visibility to window manager
notifyUiVisibilityChanged(mSystemUiVisibility);
}
}
private int updateBarMode(int oldVis, int newVis, BarTransitions transitions,
- int transientFlag, int transparentFlag) {
+ int transientFlag, int transparentFlag, int windowState) {
final int oldMode = barMode(oldVis, transientFlag, transparentFlag);
final int newMode = barMode(newVis, transientFlag, transparentFlag);
if (oldMode == newMode) {
return -1; // no mode change
}
- transitions.transitionTo(newMode);
+ boolean animate = windowState == StatusBarManager.WINDOW_STATE_SHOWING
+ && oldMode == MODE_SEMI_TRANSPARENT && newMode == MODE_OPAQUE;
+ transitions.transitionTo(newMode, animate);
return newMode;
}
: MODE_OPAQUE;
}
+ private final Runnable mResumeSemiTransparent = new Runnable() {
+ @Override
+ public void run() {
+ if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0) {
+ animateTransitionTo(BarTransitions.MODE_SEMI_TRANSPARENT);
+ }
+ }};
+
@Override
public void resumeAutohide() {
if (mAutohideSuspended) {
scheduleAutohide();
- animateTransitionTo(BarTransitions.MODE_SEMI_TRANSPARENT);
+ mHandler.postDelayed(mResumeSemiTransparent, 500); // longer than home -> launcher
}
}
@Override
public void suspendAutohide() {
mHandler.removeCallbacks(mAutohide);
- mAutohideSuspended = 0 != (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT);
+ mHandler.removeCallbacks(mResumeSemiTransparent);
+ mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0;
animateTransitionTo(BarTransitions.MODE_OPAQUE);
}
private void userAutohide() {
cancelAutohide();
- mHandler.postDelayed(mAutohide, 25);
+ mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear
}
private void setStatusBarLowProfile(boolean lightsOut) {
private final String mTag;
private final int mTransientFlag;
+ private final int mUnhideFlag;
private final int mStatusBarManagerId;
private final Handler mHandler;
private final Object mServiceAquireLock = new Object();
private IStatusBarService mStatusBarService;
private WindowState mWin;
+ private int mState;
private int mTransientBarState;
private boolean mPendingShow;
- public BarController(String tag, int transientFlag, int statusBarManagerId) {
+ public BarController(String tag, int transientFlag, int unhideFlag, int statusBarManagerId) {
mTag = "BarController." + tag;
mTransientFlag = transientFlag;
+ mUnhideFlag = unhideFlag;
mStatusBarManagerId = statusBarManagerId;
mHandler = new Handler();
}
mWin = win;
}
+ public boolean isHidden() {
+ return mState == StatusBarManager.WINDOW_STATE_HIDDEN;
+ }
+
public void showTransient() {
if (mWin != null) {
setTransientBarState(TRANSIENT_BAR_SHOWING);
return mTransientBarState == TRANSIENT_BAR_SHOWING;
}
- public void adjustSystemUiVisibilityLw(int visibility) {
+ public void adjustSystemUiVisibilityLw(int oldVis, int vis) {
if (mWin != null && mTransientBarState == TRANSIENT_BAR_SHOWING &&
- (visibility & mTransientFlag) == 0) {
+ (vis & mTransientFlag) == 0) {
+ // sysui requests hide
setTransientBarState(TRANSIENT_BAR_HIDING);
setBarShowingLw(false);
+ } else if (mWin != null && (oldVis & mUnhideFlag) != 0 && (vis & mUnhideFlag) == 0) {
+ // sysui ready to unhide
+ setBarShowingLw(true);
}
}
public boolean setBarShowingLw(final boolean show) {
if (mWin == null) return false;
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- IStatusBarService statusbar = getStatusBarService();
- if (statusbar != null) {
- statusbar.setWindowState(mStatusBarManagerId, show
- ? StatusBarManager.WINDOW_STATE_SHOWING
- : StatusBarManager.WINDOW_STATE_HIDING);
- }
- } catch (RemoteException e) {
- // re-acquire status bar service next time it is needed.
- mStatusBarService = null;
- }
- }
- });
if (show && mTransientBarState == TRANSIENT_BAR_HIDING) {
mPendingShow = true;
return false;
}
- return show ? mWin.showLw(true) : mWin.hideLw(true);
+ final boolean oldVis = mWin.isVisibleLw();
+ final boolean oldAnim = mWin.isAnimatingLw();
+ final boolean rt = show ? mWin.showLw(true) : mWin.hideLw(true);
+ final int state = computeState(oldVis, oldAnim, mWin.isVisibleLw(), mWin.isAnimatingLw());
+ if (state > -1) {
+ updateState(state);
+ }
+ return rt;
+ }
+
+ private int computeState(boolean oldVis, boolean oldAnim, boolean newVis, boolean newAnim) {
+ return (!newVis && !newAnim) ? StatusBarManager.WINDOW_STATE_HIDDEN
+ : (!oldVis && newVis && newAnim) ? StatusBarManager.WINDOW_STATE_SHOWING
+ : (oldVis && newVis && !oldAnim && newAnim) ? StatusBarManager.WINDOW_STATE_HIDING
+ : -1;
+ }
+
+ private void updateState(final int state) {
+ if (state != mState) {
+ mState = state;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ IStatusBarService statusbar = getStatusBarService();
+ if (statusbar != null) {
+ statusbar.setWindowState(mStatusBarManagerId, state);
+ }
+ } catch (RemoteException e) {
+ // re-acquire status bar service next time it is needed.
+ mStatusBarService = null;
+ }
+ }
+ });
+ }
}
public boolean checkHiddenLw() {
- if (mWin != null && mTransientBarState == TRANSIENT_BAR_HIDING && !mWin.isVisibleLw()) {
- // Finished animating out, clean up and reset style
- setTransientBarState(TRANSIENT_BAR_NONE);
- if (mPendingShow) {
- setBarShowingLw(true);
- mPendingShow = false;
+ if (mWin != null) {
+ if (!mWin.isVisibleLw() && !mWin.isAnimatingLw()) {
+ updateState(StatusBarManager.WINDOW_STATE_HIDDEN);
+ }
+ if (mTransientBarState == TRANSIENT_BAR_HIDING && !mWin.isVisibleLw()) {
+ // Finished animating out, clean up and reset style
+ setTransientBarState(TRANSIENT_BAR_NONE);
+ if (mPendingShow) {
+ setBarShowingLw(true);
+ mPendingShow = false;
+ }
+ return true;
}
- return true;
}
return false;
}
public int updateVisibilityLw(boolean allowed, int oldVis, int vis) {
if (mWin == null) return vis;
-
if (mTransientBarState == TRANSIENT_BAR_SHOWING) { // transient bar requested
if (allowed) {
vis |= mTransientFlag;
if ((oldVis & mTransientFlag) == 0) {
- setBarShowingLw(true);
+ vis |= mUnhideFlag; // tell sysui we're ready to unhide
}
} else {
setTransientBarState(TRANSIENT_BAR_NONE); // request denied
MyOrientationListener mOrientationListener;
private final BarController mStatusBarController = new BarController("StatusBar",
- View.STATUS_BAR_TRANSIENT, StatusBarManager.WINDOW_STATUS_BAR);
+ View.STATUS_BAR_TRANSIENT,
+ View.STATUS_BAR_UNHIDE,
+ StatusBarManager.WINDOW_STATUS_BAR);
+
private final BarController mNavigationBarController = new BarController("NavigationBar",
- View.NAVIGATION_BAR_TRANSIENT, StatusBarManager.WINDOW_NAVIGATION_BAR);
+ View.NAVIGATION_BAR_TRANSIENT,
+ View.NAVIGATION_BAR_UNHIDE,
+ StatusBarManager.WINDOW_NAVIGATION_BAR);
+
private TransientNavigationConfirmation mTransientNavigationConfirmation;
private SystemGesturesPointerEventListener mSystemGestures;
@Override
public int adjustSystemUiVisibilityLw(int visibility) {
- mStatusBarController.adjustSystemUiVisibilityLw(visibility);
- mNavigationBarController.adjustSystemUiVisibilityLw(visibility);
+ mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
+ mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
// Reset any bits in mForceClearingStatusBarVisibility that
// are now clear.
}
vis = mNavigationBarController.updateVisibilityLw(isTransientNav, oldVis, vis);
+ // don't send low profile updates if the system bars are hidden
+ if (mStatusBarController.isHidden() && mNavigationBarController.isHidden()) {
+ vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
+ }
return vis;
}