Merge "Improve transient bar transitions." into klp-dev
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 1acac85..7bcf43e 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -66,8 +66,9 @@
     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;
@@ -185,4 +186,12 @@
             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";
+    }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f05e372..5269ee3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2542,6 +2542,26 @@
 
     /**
      * @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;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 53041b7..e8173b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -236,7 +236,7 @@
 
     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();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index e40c4e5..292ea7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -37,12 +37,13 @@
     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
@@ -80,10 +81,11 @@
     }
 
     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 {
@@ -93,10 +95,17 @@
         }
     }
 
-    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) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index ad53fea..b36bedd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1841,37 +1841,26 @@
 
     @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;
@@ -1904,11 +1893,13 @@
 
             // 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
@@ -1919,19 +1910,29 @@
                 }
             }
 
+            // 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;
     }
 
@@ -1941,11 +1942,19 @@
                 : 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
         }
     }
 
@@ -1959,7 +1968,8 @@
     @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);
     }
 
@@ -1984,7 +1994,7 @@
 
     private void userAutohide() {
         cancelAutohide();
-        mHandler.postDelayed(mAutohide, 25);
+        mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear
     }
 
     private void setStatusBarLowProfile(boolean lightsOut) {
diff --git a/policy/src/com/android/internal/policy/impl/BarController.java b/policy/src/com/android/internal/policy/impl/BarController.java
index fb76e20..bcecff2 100644
--- a/policy/src/com/android/internal/policy/impl/BarController.java
+++ b/policy/src/com/android/internal/policy/impl/BarController.java
@@ -40,18 +40,21 @@
 
     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();
     }
@@ -60,6 +63,10 @@
         mWin = win;
     }
 
+    public boolean isHidden() {
+        return mState == StatusBarManager.WINDOW_STATE_HIDDEN;
+    }
+
     public void showTransient() {
         if (mWin != null) {
             setTransientBarState(TRANSIENT_BAR_SHOWING);
@@ -70,49 +77,75 @@
         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);
             }
-            return true;
+            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 false;
     }
@@ -134,12 +167,11 @@
 
     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
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 11e33dc..f3e7f0a 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -554,9 +554,15 @@
     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;
@@ -2551,8 +2557,8 @@
 
     @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.
@@ -5084,6 +5090,10 @@
         }
         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;
     }