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>