Fix a regression where android:windowContentOverlay did not draw properly.

This was the victim of an earlier refactoring. Have the
ActionBarOverlayLayout draw this directly over the content so that it
can stay properly in sync with any animations and also remove an extra
couple of views from the decor layout.

Some apps now expect the broken behavior in default themes. Protect
them from themselves until they bump their targetSdkVersion.

Public bug https://code.google.com/p/android/issues/detail?id=58280

Change-Id: I4284503577e322f3e68d4a7fabda8441d3749b98
This commit is contained in:
Adam Powell
2013-07-31 13:58:43 -07:00
parent 3a6f25512c
commit 9b0dc2894d
6 changed files with 130 additions and 94 deletions

View File

@ -16,6 +16,8 @@
package com.android.internal.app;
import android.animation.ValueAnimator;
import android.view.ViewParent;
import com.android.internal.view.ActionBarPolicy;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuPopupHelper;
@ -75,7 +77,6 @@ public class ActionBarImpl extends ActionBar {
private ActionBarOverlayLayout mOverlayLayout;
private ActionBarContainer mContainerView;
private ViewGroup mTopVisibilityView;
private ActionBarView mActionView;
private ActionBarContextView mContextView;
private ActionBarContainer mSplitView;
@ -125,12 +126,12 @@ public class ActionBarImpl extends ActionBar {
public void onAnimationEnd(Animator animation) {
if (mContentAnimations && mContentView != null) {
mContentView.setTranslationY(0);
mTopVisibilityView.setTranslationY(0);
mContainerView.setTranslationY(0);
}
if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) {
mSplitView.setVisibility(View.GONE);
}
mTopVisibilityView.setVisibility(View.GONE);
mContainerView.setVisibility(View.GONE);
mContainerView.setTransitioning(false);
mCurrentShowAnim = null;
completeDeferredDestroyActionMode();
@ -144,7 +145,16 @@ public class ActionBarImpl extends ActionBar {
@Override
public void onAnimationEnd(Animator animation) {
mCurrentShowAnim = null;
mTopVisibilityView.requestLayout();
mContainerView.requestLayout();
}
};
final ValueAnimator.AnimatorUpdateListener mUpdateListener =
new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
final ViewParent parent = mContainerView.getParent();
((View) parent).invalidate();
}
};
@ -153,7 +163,7 @@ public class ActionBarImpl extends ActionBar {
Window window = activity.getWindow();
View decor = window.getDecorView();
boolean overlayMode = mActivity.getWindow().hasFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
init(decor, overlayMode);
init(decor);
if (!overlayMode) {
mContentView = decor.findViewById(android.R.id.content);
}
@ -161,26 +171,21 @@ public class ActionBarImpl extends ActionBar {
public ActionBarImpl(Dialog dialog) {
mDialog = dialog;
init(dialog.getWindow().getDecorView(), false);
init(dialog.getWindow().getDecorView());
}
private void init(View decor, boolean overlayMode) {
private void init(View decor) {
mContext = decor.getContext();
mOverlayLayout = (ActionBarOverlayLayout) decor.findViewById(
com.android.internal.R.id.action_bar_overlay_layout);
if (mOverlayLayout != null) {
mOverlayLayout.setActionBar(this, overlayMode);
mOverlayLayout.setActionBar(this);
}
mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
mContextView = (ActionBarContextView) decor.findViewById(
com.android.internal.R.id.action_context_bar);
mContainerView = (ActionBarContainer) decor.findViewById(
com.android.internal.R.id.action_bar_container);
mTopVisibilityView = (ViewGroup)decor.findViewById(
com.android.internal.R.id.top_action_bar);
if (mTopVisibilityView == null) {
mTopVisibilityView = mContainerView;
}
mSplitView = (ActionBarContainer) decor.findViewById(
com.android.internal.R.id.split_action_bar);
@ -675,29 +680,30 @@ public class ActionBarImpl extends ActionBar {
if (mCurrentShowAnim != null) {
mCurrentShowAnim.end();
}
mTopVisibilityView.setVisibility(View.VISIBLE);
mContainerView.setVisibility(View.VISIBLE);
if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled
|| fromSystem)) {
mTopVisibilityView.setTranslationY(0); // because we're about to ask its window loc
float startingY = -mTopVisibilityView.getHeight();
mContainerView.setTranslationY(0); // because we're about to ask its window loc
float startingY = -mContainerView.getHeight();
if (fromSystem) {
int topLeft[] = {0, 0};
mTopVisibilityView.getLocationInWindow(topLeft);
mContainerView.getLocationInWindow(topLeft);
startingY -= topLeft[1];
}
mTopVisibilityView.setTranslationY(startingY);
mContainerView.setTranslationY(startingY);
AnimatorSet anim = new AnimatorSet();
AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mTopVisibilityView,
"translationY", 0));
ObjectAnimator a = ObjectAnimator.ofFloat(mContainerView, View.TRANSLATION_Y, 0);
a.addUpdateListener(mUpdateListener);
AnimatorSet.Builder b = anim.play(a);
if (mContentAnimations && mContentView != null) {
b.with(ObjectAnimator.ofFloat(mContentView, "translationY",
b.with(ObjectAnimator.ofFloat(mContentView, View.TRANSLATION_Y,
startingY, 0));
}
if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) {
mSplitView.setTranslationY(mSplitView.getHeight());
mSplitView.setVisibility(View.VISIBLE);
b.with(ObjectAnimator.ofFloat(mSplitView, "translationY", 0));
b.with(ObjectAnimator.ofFloat(mSplitView, View.TRANSLATION_Y, 0));
}
anim.setInterpolator(AnimationUtils.loadInterpolator(mContext,
com.android.internal.R.interpolator.decelerate_cubic));
@ -713,8 +719,8 @@ public class ActionBarImpl extends ActionBar {
mCurrentShowAnim = anim;
anim.start();
} else {
mTopVisibilityView.setAlpha(1);
mTopVisibilityView.setTranslationY(0);
mContainerView.setAlpha(1);
mContainerView.setTranslationY(0);
if (mContentAnimations && mContentView != null) {
mContentView.setTranslationY(0);
}
@ -737,24 +743,25 @@ public class ActionBarImpl extends ActionBar {
if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled
|| fromSystem)) {
mTopVisibilityView.setAlpha(1);
mContainerView.setAlpha(1);
mContainerView.setTransitioning(true);
AnimatorSet anim = new AnimatorSet();
float endingY = -mTopVisibilityView.getHeight();
float endingY = -mContainerView.getHeight();
if (fromSystem) {
int topLeft[] = {0, 0};
mTopVisibilityView.getLocationInWindow(topLeft);
mContainerView.getLocationInWindow(topLeft);
endingY -= topLeft[1];
}
AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mTopVisibilityView,
"translationY", endingY));
ObjectAnimator a = ObjectAnimator.ofFloat(mContainerView, View.TRANSLATION_Y, endingY);
a.addUpdateListener(mUpdateListener);
AnimatorSet.Builder b = anim.play(a);
if (mContentAnimations && mContentView != null) {
b.with(ObjectAnimator.ofFloat(mContentView, "translationY",
b.with(ObjectAnimator.ofFloat(mContentView, View.TRANSLATION_Y,
0, endingY));
}
if (mSplitView != null && mSplitView.getVisibility() == View.VISIBLE) {
mSplitView.setAlpha(1);
b.with(ObjectAnimator.ofFloat(mSplitView, "translationY",
b.with(ObjectAnimator.ofFloat(mSplitView, View.TRANSLATION_Y,
mSplitView.getHeight()));
}
anim.setInterpolator(AnimationUtils.loadInterpolator(mContext,

View File

@ -16,6 +16,10 @@
package com.android.internal.widget;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.Log;
import android.view.ViewGroup;
import com.android.internal.app.ActionBarImpl;
@ -31,18 +35,23 @@ import android.view.View;
* has request that its layout ignore them.
*/
public class ActionBarOverlayLayout extends ViewGroup {
private static final String TAG = "ActionBarOverlayLayout";
private int mActionBarHeight;
private ActionBarImpl mActionBar;
private int mWindowVisibility = View.VISIBLE;
// The main UI elements that we handle the layout of.
private View mContent;
private View mActionBarTop;
private View mActionBarBottom;
private ActionBarContainer mActionBarTop;
// Some interior UI elements.
private ActionBarContainer mContainerView;
private ActionBarView mActionView;
private ActionBarView mActionBarView;
// Content overlay drawable - generally the action bar's shadow
private Drawable mWindowContentOverlay;
private boolean mIgnoreWindowContentOverlay;
private boolean mOverlayMode;
private int mLastSystemUiVisibility;
@ -53,8 +62,9 @@ public class ActionBarOverlayLayout extends ViewGroup {
private final Rect mInnerInsets = new Rect();
private final Rect mLastInnerInsets = new Rect();
static final int[] mActionBarSizeAttr = new int [] {
com.android.internal.R.attr.actionBarSize
static final int[] ATTRS = new int [] {
com.android.internal.R.attr.actionBarSize,
com.android.internal.R.attr.windowContentOverlay
};
public ActionBarOverlayLayout(Context context) {
@ -68,14 +78,18 @@ public class ActionBarOverlayLayout extends ViewGroup {
}
private void init(Context context) {
TypedArray ta = getContext().getTheme().obtainStyledAttributes(mActionBarSizeAttr);
TypedArray ta = getContext().getTheme().obtainStyledAttributes(ATTRS);
mActionBarHeight = ta.getDimensionPixelSize(0, 0);
mWindowContentOverlay = ta.getDrawable(1);
setWillNotDraw(mWindowContentOverlay == null);
ta.recycle();
mIgnoreWindowContentOverlay = context.getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.KEY_LIME_PIE;
}
public void setActionBar(ActionBarImpl impl, boolean overlayMode) {
public void setActionBar(ActionBarImpl impl) {
mActionBar = impl;
mOverlayMode = overlayMode;
if (getWindowToken() != null) {
// This is being initialized after being added to a window;
// make sure to update all state now.
@ -88,6 +102,18 @@ public class ActionBarOverlayLayout extends ViewGroup {
}
}
public void setOverlayMode(boolean overlayMode) {
mOverlayMode = overlayMode;
/*
* Drawing the window content overlay was broken before K so starting to draw it
* again unexpectedly will cause artifacts in some apps. They should fix it.
*/
mIgnoreWindowContentOverlay = overlayMode &&
getContext().getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.KEY_LIME_PIE;
}
public void setShowingForActionMode(boolean showing) {
if (showing) {
// Here's a fun hack: if the status bar is currently being hidden,
@ -253,7 +279,7 @@ public class ActionBarOverlayLayout extends ViewGroup {
// we can't depend on the size currently reported by it -- this must remain constant.
topInset = mActionBarHeight;
if (mActionBar != null && mActionBar.hasNonEmbeddedTabs()) {
View tabs = mContainerView.getTabContainer();
View tabs = mActionBarTop.getTabContainer();
if (tabs != null) {
// If tabs are not embedded, increase space on top to account for them.
topInset += mActionBarHeight;
@ -265,7 +291,7 @@ public class ActionBarOverlayLayout extends ViewGroup {
topInset = mActionBarTop.getMeasuredHeight();
}
if (mActionView.isSplitActionBar()) {
if (mActionBarView.isSplitActionBar()) {
// If action bar is split, adjust bottom insets for it.
if (mActionBarBottom != null) {
if (stable) {
@ -351,6 +377,18 @@ public class ActionBarOverlayLayout extends ViewGroup {
}
}
@Override
public void draw(Canvas c) {
super.draw(c);
if (mWindowContentOverlay != null && !mIgnoreWindowContentOverlay) {
final int top = mActionBarTop.getVisibility() == VISIBLE ?
(int) (mActionBarTop.getBottom() + mActionBarTop.getTranslationY() + 0.5f) : 0;
mWindowContentOverlay.setBounds(0, top, getWidth(),
top + mWindowContentOverlay.getIntrinsicHeight());
mWindowContentOverlay.draw(c);
}
}
@Override
public boolean shouldDelayChildPressedState() {
return false;
@ -359,10 +397,9 @@ public class ActionBarOverlayLayout extends ViewGroup {
void pullChildren() {
if (mContent == null) {
mContent = findViewById(com.android.internal.R.id.content);
mActionBarTop = findViewById(com.android.internal.R.id.top_action_bar);
mContainerView = (ActionBarContainer)findViewById(
mActionBarTop = (ActionBarContainer)findViewById(
com.android.internal.R.id.action_bar_container);
mActionView = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
mActionBarView = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
mActionBarBottom = findViewById(com.android.internal.R.id.split_action_bar);
}
}