Skip to content

Commit 139e5aa

Browse files
author
Dianne Hackborn
committed
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
1 parent d0c66f6 commit 139e5aa

File tree

4 files changed

+162
-41
lines changed

4 files changed

+162
-41
lines changed

core/java/android/view/View.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15236,6 +15236,18 @@ void updateLocalSystemUiVisibility(int localValue, int localChanges) {
1523615236
}
1523715237
}
1523815238

15239+
/** @hide */
15240+
public void setDisabledSystemUiVisibility(int flags) {
15241+
if (mAttachInfo != null) {
15242+
if (mAttachInfo.mDisabledSystemUiVisibility != flags) {
15243+
mAttachInfo.mDisabledSystemUiVisibility = flags;
15244+
if (mParent != null) {
15245+
mParent.recomputeViewAttributes(this);
15246+
}
15247+
}
15248+
}
15249+
}
15250+
1523915251
/**
1524015252
* Creates an image that the system displays during the drag and drop
1524115253
* operation. This is called a "drag shadow". The default implementation
@@ -16912,6 +16924,11 @@ public void setPooled(boolean isPooled) {
1691216924
*/
1691316925
int mSystemUiVisibility;
1691416926

16927+
/**
16928+
* Hack to force certain system UI visibility flags to be cleared.
16929+
*/
16930+
int mDisabledSystemUiVisibility;
16931+
1691516932
/**
1691616933
* True if a view in this hierarchy has an OnSystemUiVisibilityChangeListener
1691716934
* attached.

core/java/android/view/ViewRootImpl.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,6 +1025,7 @@ private boolean collectViewAttributes() {
10251025
attachInfo.mSystemUiVisibility = 0;
10261026
attachInfo.mHasSystemUiListeners = false;
10271027
mView.dispatchCollectViewAttributes(attachInfo, 0);
1028+
attachInfo.mSystemUiVisibility &= ~attachInfo.mDisabledSystemUiVisibility;
10281029
if (attachInfo.mKeepScreenOn != oldScreenOn
10291030
|| attachInfo.mSystemUiVisibility != oldVis
10301031
|| attachInfo.mHasSystemUiListeners != oldHasSystemUiListeners) {

core/java/com/android/internal/app/ActionBarImpl.java

Lines changed: 117 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@
3939
import android.content.res.Configuration;
4040
import android.content.res.Resources;
4141
import android.graphics.drawable.Drawable;
42-
import android.os.Build;
4342
import android.os.Handler;
43+
import android.util.Log;
4444
import android.util.TypedValue;
4545
import android.view.ActionMode;
4646
import android.view.ContextThemeWrapper;
@@ -110,10 +110,14 @@ public class ActionBarImpl extends ActionBar {
110110

111111
private int mCurWindowVisibility = View.VISIBLE;
112112

113+
private boolean mHiddenByApp;
114+
private boolean mHiddenBySystem;
115+
private boolean mShowingForMode;
116+
117+
private boolean mNowShowing = true;
118+
113119
private Animator mCurrentShowAnim;
114-
private Animator mCurrentModeAnim;
115120
private boolean mShowHideAnimationEnabled;
116-
boolean mWasHiddenBeforeMode;
117121

118122
final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
119123
@Override
@@ -129,6 +133,9 @@ public void onAnimationEnd(Animator animation) {
129133
mContainerView.setTransitioning(false);
130134
mCurrentShowAnim = null;
131135
completeDeferredDestroyActionMode();
136+
if (mOverlayLayout != null) {
137+
mOverlayLayout.requestFitSystemWindows();
138+
}
132139
}
133140
};
134141

@@ -430,16 +437,13 @@ public int getDisplayOptions() {
430437
}
431438

432439
public ActionMode startActionMode(ActionMode.Callback callback) {
433-
boolean wasHidden = false;
434440
if (mActionMode != null) {
435-
wasHidden = mWasHiddenBeforeMode;
436441
mActionMode.finish();
437442
}
438443

439444
mContextView.killMode();
440445
ActionModeImpl mode = new ActionModeImpl(callback);
441446
if (mode.dispatchOnCreate()) {
442-
mWasHiddenBeforeMode = !isShowing() || wasHidden;
443447
mode.invalidate();
444448
mContextView.initForMode(mode);
445449
animateToMode(true);
@@ -584,21 +588,91 @@ public int getHeight() {
584588

585589
@Override
586590
public void show() {
587-
show(true, false);
591+
if (mHiddenByApp) {
592+
mHiddenByApp = false;
593+
updateVisibility(false);
594+
}
588595
}
589596

590-
public void show(boolean markHiddenBeforeMode, boolean alwaysAnimate) {
597+
private void showForActionMode() {
598+
if (!mShowingForMode) {
599+
mShowingForMode = true;
600+
if (mOverlayLayout != null) {
601+
mOverlayLayout.setShowingForActionMode(true);
602+
}
603+
updateVisibility(false);
604+
}
605+
}
606+
607+
public void showForSystem() {
608+
if (mHiddenBySystem) {
609+
mHiddenBySystem = false;
610+
updateVisibility(true);
611+
}
612+
}
613+
614+
@Override
615+
public void hide() {
616+
if (!mHiddenByApp) {
617+
mHiddenByApp = true;
618+
updateVisibility(false);
619+
}
620+
}
621+
622+
private void hideForActionMode() {
623+
if (mShowingForMode) {
624+
mShowingForMode = false;
625+
if (mOverlayLayout != null) {
626+
mOverlayLayout.setShowingForActionMode(false);
627+
}
628+
updateVisibility(false);
629+
}
630+
}
631+
632+
public void hideForSystem() {
633+
if (!mHiddenBySystem) {
634+
mHiddenBySystem = true;
635+
updateVisibility(true);
636+
}
637+
}
638+
639+
private static boolean checkShowingFlags(boolean hiddenByApp, boolean hiddenBySystem,
640+
boolean showingForMode) {
641+
if (showingForMode) {
642+
return true;
643+
} else if (hiddenByApp || hiddenBySystem) {
644+
return false;
645+
} else {
646+
return true;
647+
}
648+
}
649+
650+
private void updateVisibility(boolean fromSystem) {
651+
// Based on the current state, should we be hidden or shown?
652+
final boolean shown = checkShowingFlags(mHiddenByApp, mHiddenBySystem,
653+
mShowingForMode);
654+
655+
if (shown) {
656+
if (!mNowShowing) {
657+
mNowShowing = true;
658+
doShow(fromSystem);
659+
}
660+
} else {
661+
if (mNowShowing) {
662+
mNowShowing = false;
663+
doHide(fromSystem);
664+
}
665+
}
666+
}
667+
668+
public void doShow(boolean fromSystem) {
591669
if (mCurrentShowAnim != null) {
592670
mCurrentShowAnim.end();
593671
}
594-
if (mTopVisibilityView.getVisibility() == View.VISIBLE) {
595-
if (markHiddenBeforeMode) mWasHiddenBeforeMode = false;
596-
return;
597-
}
598672
mTopVisibilityView.setVisibility(View.VISIBLE);
599673

600674
if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled
601-
|| alwaysAnimate)) {
675+
|| fromSystem)) {
602676
mTopVisibilityView.setAlpha(0);
603677
mTopVisibilityView.setTranslationY(-mTopVisibilityView.getHeight());
604678
AnimatorSet anim = new AnimatorSet();
@@ -619,6 +693,16 @@ public void show(boolean markHiddenBeforeMode, boolean alwaysAnimate) {
619693
com.android.internal.R.interpolator.decelerate_quad));
620694
anim.setDuration(mContext.getResources().getInteger(
621695
com.android.internal.R.integer.config_mediumAnimTime));
696+
// If this is being shown from the system, add a small delay.
697+
// This is because we will also be animating in the status bar,
698+
// and these two elements can't be done in lock-step. So we give
699+
// a little time for the status bar to start its animation before
700+
// the action bar animates. (This corresponds to the corresponding
701+
// case when hiding, where the status bar has a small delay before
702+
// starting.)
703+
if (fromSystem) {
704+
anim.setStartDelay(100);
705+
}
622706
anim.addListener(mShowListener);
623707
mCurrentShowAnim = anim;
624708
anim.start();
@@ -627,23 +711,18 @@ public void show(boolean markHiddenBeforeMode, boolean alwaysAnimate) {
627711
mContainerView.setTranslationY(0);
628712
mShowListener.onAnimationEnd(null);
629713
}
714+
if (mOverlayLayout != null) {
715+
mOverlayLayout.requestFitSystemWindows();
716+
}
630717
}
631718

632-
@Override
633-
public void hide() {
634-
hide(false);
635-
}
636-
637-
public void hide(boolean alwaysAnimate) {
719+
public void doHide(boolean fromSystem) {
638720
if (mCurrentShowAnim != null) {
639721
mCurrentShowAnim.end();
640722
}
641-
if (mTopVisibilityView.getVisibility() == View.GONE) {
642-
return;
643-
}
644723

645724
if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled
646-
|| alwaysAnimate)) {
725+
|| fromSystem)) {
647726
mTopVisibilityView.setAlpha(1);
648727
mContainerView.setTransitioning(true);
649728
AnimatorSet anim = new AnimatorSet();
@@ -673,15 +752,18 @@ public void hide(boolean alwaysAnimate) {
673752
}
674753

675754
public boolean isShowing() {
676-
return mTopVisibilityView.getVisibility() == View.VISIBLE;
755+
return mNowShowing;
756+
}
757+
758+
public boolean isSystemShowing() {
759+
return !mHiddenBySystem;
677760
}
678761

679762
void animateToMode(boolean toActionMode) {
680763
if (toActionMode) {
681-
show(false, false);
682-
}
683-
if (mCurrentModeAnim != null) {
684-
mCurrentModeAnim.end();
764+
showForActionMode();
765+
} else {
766+
hideForActionMode();
685767
}
686768

687769
mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
@@ -740,11 +822,13 @@ public void finish() {
740822
return;
741823
}
742824

743-
// If we were hidden before the mode was shown, defer the onDestroy
744-
// callback until the animation is finished and associated relayout
745-
// is about to happen. This lets apps better anticipate visibility
746-
// and layout behavior.
747-
if (mWasHiddenBeforeMode) {
825+
// If this change in state is going to cause the action bar
826+
// to be hidden, defer the onDestroy callback until the animation
827+
// is finished and associated relayout is about to happen. This lets
828+
// apps better anticipate visibility and layout behavior.
829+
if (!checkShowingFlags(mHiddenByApp, mHiddenBySystem, false)) {
830+
// With the current state but the action bar hidden, our
831+
// overall showing state is going to be false.
748832
mDeferredDestroyActionMode = this;
749833
mDeferredModeDestroyCallback = mCallback;
750834
} else {
@@ -758,10 +842,6 @@ public void finish() {
758842
mActionView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
759843

760844
mActionMode = null;
761-
762-
if (mWasHiddenBeforeMode) {
763-
hide();
764-
}
765845
}
766846

767847
@Override

core/java/com/android/internal/widget/ActionBarOverlayLayout.java

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import android.content.res.TypedArray;
2323
import android.graphics.Rect;
2424
import android.util.AttributeSet;
25+
import android.util.Log;
2526
import android.view.View;
2627
import android.widget.FrameLayout;
2728

@@ -76,18 +77,40 @@ public void setActionBar(ActionBarImpl impl) {
7677
}
7778
}
7879

80+
public void setShowingForActionMode(boolean showing) {
81+
if (showing) {
82+
// Here's a fun hack: if the status bar is currently being hidden,
83+
// and the application has asked for stable content insets, then
84+
// we will end up with the action mode action bar being shown
85+
// without the status bar, but moved below where the status bar
86+
// would be. Not nice. Trying to have this be positioned
87+
// correctly is not easy (basically we need yet *another* content
88+
// inset from the window manager to know where to put it), so
89+
// instead we will just temporarily force the status bar to be shown.
90+
if ((getWindowSystemUiVisibility() & (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
91+
| SYSTEM_UI_FLAG_LAYOUT_STABLE))
92+
== (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_LAYOUT_STABLE)) {
93+
setDisabledSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN);
94+
}
95+
} else {
96+
setDisabledSystemUiVisibility(0);
97+
}
98+
}
99+
79100
@Override
80101
public void onWindowSystemUiVisibilityChanged(int visible) {
81102
super.onWindowSystemUiVisibilityChanged(visible);
82103
pullChildren();
83104
final int diff = mLastSystemUiVisibility ^ visible;
84105
mLastSystemUiVisibility = visible;
85106
final boolean barVisible = (visible&SYSTEM_UI_FLAG_FULLSCREEN) == 0;
86-
final boolean wasVisible = mActionBar != null ? mActionBar.isShowing() : true;
87-
if (barVisible != wasVisible || (diff&SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
107+
final boolean wasVisible = mActionBar != null ? mActionBar.isSystemShowing() : true;
108+
if (mActionBar != null) {
109+
if (barVisible) mActionBar.showForSystem();
110+
else mActionBar.hideForSystem();
111+
}
112+
if ((diff&SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
88113
if (mActionBar != null) {
89-
if (barVisible) mActionBar.show(true, true);
90-
else mActionBar.hide(true);
91114
requestFitSystemWindows();
92115
}
93116
}

0 commit comments

Comments
 (0)