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
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index e092fff..066d6c3 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -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 @@
private ActionBarOverlayLayout mOverlayLayout;
private ActionBarContainer mContainerView;
- private ViewGroup mTopVisibilityView;
private ActionBarView mActionView;
private ActionBarContextView mContextView;
private ActionBarContainer mSplitView;
@@ -125,12 +126,12 @@
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 @@
@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 @@
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 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 @@
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 @@
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 @@
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,
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index f359146..88ff7e2 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -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 @@
* 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 @@
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 @@
}
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 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 @@
// 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 @@
topInset = mActionBarTop.getMeasuredHeight();
}
- if (mActionView.isSplitActionBar()) {
+ if (mActionBarView.isSplitActionBar()) {
// If action bar is split, adjust bottom insets for it.
if (mActionBarBottom != null) {
if (stable) {
@@ -352,6 +378,18 @@
}
@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 @@
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);
}
}