summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2012-05-05 20:36:38 -0700
committerDianne Hackborn <hackbod@google.com>2012-05-06 11:52:05 -0700
commit139e5aa1da51b27231ab36344cf2d0dafab23f1e (patch)
treea15bf78ea4fc0f31bf8c6793aa0072c04c12fca0
parentd0c66f6a8f15bb95648b4306d2583575d0bb9935 (diff)
downloadframeworks_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.java17
-rw-r--r--core/java/android/view/ViewRootImpl.java1
-rw-r--r--core/java/com/android/internal/app/ActionBarImpl.java154
-rw-r--r--core/java/com/android/internal/widget/ActionBarOverlayLayout.java31
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 &quot;drag shadow&quot;. 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();
}
}