Allow GridBuilder to specify a top-level content intent

Bug: 68378574
Test: manual, check weather / contact / gallery slices
      Check that weather is tappable
Change-Id: If10c48a05c0991b371887ee167799db6a5c7641d
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 e692ca0..0ce0190 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
@@ -124,7 +124,11 @@
     }
 
     private Slice createWeather(Uri sliceUri) {
+        SliceAction primaryAction = new SliceAction(getBroadcastIntent(ACTION_TOAST,
+                "open weather app"), Icon.createWithResource(getContext(), R.drawable.weather_1),
+                "Weather is happening!");
         return new GridBuilder(getContext(), sliceUri)
+                .setPrimaryAction(primaryAction)
                 .addCell(cb -> cb
                         .addLargeImage(Icon.createWithResource(getContext(), R.drawable.weather_1))
                         .addText("MON")
diff --git a/slices/builders/api/current.txt b/slices/builders/api/current.txt
index 21cfaf5..7d9d54a 100644
--- a/slices/builders/api/current.txt
+++ b/slices/builders/api/current.txt
@@ -8,6 +8,7 @@
     method public androidx.app.slice.builders.GridBuilder addSeeMoreAction(android.app.PendingIntent);
     method public androidx.app.slice.builders.GridBuilder addSeeMoreCell(androidx.app.slice.builders.GridBuilder.CellBuilder);
     method public androidx.app.slice.builders.GridBuilder addSeeMoreCell(java.util.function.Consumer<androidx.app.slice.builders.GridBuilder.CellBuilder>);
+    method public androidx.app.slice.builders.GridBuilder setPrimaryAction(androidx.app.slice.builders.SliceAction);
   }
 
   public static final class GridBuilder.CellBuilder extends androidx.app.slice.builders.TemplateSliceBuilder {
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/GridBuilder.java b/slices/builders/src/main/java/androidx/app/slice/builders/GridBuilder.java
index 0dff405..c3fc27a 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/GridBuilder.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/GridBuilder.java
@@ -179,6 +179,14 @@
         return this;
     }
 
+    /**
+     * Sets the intent to send when the slice is activated.
+     */
+    @NonNull
+    public GridBuilder setPrimaryAction(@NonNull SliceAction action) {
+        mImpl.setPrimaryAction(action);
+        return this;
+    }
 
     /**
      * @hide
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilder.java b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilder.java
index 302e1b3..d15dba5 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilder.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilder.java
@@ -26,6 +26,7 @@
 import android.support.annotation.RestrictTo;
 
 import androidx.app.slice.Slice;
+import androidx.app.slice.builders.SliceAction;
 
 /**
  * @hide
@@ -70,6 +71,11 @@
     void addSeeMoreAction(PendingIntent intent);
 
     /**
+     * Sets the action to be invoked if the user taps on the main content of the template.
+     */
+    void setPrimaryAction(SliceAction action);
+
+    /**
      * Builds a standalone slice of this grid builder (i.e. not contained within a List).
      */
     Slice buildIndividual();
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderBasicImpl.java b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderBasicImpl.java
index 67bbfa1..dce8b9f 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderBasicImpl.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderBasicImpl.java
@@ -27,6 +27,7 @@
 
 import androidx.app.slice.Slice;
 import androidx.app.slice.SliceSpec;
+import androidx.app.slice.builders.SliceAction;
 
 
 /**
@@ -77,6 +78,12 @@
     /**
      */
     @Override
+    public void setPrimaryAction(SliceAction action) {
+    }
+
+    /**
+     */
+    @Override
     public Slice buildIndividual() {
         // Empty slice, nothing useful from a grid to basic.
         return getBuilder().build();
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderListV1Impl.java b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderListV1Impl.java
index fe755bd..4d2ec07 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderListV1Impl.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderListV1Impl.java
@@ -21,6 +21,8 @@
 import static android.app.slice.Slice.HINT_LIST_ITEM;
 import static android.app.slice.Slice.HINT_PARTIAL;
 import static android.app.slice.Slice.HINT_SEE_MORE;
+import static android.app.slice.Slice.HINT_SHORTCUT;
+import static android.app.slice.Slice.HINT_TITLE;
 import static android.support.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.app.PendingIntent;
@@ -32,6 +34,7 @@
 
 import androidx.app.slice.Slice;
 import androidx.app.slice.SliceSpec;
+import androidx.app.slice.builders.SliceAction;
 
 /**
  * @hide
@@ -39,6 +42,8 @@
 @RestrictTo(LIBRARY)
 public class GridBuilderListV1Impl extends TemplateBuilderImpl implements GridBuilder {
 
+    private SliceAction mPrimaryAction;
+
     /**
      */
     public GridBuilderListV1Impl(@NonNull Slice.Builder builder, SliceSpec spec) {
@@ -50,6 +55,10 @@
     @Override
     public void apply(Slice.Builder builder) {
         builder.addHints(HINT_HORIZONTAL, HINT_LIST_ITEM);
+        if (mPrimaryAction != null) {
+            Slice.Builder sb = new Slice.Builder(builder).addHints(HINT_SHORTCUT, HINT_TITLE);
+            builder.addSubSlice(mPrimaryAction.buildSlice(sb));
+        }
     }
 
     /**
@@ -96,10 +105,23 @@
     /**
      */
     @Override
+    public void setPrimaryAction(SliceAction action) {
+        mPrimaryAction = action;
+    }
+
+    /**
+     */
+    @Override
     public Slice buildIndividual() {
-        return new Slice.Builder(getBuilder()).addHints(HINT_HORIZONTAL, HINT_LIST_ITEM)
-                .addSubSlice(getBuilder()
-                        .addHints(HINT_HORIZONTAL, HINT_LIST_ITEM).build()).build();
+        Slice.Builder sb = new Slice.Builder(getBuilder())
+                .addHints(HINT_HORIZONTAL, HINT_LIST_ITEM);
+        sb.addSubSlice(getBuilder().addHints(HINT_HORIZONTAL, HINT_LIST_ITEM).build());
+        if (mPrimaryAction != null) {
+            Slice.Builder actionBuilder = new Slice.Builder(getBuilder())
+                    .addHints(HINT_SHORTCUT, HINT_TITLE);
+            sb.addSubSlice(mPrimaryAction.buildSlice(actionBuilder));
+        }
+        return sb.build();
     }
 
     /**
diff --git a/slices/view/src/androidTest/java/androidx/app/slice/render/SliceCreator.java b/slices/view/src/androidTest/java/androidx/app/slice/render/SliceCreator.java
index d4551ef..ca24c98 100644
--- a/slices/view/src/androidTest/java/androidx/app/slice/render/SliceCreator.java
+++ b/slices/view/src/androidTest/java/androidx/app/slice/render/SliceCreator.java
@@ -98,7 +98,11 @@
     }
 
     private Slice createWeather(Uri sliceUri) {
+        SliceAction primaryAction = new SliceAction(getBroadcastIntent(ACTION_TOAST,
+                "open weather app"), Icon.createWithResource(getContext(), R.drawable.weather_1),
+                "Weather is happening!");
         GridBuilder b = new GridBuilder(getContext(), sliceUri);
+        b.setPrimaryAction(primaryAction);
         return b.addCell(new GridBuilder.CellBuilder(b)
                         .addLargeImage(Icon.createWithResource(getContext(), R.drawable.weather_1))
                         .addText("MON")
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/GridContent.java b/slices/view/src/main/java/androidx/app/slice/widget/GridContent.java
index 9569dc0..794187d 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/GridContent.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/GridContent.java
@@ -16,6 +16,9 @@
 
 package androidx.app.slice.widget;
 
+import static android.app.slice.Slice.HINT_ACTIONS;
+import static android.app.slice.Slice.HINT_SHORTCUT;
+import static android.app.slice.Slice.HINT_TITLE;
 import static android.app.slice.Slice.SUBTYPE_COLOR;
 import static android.app.slice.SliceItem.FORMAT_ACTION;
 import static android.app.slice.SliceItem.FORMAT_IMAGE;
@@ -24,6 +27,7 @@
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
 
+import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 
 import java.util.ArrayList;
@@ -40,8 +44,9 @@
 public class GridContent {
 
     private boolean mAllImages;
-    public SliceItem mColorItem;
-    public ArrayList<CellContent> mGridContent = new ArrayList<>();
+    private SliceItem mColorItem;
+    private SliceItem mPrimaryAction;
+    private ArrayList<CellContent> mGridContent = new ArrayList<>();
 
     public GridContent(SliceItem gridItem) {
         populate(gridItem);
@@ -58,9 +63,13 @@
     public boolean populate(SliceItem gridItem) {
         reset();
         mColorItem = SliceQuery.findSubtype(gridItem, FORMAT_INT, SUBTYPE_COLOR);
+        String[] hints = new String[] {HINT_SHORTCUT, HINT_TITLE};
+        mPrimaryAction = SliceQuery.find(gridItem, FORMAT_SLICE, hints,
+                new String[] {HINT_ACTIONS} /* nonHints */);
         mAllImages = true;
         if (FORMAT_SLICE.equals(gridItem.getFormat())) {
             List<SliceItem> items = gridItem.getSlice().getItems();
+            items = filterInvalidItems(items);
             // Check if it it's only one item that is a slice
             if (items.size() == 1 && items.get(0).getFormat().equals(FORMAT_SLICE)) {
                 items = items.get(0).getSlice().getItems();
@@ -99,6 +108,14 @@
     }
 
     /**
+     * @return the content intent item for this grid.
+     */
+    @Nullable
+    public SliceItem getContentIntent() {
+        return mPrimaryAction;
+    }
+
+    /**
      * @return whether this grid has content that is valid to display.
      */
     public boolean isValid() {
@@ -112,6 +129,17 @@
         return mAllImages;
     }
 
+    private List<SliceItem> filterInvalidItems(List<SliceItem> items) {
+        List<SliceItem> filteredItems = new ArrayList<>();
+        for (int i = 0; i < items.size(); i++) {
+            SliceItem item = items.get(i);
+            if (!item.hasHint(HINT_SHORTCUT)) {
+                filteredItems.add(item);
+            }
+        }
+        return filteredItems;
+    }
+
     /**
      * Extracts information required to present content in a cell.
      * @hide
@@ -130,7 +158,8 @@
          */
         public boolean populate(SliceItem cellItem) {
             final String format = cellItem.getFormat();
-            if (FORMAT_SLICE.equals(format) || FORMAT_ACTION.equals(format)) {
+            if (!cellItem.hasHint(HINT_SHORTCUT)
+                    && (FORMAT_SLICE.equals(format) || FORMAT_ACTION.equals(format))) {
                 List<SliceItem> items = cellItem.getSlice().getItems();
                 // If we've only got one item that's a slice / action use those items instead
                 if (items.size() == 1 && (FORMAT_ACTION.equals(items.get(0).getFormat())
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/GridRowView.java b/slices/view/src/main/java/androidx/app/slice/widget/GridRowView.java
index 6b0ae5a..f837713 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/GridRowView.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/GridRowView.java
@@ -162,6 +162,13 @@
     }
 
     private void populateViews(GridContent gc) {
+        if (gc.getContentIntent() != null) {
+            EventInfo info = new EventInfo(getMode(), EventInfo.ACTION_TYPE_CONTENT,
+                    EventInfo.ROW_TYPE_GRID, mRowIndex);
+            Pair<SliceItem, EventInfo> tagItem = new Pair(gc.getContentIntent(), info);
+            mViewContainer.setTag(tagItem);
+            makeClickable(mViewContainer);
+        }
         mIsAllImages = gc.isAllImages();
         ArrayList<GridContent.CellContent> cells = gc.getGridContent();
         final int max = mIsAllImages ? MAX_IMAGES : MAX_ALL;