OSDN Git Service

Fix a regression where android:windowContentOverlay did not draw properly.
authorAdam Powell <adamp@google.com>
Wed, 31 Jul 2013 20:58:43 +0000 (13:58 -0700)
committerAdam Powell <adamp@google.com>
Thu, 1 Aug 2013 23:02:02 +0000 (16:02 -0700)
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

core/java/com/android/internal/app/ActionBarImpl.java
core/java/com/android/internal/widget/ActionBarOverlayLayout.java
core/res/res/layout-xlarge/screen_action_bar.xml
core/res/res/layout/screen_action_bar.xml
core/res/res/values/symbols.xml
policy/src/com/android/internal/policy/impl/PhoneWindow.java

index e092fff..066d6c3 100644 (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,
index f359146..88ff7e2 100644 (file)
 
 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) {
@@ -352,6 +378,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);
         }
     }
index 4f28678..e495e53 100644 (file)
@@ -28,30 +28,23 @@ the Action Bar enabled overlaying application content.
     <FrameLayout android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
-    <LinearLayout android:id="@+id/top_action_bar"
-                  android:layout_width="match_parent"
-                  android:layout_height="wrap_content">
-        <com.android.internal.widget.ActionBarContainer android:id="@+id/action_bar_container"
+    <com.android.internal.widget.ActionBarContainer
+        android:id="@+id/action_bar_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        style="?android:attr/actionBarStyle"
+        android:gravity="top">
+        <com.android.internal.widget.ActionBarView
+            android:id="@+id/action_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="?android:attr/actionBarStyle" />
+        <com.android.internal.widget.ActionBarContextView
+            android:id="@+id/action_context_bar"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_alignParentTop="true"
-            style="?android:attr/actionBarStyle"
-            android:gravity="top">
-            <com.android.internal.widget.ActionBarView
-                android:id="@+id/action_bar"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                style="?android:attr/actionBarStyle" />
-            <com.android.internal.widget.ActionBarContextView
-                android:id="@+id/action_context_bar"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:visibility="gone"
-                style="?android:attr/actionModeStyle" />
-        </com.android.internal.widget.ActionBarContainer>
-        <ImageView android:src="?android:attr/windowContentOverlay"
-                   android:scaleType="fitXY"
-                   android:layout_width="match_parent"
-                   android:layout_height="wrap_content" />
-    </LinearLayout>
+            android:visibility="gone"
+            style="?android:attr/actionModeStyle" />
+    </com.android.internal.widget.ActionBarContainer>
 </com.android.internal.widget.ActionBarOverlayLayout>
index e310bf5..b1889a2 100644 (file)
@@ -25,34 +25,27 @@ This is an optimized layout for a screen with the Action Bar enabled.
     android:layout_height="match_parent"
     android:splitMotionEvents="false">
     <FrameLayout android:id="@android:id/content"
+                 android:layout_width="match_parent"
+                 android:layout_height="match_parent" />
+    <com.android.internal.widget.ActionBarContainer
+        android:id="@+id/action_bar_container"
         android:layout_width="match_parent"
-        android:layout_height="match_parent" />
-    <LinearLayout android:id="@+id/top_action_bar"
-                  android:layout_width="match_parent"
-                  android:layout_height="wrap_content">
-        <com.android.internal.widget.ActionBarContainer android:id="@+id/action_bar_container"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        style="?android:attr/actionBarStyle"
+        android:gravity="top">
+        <com.android.internal.widget.ActionBarView
+            android:id="@+id/action_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="?android:attr/actionBarStyle" />
+        <com.android.internal.widget.ActionBarContextView
+            android:id="@+id/action_context_bar"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_alignParentTop="true"
-            style="?android:attr/actionBarStyle"
-            android:gravity="top">
-            <com.android.internal.widget.ActionBarView
-                android:id="@+id/action_bar"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                style="?android:attr/actionBarStyle" />
-            <com.android.internal.widget.ActionBarContextView
-                android:id="@+id/action_context_bar"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:visibility="gone"
-                style="?android:attr/actionModeStyle" />
-        </com.android.internal.widget.ActionBarContainer>
-        <ImageView android:src="?android:attr/windowContentOverlay"
-                   android:scaleType="fitXY"
-                   android:layout_width="match_parent"
-                   android:layout_height="wrap_content" />
-    </LinearLayout>
+            android:visibility="gone"
+            style="?android:attr/actionModeStyle" />
+    </com.android.internal.widget.ActionBarContainer>
     <com.android.internal.widget.ActionBarContainer android:id="@+id/split_action_bar"
                   android:layout_width="match_parent"
                   android:layout_height="wrap_content"
index 008c334..3e29221 100755 (executable)
   <java-symbol type="id" name="to_common" />
   <java-symbol type="id" name="to_org" />
   <java-symbol type="id" name="to_org_unit" />
-  <java-symbol type="id" name="top_action_bar" />
   <java-symbol type="id" name="topPanel" />
   <java-symbol type="id" name="up" />
   <java-symbol type="id" name="value" />
index 459c92c..26f910e 100644 (file)
@@ -2985,6 +2985,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
                         mActionBar.initIndeterminateProgress();
                     }
 
+                    final ActionBarOverlayLayout abol = (ActionBarOverlayLayout) findViewById(
+                            com.android.internal.R.id.action_bar_overlay_layout);
+                    if (abol != null) {
+                        abol.setOverlayMode(
+                                (localFeatures & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0);
+                    }
+
                     boolean splitActionBar = false;
                     final boolean splitWhenNarrow =
                             (mUiOptions & ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW) != 0;