am bb829e3d: Merge "Add APIs to set AppBarLayout to be expanded or not" into lmp-mr1-ub-dev

* commit 'bb829e3d51be7da7e4d02c1abc58c5f9132197c2':
  Add APIs to set AppBarLayout to be expanded or not
diff --git a/design/api/current.txt b/design/api/current.txt
index 7ecf0f8..6b5058f 100644
--- a/design/api/current.txt
+++ b/design/api/current.txt
@@ -7,6 +7,8 @@
     method public float getTargetElevation();
     method public final int getTotalScrollRange();
     method public void removeOnOffsetChangedListener(android.support.design.widget.AppBarLayout.OnOffsetChangedListener);
+    method public void setExpanded(boolean);
+    method public void setExpanded(boolean, boolean);
     method public void setTargetElevation(float);
   }
 
diff --git a/design/res/values/attrs.xml b/design/res/values/attrs.xml
index a23b699..2dfeb41 100644
--- a/design/res/values/attrs.xml
+++ b/design/res/values/attrs.xml
@@ -172,6 +172,9 @@
     <declare-styleable name="AppBarLayout">
         <attr name="elevation" />
         <attr name="android:background" />
+        <!-- The initial expanded state for the AppBarLayout. This only takes effect when this
+             view is a direct child of a CoordinatorLayout. -->
+        <attr name="expanded" format="boolean" />
     </declare-styleable>
 
     <declare-styleable name="AppBarLayout_LayoutParams">
diff --git a/design/src/android/support/design/widget/AppBarLayout.java b/design/src/android/support/design/widget/AppBarLayout.java
index 949ce5f..9808e5e 100644
--- a/design/src/android/support/design/widget/AppBarLayout.java
+++ b/design/src/android/support/design/widget/AppBarLayout.java
@@ -95,6 +95,11 @@
 @CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)
 public class AppBarLayout extends LinearLayout {
 
+    private static final int PENDING_ACTION_NONE = 0x0;
+    private static final int PENDING_ACTION_EXPANDED = 0x1;
+    private static final int PENDING_ACTION_COLLAPSED = 0x2;
+    private static final int PENDING_ACTION_ANIMATE_ENABLED = 0x4;
+
     /**
      * Interface definition for a callback to be invoked when an {@link AppBarLayout}'s vertical
      * offset changes.
@@ -121,6 +126,8 @@
 
     private float mTargetElevation;
 
+    private int mPendingAction = PENDING_ACTION_NONE;
+
     private WindowInsetsCompat mLastInsets;
 
     private final List<WeakReference<OnOffsetChangedListener>> mListeners;
@@ -137,6 +144,9 @@
                 0, R.style.Widget_Design_AppBarLayout);
         mTargetElevation = a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0);
         setBackgroundDrawable(a.getDrawable(R.styleable.AppBarLayout_android_background));
+        if (a.hasValue(R.styleable.AppBarLayout_expanded)) {
+            setExpanded(a.getBoolean(R.styleable.AppBarLayout_expanded, false));
+        }
         a.recycle();
 
         // Use the bounds view outline provider so that we cast a shadow, even without a background
@@ -223,6 +233,40 @@
         super.setOrientation(orientation);
     }
 
+    /**
+     * Sets whether this {@link AppBarLayout} is expanded or not, animating if it has already
+     * been laid out.
+     *
+     * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a
+     * direct child of a {@link CoordinatorLayout}.</p>
+     *
+     * @param expanded true if the layout should be fully expanded, false if it should
+     *                 be fully collapsed
+     *
+     * @attr ref android.support.design.R.styleable#AppBarLayout_expanded
+     */
+    public void setExpanded(boolean expanded) {
+        setExpanded(expanded, ViewCompat.isLaidOut(this));
+    }
+
+    /**
+     * Sets whether this {@link AppBarLayout} is expanded or not.
+     *
+     * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a
+     * direct child of a {@link CoordinatorLayout}.</p>
+     *
+     * @param expanded true if the layout should be fully expanded, false if it should
+     *                 be fully collapsed
+     * @param animate Whether to animate to the new state
+     *
+     * @attr ref android.support.design.R.styleable#AppBarLayout_expanded
+     */
+    public void setExpanded(boolean expanded, boolean animate) {
+        mPendingAction = (expanded ? PENDING_ACTION_EXPANDED : PENDING_ACTION_COLLAPSED)
+                | (animate ? PENDING_ACTION_ANIMATE_ENABLED : 0);
+        requestLayout();
+    }
+
     @Override
     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
         return p instanceof LayoutParams;
@@ -419,6 +463,14 @@
         return mTargetElevation;
     }
 
+    int getPendingAction() {
+        return mPendingAction;
+    }
+
+    void resetPendingAction() {
+        mPendingAction = PENDING_ACTION_NONE;
+    }
+
     private void setWindowInsets(WindowInsetsCompat insets) {
         // Invalidate the total scroll range...
         mTotalScrollRange = INVALID_SCROLL_RANGE;
@@ -771,12 +823,31 @@
         }
 
         @Override
-        public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout appBarLayout,
+        public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout abl,
                 int layoutDirection) {
-            boolean handled = super.onLayoutChild(parent, appBarLayout, layoutDirection);
+            boolean handled = super.onLayoutChild(parent, abl, layoutDirection);
 
-            if (mOffsetToChildIndexOnLayout >= 0) {
-                View child = appBarLayout.getChildAt(mOffsetToChildIndexOnLayout);
+            final int pendingAction = abl.getPendingAction();
+            if (pendingAction != PENDING_ACTION_NONE) {
+                final boolean animate = (pendingAction & PENDING_ACTION_ANIMATE_ENABLED) != 0;
+                if ((pendingAction & PENDING_ACTION_COLLAPSED) != 0) {
+                    final int offset = -abl.getUpNestedPreScrollRange();
+                    if (animate) {
+                        animateOffsetTo(parent, abl, offset);
+                    } else {
+                        setAppBarTopBottomOffset(parent, abl, offset);
+                    }
+                } else if ((pendingAction & PENDING_ACTION_EXPANDED) != 0) {
+                    if (animate) {
+                        animateOffsetTo(parent, abl, 0);
+                    } else {
+                        setAppBarTopBottomOffset(parent, abl, 0);
+                    }
+                }
+                // Finally reset the pending state
+                abl.resetPendingAction();
+            } else if (mOffsetToChildIndexOnLayout >= 0) {
+                View child = abl.getChildAt(mOffsetToChildIndexOnLayout);
                 int offset = -child.getBottom();
                 if (mOffsetToChildIndexOnLayoutIsMinHeight) {
                     offset += ViewCompat.getMinimumHeight(child);
@@ -788,7 +859,7 @@
             }
 
             // Make sure we update the elevation
-            dispatchOffsetUpdates(appBarLayout);
+            dispatchOffsetUpdates(abl);
 
             return handled;
         }
diff --git a/design/src/android/support/design/widget/CollapsingTextHelper.java b/design/src/android/support/design/widget/CollapsingTextHelper.java
index 8b3bd84..46b3b82 100644
--- a/design/src/android/support/design/widget/CollapsingTextHelper.java
+++ b/design/src/android/support/design/widget/CollapsingTextHelper.java
@@ -85,6 +85,8 @@
     private float mScale;
     private float mCurrentTextSize;
 
+    private boolean mBoundsChanged;
+
     private final TextPaint mTextPaint;
 
     private Interpolator mPositionInterpolator;
@@ -140,11 +142,17 @@
     }
 
     void setExpandedBounds(int left, int top, int right, int bottom) {
-        mExpandedBounds.set(left, top, right, bottom);
+        if (!rectEquals(mExpandedBounds, left, top, right, bottom)) {
+            mExpandedBounds.set(left, top, right, bottom);
+            mBoundsChanged = true;
+        }
     }
 
     void setCollapsedBounds(int left, int top, int right, int bottom) {
-        mCollapsedBounds.set(left, top, right, bottom);
+        if (!rectEquals(mCollapsedBounds, left, top, right, bottom)) {
+            mCollapsedBounds.set(left, top, right, bottom);
+            mBoundsChanged = true;
+        }
     }
 
     void setExpandedTextGravity(int gravity) {
@@ -424,8 +432,9 @@
         }
 
         if (availableWidth > 0) {
-            updateDrawText = mCurrentTextSize != newTextSize;
+            updateDrawText = (mCurrentTextSize != newTextSize) || mBoundsChanged;
             mCurrentTextSize = newTextSize;
+            mBoundsChanged = false;
         }
 
         if (mTextToDraw == null || updateDrawText) {
@@ -552,4 +561,8 @@
         }
         return AnimationUtils.lerp(startValue, endValue, fraction);
     }
+
+    private static boolean rectEquals(Rect r, int left, int top, int right, int bottom) {
+        return !(r.left != left || r.top != top || r.right != right || r.bottom != bottom);
+    }
 }