diff options
author | Dianne Hackborn <hackbod@google.com> | 2012-05-05 20:36:38 -0700 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2012-05-06 11:52:05 -0700 |
commit | 139e5aa1da51b27231ab36344cf2d0dafab23f1e (patch) | |
tree | a15bf78ea4fc0f31bf8c6793aa0072c04c12fca0 | |
parent | d0c66f6a8f15bb95648b4306d2583575d0bb9935 (diff) | |
download | frameworks_base-139e5aa1da51b27231ab36344cf2d0dafab23f1e.zip frameworks_base-139e5aa1da51b27231ab36344cf2d0dafab23f1e.tar.gz frameworks_base-139e5aa1da51b27231ab36344cf2d0dafab23f1e.tar.bz2 |
Fix issue #6404215: New ActionBar auto-hide can conflict with application
The action bar now maintains separate states for the things that can
impact its visibility (calls from the app, action mode, system UI) so
that the changes in these won't incorrectly mix together.
Also added a hack to force the status bar to be shown when showing
the action bar for an action mode, when the UI is in a state where
the action bar would be shown with a gap above where the status bar
is.
Change-Id: Ib0950a7f585c5d2c9e77d11b237ba6e150f15ebd
-rw-r--r-- | core/java/android/view/View.java | 17 | ||||
-rw-r--r-- | core/java/android/view/ViewRootImpl.java | 1 | ||||
-rw-r--r-- | core/java/com/android/internal/app/ActionBarImpl.java | 154 | ||||
-rw-r--r-- | core/java/com/android/internal/widget/ActionBarOverlayLayout.java | 31 |
4 files changed, 162 insertions, 41 deletions
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index d1d036c..269760e 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -15236,6 +15236,18 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } } + /** @hide */ + public void setDisabledSystemUiVisibility(int flags) { + if (mAttachInfo != null) { + if (mAttachInfo.mDisabledSystemUiVisibility != flags) { + mAttachInfo.mDisabledSystemUiVisibility = flags; + if (mParent != null) { + mParent.recomputeViewAttributes(this); + } + } + } + } + /** * Creates an image that the system displays during the drag and drop * operation. This is called a "drag shadow". The default implementation @@ -16913,6 +16925,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal int mSystemUiVisibility; /** + * Hack to force certain system UI visibility flags to be cleared. + */ + int mDisabledSystemUiVisibility; + + /** * True if a view in this hierarchy has an OnSystemUiVisibilityChangeListener * attached. */ diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 5eeb211..5b3eca5 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1025,6 +1025,7 @@ public final class ViewRootImpl implements ViewParent, attachInfo.mSystemUiVisibility = 0; attachInfo.mHasSystemUiListeners = false; mView.dispatchCollectViewAttributes(attachInfo, 0); + attachInfo.mSystemUiVisibility &= ~attachInfo.mDisabledSystemUiVisibility; if (attachInfo.mKeepScreenOn != oldScreenOn || attachInfo.mSystemUiVisibility != oldVis || attachInfo.mHasSystemUiListeners != oldHasSystemUiListeners) { diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index 93cf9d0..2cbd9cc 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -39,8 +39,8 @@ import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.drawable.Drawable; -import android.os.Build; import android.os.Handler; +import android.util.Log; import android.util.TypedValue; import android.view.ActionMode; import android.view.ContextThemeWrapper; @@ -110,10 +110,14 @@ public class ActionBarImpl extends ActionBar { private int mCurWindowVisibility = View.VISIBLE; + private boolean mHiddenByApp; + private boolean mHiddenBySystem; + private boolean mShowingForMode; + + private boolean mNowShowing = true; + private Animator mCurrentShowAnim; - private Animator mCurrentModeAnim; private boolean mShowHideAnimationEnabled; - boolean mWasHiddenBeforeMode; final AnimatorListener mHideListener = new AnimatorListenerAdapter() { @Override @@ -129,6 +133,9 @@ public class ActionBarImpl extends ActionBar { mContainerView.setTransitioning(false); mCurrentShowAnim = null; completeDeferredDestroyActionMode(); + if (mOverlayLayout != null) { + mOverlayLayout.requestFitSystemWindows(); + } } }; @@ -430,16 +437,13 @@ public class ActionBarImpl extends ActionBar { } public ActionMode startActionMode(ActionMode.Callback callback) { - boolean wasHidden = false; if (mActionMode != null) { - wasHidden = mWasHiddenBeforeMode; mActionMode.finish(); } mContextView.killMode(); ActionModeImpl mode = new ActionModeImpl(callback); if (mode.dispatchOnCreate()) { - mWasHiddenBeforeMode = !isShowing() || wasHidden; mode.invalidate(); mContextView.initForMode(mode); animateToMode(true); @@ -584,21 +588,91 @@ public class ActionBarImpl extends ActionBar { @Override public void show() { - show(true, false); + if (mHiddenByApp) { + mHiddenByApp = false; + updateVisibility(false); + } } - public void show(boolean markHiddenBeforeMode, boolean alwaysAnimate) { + private void showForActionMode() { + if (!mShowingForMode) { + mShowingForMode = true; + if (mOverlayLayout != null) { + mOverlayLayout.setShowingForActionMode(true); + } + updateVisibility(false); + } + } + + public void showForSystem() { + if (mHiddenBySystem) { + mHiddenBySystem = false; + updateVisibility(true); + } + } + + @Override + public void hide() { + if (!mHiddenByApp) { + mHiddenByApp = true; + updateVisibility(false); + } + } + + private void hideForActionMode() { + if (mShowingForMode) { + mShowingForMode = false; + if (mOverlayLayout != null) { + mOverlayLayout.setShowingForActionMode(false); + } + updateVisibility(false); + } + } + + public void hideForSystem() { + if (!mHiddenBySystem) { + mHiddenBySystem = true; + updateVisibility(true); + } + } + + private static boolean checkShowingFlags(boolean hiddenByApp, boolean hiddenBySystem, + boolean showingForMode) { + if (showingForMode) { + return true; + } else if (hiddenByApp || hiddenBySystem) { + return false; + } else { + return true; + } + } + + private void updateVisibility(boolean fromSystem) { + // Based on the current state, should we be hidden or shown? + final boolean shown = checkShowingFlags(mHiddenByApp, mHiddenBySystem, + mShowingForMode); + + if (shown) { + if (!mNowShowing) { + mNowShowing = true; + doShow(fromSystem); + } + } else { + if (mNowShowing) { + mNowShowing = false; + doHide(fromSystem); + } + } + } + + public void doShow(boolean fromSystem) { if (mCurrentShowAnim != null) { mCurrentShowAnim.end(); } - if (mTopVisibilityView.getVisibility() == View.VISIBLE) { - if (markHiddenBeforeMode) mWasHiddenBeforeMode = false; - return; - } mTopVisibilityView.setVisibility(View.VISIBLE); if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled - || alwaysAnimate)) { + || fromSystem)) { mTopVisibilityView.setAlpha(0); mTopVisibilityView.setTranslationY(-mTopVisibilityView.getHeight()); AnimatorSet anim = new AnimatorSet(); @@ -619,6 +693,16 @@ public class ActionBarImpl extends ActionBar { com.android.internal.R.interpolator.decelerate_quad)); anim.setDuration(mContext.getResources().getInteger( com.android.internal.R.integer.config_mediumAnimTime)); + // If this is being shown from the system, add a small delay. + // This is because we will also be animating in the status bar, + // and these two elements can't be done in lock-step. So we give + // a little time for the status bar to start its animation before + // the action bar animates. (This corresponds to the corresponding + // case when hiding, where the status bar has a small delay before + // starting.) + if (fromSystem) { + anim.setStartDelay(100); + } anim.addListener(mShowListener); mCurrentShowAnim = anim; anim.start(); @@ -627,23 +711,18 @@ public class ActionBarImpl extends ActionBar { mContainerView.setTranslationY(0); mShowListener.onAnimationEnd(null); } + if (mOverlayLayout != null) { + mOverlayLayout.requestFitSystemWindows(); + } } - @Override - public void hide() { - hide(false); - } - - public void hide(boolean alwaysAnimate) { + public void doHide(boolean fromSystem) { if (mCurrentShowAnim != null) { mCurrentShowAnim.end(); } - if (mTopVisibilityView.getVisibility() == View.GONE) { - return; - } if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled - || alwaysAnimate)) { + || fromSystem)) { mTopVisibilityView.setAlpha(1); mContainerView.setTransitioning(true); AnimatorSet anim = new AnimatorSet(); @@ -673,15 +752,18 @@ public class ActionBarImpl extends ActionBar { } public boolean isShowing() { - return mTopVisibilityView.getVisibility() == View.VISIBLE; + return mNowShowing; + } + + public boolean isSystemShowing() { + return !mHiddenBySystem; } void animateToMode(boolean toActionMode) { if (toActionMode) { - show(false, false); - } - if (mCurrentModeAnim != null) { - mCurrentModeAnim.end(); + showForActionMode(); + } else { + hideForActionMode(); } mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE); @@ -740,11 +822,13 @@ public class ActionBarImpl extends ActionBar { return; } - // If we were hidden before the mode was shown, defer the onDestroy - // callback until the animation is finished and associated relayout - // is about to happen. This lets apps better anticipate visibility - // and layout behavior. - if (mWasHiddenBeforeMode) { + // If this change in state is going to cause the action bar + // to be hidden, defer the onDestroy callback until the animation + // is finished and associated relayout is about to happen. This lets + // apps better anticipate visibility and layout behavior. + if (!checkShowingFlags(mHiddenByApp, mHiddenBySystem, false)) { + // With the current state but the action bar hidden, our + // overall showing state is going to be false. mDeferredDestroyActionMode = this; mDeferredModeDestroyCallback = mCallback; } else { @@ -758,10 +842,6 @@ public class ActionBarImpl extends ActionBar { mActionView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); mActionMode = null; - - if (mWasHiddenBeforeMode) { - hide(); - } } @Override diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java index d1652df..a129496 100644 --- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java +++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import android.widget.FrameLayout; @@ -76,6 +77,26 @@ public class ActionBarOverlayLayout extends FrameLayout { } } + public void setShowingForActionMode(boolean showing) { + if (showing) { + // Here's a fun hack: if the status bar is currently being hidden, + // and the application has asked for stable content insets, then + // we will end up with the action mode action bar being shown + // without the status bar, but moved below where the status bar + // would be. Not nice. Trying to have this be positioned + // correctly is not easy (basically we need yet *another* content + // inset from the window manager to know where to put it), so + // instead we will just temporarily force the status bar to be shown. + if ((getWindowSystemUiVisibility() & (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | SYSTEM_UI_FLAG_LAYOUT_STABLE)) + == (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_LAYOUT_STABLE)) { + setDisabledSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN); + } + } else { + setDisabledSystemUiVisibility(0); + } + } + @Override public void onWindowSystemUiVisibilityChanged(int visible) { super.onWindowSystemUiVisibilityChanged(visible); @@ -83,11 +104,13 @@ public class ActionBarOverlayLayout extends FrameLayout { final int diff = mLastSystemUiVisibility ^ visible; mLastSystemUiVisibility = visible; final boolean barVisible = (visible&SYSTEM_UI_FLAG_FULLSCREEN) == 0; - final boolean wasVisible = mActionBar != null ? mActionBar.isShowing() : true; - if (barVisible != wasVisible || (diff&SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { + final boolean wasVisible = mActionBar != null ? mActionBar.isSystemShowing() : true; + if (mActionBar != null) { + if (barVisible) mActionBar.showForSystem(); + else mActionBar.hideForSystem(); + } + if ((diff&SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { if (mActionBar != null) { - if (barVisible) mActionBar.show(true, true); - else mActionBar.hide(true); requestFitSystemWindows(); } } |