Merge changes Idbbf8dd8,I1142b508

* changes:
  Support summary content and restrict end items (builders)
  Support summary and restrict end items in RowView (views)
diff --git a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
index 2f2a1c3..9042ed1 100644
--- a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
+++ b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
@@ -122,6 +122,13 @@
     private Slice createRideSlice(Uri sliceUri) {
         return new ListBuilder(sliceUri)
                 .setColor(0xff1b5e20)
+                .addSummaryRow(b -> b
+                    .setTitle("Get ride")
+                    .setSubtitle("Multiple cars 4 minutes away")
+                    .addEndItem(getBroadcastIntent(ACTION_TOAST, "home"),
+                            Icon.createWithResource(getContext(), R.drawable.ic_home))
+                    .addEndItem(getBroadcastIntent(ACTION_TOAST, "work"),
+                            Icon.createWithResource(getContext(), R.drawable.ic_work)))
                 .add(b -> b
                     .setContentIntent(getBroadcastIntent(ACTION_TOAST, "work"))
                     .setTitle("Work")
@@ -131,8 +138,7 @@
                     .setContentIntent(getBroadcastIntent(ACTION_TOAST, "home"))
                     .setTitle("Home")
                     .setSubtitle("2 hours 33 min via 101")
-                    .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_home))
-                    .setIsHeader(true))
+                    .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_home)))
                 .add(b -> b
                     .setContentIntent(getBroadcastIntent(ACTION_TOAST, "book ride"))
                     .setTitle("Book ride")
diff --git a/slices/builders/api/current.txt b/slices/builders/api/current.txt
index 52f30c8..2a02c32 100644
--- a/slices/builders/api/current.txt
+++ b/slices/builders/api/current.txt
@@ -4,6 +4,8 @@
     ctor public ListBuilder(android.net.Uri);
     method public androidx.app.slice.builders.ListBuilder add(androidx.app.slice.builders.RowBuilder);
     method public androidx.app.slice.builders.ListBuilder add(java.util.function.Consumer<androidx.app.slice.builders.RowBuilder>);
+    method public androidx.app.slice.builders.ListBuilder addSummaryRow(androidx.app.slice.builders.RowBuilder);
+    method public androidx.app.slice.builders.ListBuilder addSummaryRow(java.util.function.Consumer<androidx.app.slice.builders.RowBuilder>);
   }
 
   public class MessagingSliceBuilder extends androidx.app.slice.builders.TemplateSliceBuilder {
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/ListBuilder.java b/slices/builders/src/main/java/androidx/app/slice/builders/ListBuilder.java
index cd644d5..04a107c 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/ListBuilder.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/ListBuilder.java
@@ -18,6 +18,8 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
+import static androidx.app.slice.core.SliceHints.HINT_SUMMARY;
+
 import android.net.Uri;
 import android.os.Build;
 import android.support.annotation.NonNull;
@@ -37,7 +39,7 @@
  * <ul>
  *     <li>Shortcut - The slice is displayed as an icon with a text label.</li>
  *     <li>Small - Only a single row of content is displayed in small format, to specify which
- *         row to display in small format see {@link RowBuilder#setIsHeader(boolean)}.</li>
+ *         row to display in small format see {@link #addSummaryRow(RowBuilder)}.</li>
  *     <li>Large - As many rows of content are shown as possible. If the presenter of the slice
  *         allows scrolling then all rows of content will be displayed in a scrollable view.</li>
  * </ul>
@@ -47,6 +49,8 @@
  */
 public class ListBuilder extends TemplateSliceBuilder {
 
+    private boolean mHasSummary;
+
     public ListBuilder(@NonNull Uri uri) {
         super(uri);
     }
@@ -79,6 +83,47 @@
     }
 
     /**
+     * Add a summary row for this template. The summary content is displayed
+     * when the slice is displayed in small format.
+     * <p>
+     * Only one summary row can be added, this throws {@link IllegalArgumentException} if
+     * called more than once.
+     * </p>
+     */
+    public ListBuilder addSummaryRow(RowBuilder builder) {
+        if (mHasSummary) {
+            throw new IllegalArgumentException("Trying to add summary row when one has "
+                    + "already been added");
+        }
+        builder.getBuilder().addHints(HINT_SUMMARY);
+        getBuilder().addSubSlice(builder.build(), null);
+        mHasSummary = true;
+        return this;
+    }
+
+    /**
+     * Add a summary row for this template. The summary content is displayed
+     * when the slice is displayed in small format.
+     * <p>
+     * Only one summary row can be added, this throws {@link IllegalArgumentException} if
+     * called more than once.
+     * </p>
+     */
+    @RequiresApi(api = Build.VERSION_CODES.N)
+    public ListBuilder addSummaryRow(Consumer<RowBuilder> c) {
+        if (mHasSummary) {
+            throw new IllegalArgumentException("Trying to add summary row when one has "
+                    + "already been added");
+        }
+        RowBuilder b = new RowBuilder(this);
+        c.accept(b);
+        b.getBuilder().addHints(HINT_SUMMARY);
+        getBuilder().addSubSlice(b.build(), null);
+        mHasSummary = true;
+        return this;
+    }
+
+    /**
      * Sets the color to tint items displayed by this template (e.g. icons).
      * @hide
      */
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/RowBuilder.java b/slices/builders/src/main/java/androidx/app/slice/builders/RowBuilder.java
index f2e57ba..7373186 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/RowBuilder.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/RowBuilder.java
@@ -66,6 +66,10 @@
     private SliceItem mSubtitleItem;
     private SliceItem mStartItem;
     private ArrayList<SliceItem> mEndItems = new ArrayList<>();
+    private boolean mHasToggle;
+    private boolean mHasEndAction;
+    private boolean mHasEndImage;
+    private boolean mHasTimestamp;
 
     public RowBuilder(ListBuilder parent) {
         super(parent.createChildBuilder());
@@ -76,9 +80,8 @@
     }
 
     /**
-     * Sets this row to be considered the header of the slice. This means that when the slice is
-     * requested to be show in small format, it will display only the contents specified in this
-     * row. If a slice has no header specified, the first row item will be used in the small format.
+     * Sets this row to be the header of the slice. This item will be displayed at the
+     * top of the slice and other items in the slice will scroll below it.
      */
     public RowBuilder setIsHeader(boolean isHeader) {
         mIsHeader = isHeader;
@@ -86,13 +89,19 @@
     }
 
     /**
-     * Sets the title item to be the provided timestamp.
+     * Sets the title item to be the provided timestamp. Only one timestamp can be added, if
+     * one is already added this will throw {@link IllegalArgumentException}.
      * <p>
      * There can only be one title item, this will replace any other title
      * items that may have been set.
      */
     public RowBuilder setTitleItem(long timeStamp) {
+        if (mHasTimestamp) {
+            throw new IllegalArgumentException("Trying to add a timestamp when one has "
+                    + "already been added");
+        }
         mStartItem = new SliceItem(timeStamp, FORMAT_TIMESTAMP, null, new String[0]);
+        mHasTimestamp = true;
         return this;
     }
 
@@ -144,29 +153,50 @@
     }
 
     /**
-     * Adds a timestamp to be displayed at the end of the row.
+     * Adds a timestamp to be displayed at the end of the row. Only one timestamp can be added, if
+     * one is already added this will throw {@link IllegalArgumentException}.
      */
     public RowBuilder addEndItem(long timeStamp) {
-        // TODO -- should multiple timestamps be allowed at the end of the row?
+        if (mHasTimestamp) {
+            throw new IllegalArgumentException("Trying to add a timestamp when one has "
+                    + "already been added");
+        }
         mEndItems.add(new SliceItem(timeStamp, FORMAT_TIMESTAMP, null, new String[0]));
+        mHasTimestamp = true;
         return this;
     }
 
     /**
-     * Adds an icon to be displayed at the end of the row.
+     * Adds an icon to be displayed at the end of the row. A mixture of icons and tappable
+     * icons is not permitted, if an action has already been added this will throw
+     * {@link IllegalArgumentException}.
      */
     public RowBuilder addEndItem(Icon icon) {
+        if (mHasEndAction) {
+            throw new IllegalArgumentException("Trying to add an icon to end items when an action "
+                    + "has already been added. End items cannot have a mixture of "
+                    + "tappable icons and icons.");
+        }
         mEndItems.add(new SliceItem(icon, FORMAT_IMAGE, null,
                 new String[] {HINT_NO_TINT, HINT_LARGE}));
+        mHasEndImage = true;
         return this;
     }
 
     /**
-     * Adds a tappable icon to be displayed at the end of the row.
+     * Adds a tappable icon to be displayed at the end of the row. A mixture of icons and tappable
+     * icons is not permitted, if an icon has already been added this will throw
+     * {@link IllegalArgumentException}.
      */
     public RowBuilder addEndItem(@NonNull PendingIntent action, @NonNull Icon icon) {
+        if (mHasEndImage) {
+            throw new IllegalArgumentException("Trying to add an action to end items when an icon "
+                            + "has already been added. End items cannot have a mixture of "
+                            + "tappable icons and icons.");
+        }
         Slice actionSlice = new Slice.Builder(getBuilder()).addIcon(icon, null).build();
         mEndItems.add(new SliceItem(action, actionSlice, FORMAT_ACTION, null, new String[0]));
+        mHasEndAction = true;
         return this;
     }
 
@@ -175,11 +205,16 @@
      * that were added will not be shown.
      */
     public RowBuilder addToggle(@NonNull PendingIntent action, boolean isChecked) {
+        if (mHasToggle) {
+            throw new IllegalArgumentException("Trying to add a toggle when one has already "
+                    + "been added.");
+        }
         @Slice.SliceHint String[] hints = isChecked
                 ? new String[] {SUBTYPE_TOGGLE, HINT_SELECTED}
                 : new String[] {SUBTYPE_TOGGLE};
         Slice s = new Slice.Builder(getBuilder()).addHints(hints).build();
         mEndItems.add(0, new SliceItem(action, s, FORMAT_ACTION, null, hints));
+        mHasToggle = true;
         return this;
     }
 
@@ -189,6 +224,10 @@
      */
     public RowBuilder addToggle(@NonNull PendingIntent action, @NonNull Icon icon,
             boolean isChecked) {
+        if (mHasToggle) {
+            throw new IllegalArgumentException("Trying to add a toggle when one has already "
+                    + "been added.");
+        }
         @Slice.SliceHint String[] hints = isChecked
                 ? new String[] {SliceHints.SUBTYPE_TOGGLE, HINT_SELECTED}
                 : new String[] {SliceHints.SUBTYPE_TOGGLE};
@@ -196,6 +235,7 @@
                 .addIcon(icon, null)
                 .addHints(hints).build();
         mEndItems.add(0, new SliceItem(action, actionSlice, FORMAT_ACTION, null, hints));
+        mHasToggle = true;
         return this;
     }
 
diff --git a/slices/core/src/main/java/androidx/app/slice/Slice.java b/slices/core/src/main/java/androidx/app/slice/Slice.java
index bda341d..517ea13 100644
--- a/slices/core/src/main/java/androidx/app/slice/Slice.java
+++ b/slices/core/src/main/java/androidx/app/slice/Slice.java
@@ -81,7 +81,7 @@
     @RestrictTo(Scope.LIBRARY)
     @StringDef({HINT_TITLE, HINT_LIST, HINT_LIST_ITEM, HINT_LARGE, HINT_ACTIONS, HINT_SELECTED,
             HINT_HORIZONTAL, HINT_NO_TINT, HINT_PARTIAL,
-            SliceHints.HINT_HIDDEN, SliceHints.SUBTYPE_TOGGLE})
+            SliceHints.HINT_SUMMARY, SliceHints.SUBTYPE_TOGGLE})
     public @interface SliceHint{ }
 
     private final SliceItem[] mItems;
diff --git a/slices/core/src/main/java/androidx/app/slice/SliceItem.java b/slices/core/src/main/java/androidx/app/slice/SliceItem.java
index e4412d1..51517e0 100644
--- a/slices/core/src/main/java/androidx/app/slice/SliceItem.java
+++ b/slices/core/src/main/java/androidx/app/slice/SliceItem.java
@@ -269,7 +269,7 @@
      * @hide
      */
     @RestrictTo(Scope.LIBRARY)
-    public boolean hasAnyHints(@Slice.SliceHint String[] hints) {
+    public boolean hasAnyHints(@Slice.SliceHint String... hints) {
         if (hints == null) return false;
         for (String hint : hints) {
             if (ArrayUtils.contains(mHints, hint)) {
diff --git a/slices/core/src/main/java/androidx/app/slice/core/SliceHints.java b/slices/core/src/main/java/androidx/app/slice/core/SliceHints.java
index 96e2c3f..34acf93 100644
--- a/slices/core/src/main/java/androidx/app/slice/core/SliceHints.java
+++ b/slices/core/src/main/java/androidx/app/slice/core/SliceHints.java
@@ -39,12 +39,10 @@
      * Key to retrieve an extra added to an intent when a control is changed.
      */
     public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
+
     /**
-     * Hint to indicate that this content should not be shown in the
-     * {@link androidx.app.slice.widget.SliceView#MODE_SMALL}
-     * and {@link androidx.app.slice.widget.SliceView#MODE_LARGE} modes of SliceView.
-     * This content may be used to populate
-     * the {@link androidx.app.slice.widget.SliceView#MODE_SHORTCUT} format of the slice.
+     * Hint indicating this content should be shown instead of the normal content when the slice
+     * is in small format
      */
-    public static final String HINT_HIDDEN = "hidden";
+    public static final String HINT_SUMMARY = "summary";
 }
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/GridView.java b/slices/view/src/main/java/androidx/app/slice/widget/GridRowView.java
similarity index 98%
rename from slices/view/src/main/java/androidx/app/slice/widget/GridView.java
rename to slices/view/src/main/java/androidx/app/slice/widget/GridRowView.java
index edccc2c..531928f 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/GridView.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/GridRowView.java
@@ -62,7 +62,7 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 @TargetApi(24)
-public class GridView extends LinearLayout implements LargeSliceAdapter.SliceListView,
+public class GridRowView extends LinearLayout implements LargeSliceAdapter.SliceListView,
         View.OnClickListener, SliceView.SliceModeView {
 
     private static final String TAG = "GridView";
@@ -94,11 +94,11 @@
     private int mBigPictureHeight;
     private int mAllImagesHeight;
 
-    public GridView(Context context) {
+    public GridRowView(Context context) {
         this(context, null);
     }
 
-    public GridView(Context context, AttributeSet attrs) {
+    public GridRowView(Context context, AttributeSet attrs) {
         super(context, attrs);
         final Resources res = getContext().getResources();
         mIconSize = res.getDimensionPixelSize(R.dimen.abc_slice_icon_size);
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/LargeTemplateView.java b/slices/view/src/main/java/androidx/app/slice/widget/LargeTemplateView.java
index eb03d91..f77e1d9 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/LargeTemplateView.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/LargeTemplateView.java
@@ -24,6 +24,8 @@
 import static android.app.slice.SliceItem.FORMAT_SLICE;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
+import static androidx.app.slice.core.SliceHints.HINT_SUMMARY;
+
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.support.annotation.RestrictTo;
@@ -101,7 +103,7 @@
             slice.getItems().forEach(new Consumer<SliceItem>() {
                 @Override
                 public void accept(SliceItem item) {
-                    if (item.hasHint(HINT_ACTIONS)) {
+                    if (item.hasAnyHints(HINT_ACTIONS, HINT_SUMMARY)) {
                         return;
                     } else if (FORMAT_COLOR.equals(item.getFormat())) {
                         return;
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/RowView.java b/slices/view/src/main/java/androidx/app/slice/widget/RowView.java
index b903e3b..c516021 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/RowView.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/RowView.java
@@ -29,6 +29,7 @@
 import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
 
 import static androidx.app.slice.core.SliceHints.EXTRA_TOGGLE_STATE;
+import static androidx.app.slice.core.SliceHints.HINT_SUMMARY;
 
 import android.annotation.TargetApi;
 import android.app.PendingIntent;
@@ -76,9 +77,8 @@
 
     private int mIconSize;
     private int mPadding;
-
-    // If this is being used as a small template we don't allow a start item, for list items we do.
-    private boolean mAllowStartItem;
+    private boolean mInSmallMode;
+    private boolean mIsHeader;
 
     private LinearLayout mStartContainer;
     private LinearLayout mContent;
@@ -125,7 +125,8 @@
      */
     @Override
     public void setSliceItem(SliceItem slice, boolean isHeader) {
-        mAllowStartItem = !isHeader; // Headers don't show start items
+        mIsHeader = isHeader;
+        mInSmallMode = false;
         populateViews(slice, slice);
     }
 
@@ -134,16 +135,21 @@
      */
     @Override
     public void setSlice(Slice slice) {
-        mAllowStartItem = false;
+        mInSmallMode = true;
         Slice.Builder sb = new Slice.Builder(slice.getUri());
         sb.addSubSlice(slice);
         Slice parentSlice = sb.build();
-        populateViews(parentSlice.getItems().get(0), getHeaderItem(slice));
+        populateViews(parentSlice.getItems().get(0), getSummaryItem(slice));
     }
 
-    private SliceItem getHeaderItem(Slice slice) {
+    private SliceItem getSummaryItem(Slice slice) {
         List<SliceItem> items = slice.getItems();
-        // See if a header is specified
+        // See if a summary is specified
+        SliceItem summary = SliceQuery.find(slice, FORMAT_SLICE, HINT_SUMMARY, null);
+        if (summary != null) {
+            return summary;
+        }
+        // First fallback is using a header
         SliceItem header = SliceQuery.find(slice, FORMAT_SLICE, null, HINT_LIST_ITEM);
         if (header != null) {
             return header;
@@ -199,7 +205,7 @@
         SliceItem subTitle = null;
         ArrayList<SliceItem> endItems = new ArrayList<>();
 
-        // If the first item is an action let's check if it should be used to populate the content
+        // If the first item is an action check if it should be used to populate the content
         // or if it should be in the start position.
         SliceItem firstSlice = items.size() > 0 ? items.get(0) : null;
         if (firstSlice != null && FORMAT_ACTION.equals(firstSlice.getFormat())) {
@@ -255,7 +261,7 @@
                         : -1;
         // Populate main part of the template
         if (startItem != null) {
-            if (mAllowStartItem) {
+            if (!mIsHeader) {
                 startItem = addItem(startItem, color, mStartContainer, 0 /* padding */)
                         ? startItem
                         : null;
@@ -263,8 +269,8 @@
                     endItems.remove(startItem);
                 }
             } else {
-                startItem = null;
                 endItems.add(0, startItem);
+                startItem = null;
             }
         }
         mStartContainer.setVisibility(startItem != null ? View.VISIBLE : View.GONE);
@@ -290,14 +296,10 @@
                 .filter(new Predicate<SliceItem>() {
                     @Override
                     public boolean test(SliceItem item) {
-                        if (item == null) {
-                            return false;
-                        }
                         return FORMAT_ACTION.equals(item.getFormat())
                                 && SliceQuery.hasHints(item.getSlice(), SliceHints.SUBTYPE_TOGGLE);
                     }
-                })
-                .findFirst().orElse(null);
+                }).findFirst().orElse(null);
         if (toggleItem != null) {
             if (addToggle(toggleItem, color)) {
                 mDivider.setVisibility(mRowAction != null ? View.VISIBLE : View.GONE);
@@ -310,9 +312,9 @@
         int itemCount = 0;
         for (int i = 0; i < endItems.size(); i++) {
             SliceItem item = endItems.get(i);
-            if (item == null) {
-                // do nothing
-            } else if (itemCount <= MAX_END_ITEMS) {
+            // Only show one type of format at the end of the slice, use whatever is first
+            if (itemCount <= MAX_END_ITEMS
+                    && item.getFormat().equals(endItems.get(0).getFormat())) {
                 if (FORMAT_ACTION.equals(item.getFormat())
                         && itemCount == 0
                         && SliceQuery.hasHints(item.getSlice(), SliceHints.SUBTYPE_TOGGLE)
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/SliceView.java b/slices/view/src/main/java/androidx/app/slice/widget/SliceView.java
index 6a40898..b3a7f0c 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/SliceView.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/SliceView.java
@@ -260,7 +260,7 @@
             case MODE_SMALL:
                 // Check if it's horizontal
                 if (SliceQuery.hasHints(mCurrentSlice, HINT_HORIZONTAL)) {
-                    return new GridView(getContext());
+                    return new GridRowView(getContext());
                 } else {
                     return new RowView(getContext());
                 }
diff --git a/slices/view/src/main/res/layout/abc_slice_grid.xml b/slices/view/src/main/res/layout/abc_slice_grid.xml
index 7e264d0..890f77d 100644
--- a/slices/view/src/main/res/layout/abc_slice_grid.xml
+++ b/slices/view/src/main/res/layout/abc_slice_grid.xml
@@ -14,7 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<androidx.app.slice.widget.GridView
+<androidx.app.slice.widget.GridRowView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -22,4 +22,4 @@
     android:gravity="center_vertical"
     android:background="?android:attr/activatedBackgroundIndicator"
     android:clipToPadding="false">
-</androidx.app.slice.widget.GridView>
+</androidx.app.slice.widget.GridRowView>