Merge "Update view side for small template / header changes"
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 2e526e0..49818e6 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
@@ -32,6 +32,8 @@
import android.text.format.DateUtils;
import android.text.style.ForegroundColorSpan;
+import java.util.Calendar;
+
import androidx.app.slice.Slice;
import androidx.app.slice.SliceProvider;
import androidx.app.slice.builders.GridBuilder;
@@ -167,12 +169,25 @@
}
private Slice createContact(Uri sliceUri) {
+ final long lastCalled = System.currentTimeMillis() - 20 * DateUtils.MINUTE_IN_MILLIS;
+ CharSequence lastCalledString = DateUtils.getRelativeTimeSpanString(lastCalled,
+ Calendar.getInstance().getTimeInMillis(),
+ DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE);
return new ListBuilder(getContext(), sliceUri)
.setColor(0xff3949ab)
- .addRow(b -> b
+ .setHeader(b -> b
.setTitle("Mady Pitza")
- .setSubtitle("Frequently contacted contact")
- .addEndItem(Icon.createWithResource(getContext(), R.drawable.mady)))
+ .setSummarySubtitle("Called " + lastCalledString)
+ .setContentIntent(getBroadcastIntent(ACTION_TOAST, "See contact info")))
+ .addRow(b -> b
+ .setTitleItem(Icon.createWithResource(getContext(), R.drawable.ic_call))
+ .setTitle("314-259-2653")
+ .setSubtitle("Call lasted 1 hr 17 min")
+ .addEndItem(lastCalled))
+ .addRow(b -> b
+ .setTitleItem(Icon.createWithResource(getContext(), R.drawable.ic_text))
+ .setTitle("You: Coooooool see you then")
+ .addEndItem(System.currentTimeMillis() - 40 * DateUtils.MINUTE_IN_MILLIS))
.addGrid(b -> b
.addCell(cb -> cb
.addImage(Icon.createWithResource(getContext(), R.drawable.ic_call))
@@ -263,6 +278,11 @@
return new ListBuilder(getContext(), sliceUri)
.setColor(0xff0F9D58)
+ .setHeader(b -> b
+ .setTitle("Get ride")
+ .setSubtitle(headerSubtitle)
+ .setSummarySubtitle("Ride to work in 12 min | Ride home in 1 hour 45 min")
+ .setContentIntent(getBroadcastIntent(ACTION_TOAST, "get ride")))
.addRow(b -> b
.setTitle("Work")
.setSubtitle(workSubtitle)
@@ -270,7 +290,7 @@
getBroadcastIntent(ACTION_TOAST, "work")))
.addRow(b -> b
.setTitle("Home")
- .setSubtitle("2 hours 33 min via 101")
+ .setSubtitle(homeSubtitle)
.addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_home),
getBroadcastIntent(ACTION_TOAST, "home")))
.build();
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderV1Impl.java b/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderV1Impl.java
index 475be81..1e8b4e8 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderV1Impl.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderV1Impl.java
@@ -288,7 +288,7 @@
@NonNull
@Override
public void setTitleItem(long timeStamp) {
- mStartItem = new SliceItem(timeStamp, FORMAT_TIMESTAMP, null, new String[0]);
+ mStartItem = new SliceItem(timeStamp, FORMAT_TIMESTAMP, null, new String[]{HINT_TITLE});
}
/**
@@ -303,7 +303,7 @@
*/
@Override
public void setTitleItem(@Nullable Icon icon, boolean isLoading) {
- mStartItem = new SliceItem(icon, FORMAT_IMAGE, null, new String[0]);
+ mStartItem = new SliceItem(icon, FORMAT_IMAGE, null, new String[]{HINT_TITLE});
if (isLoading) {
mStartItem.addHint(HINT_PARTIAL);
}
@@ -322,7 +322,8 @@
@Override
public void setTitleItem(Icon icon, PendingIntent action, boolean isLoading) {
Slice actionSlice = new Slice.Builder(getBuilder()).addIcon(icon, null).build();
- mStartItem = new SliceItem(action, actionSlice, FORMAT_ACTION, null, new String[0]);
+ mStartItem = new SliceItem(action, actionSlice, FORMAT_ACTION, null,
+ new String[]{HINT_TITLE});
if (isLoading) {
mStartItem.addHint(HINT_PARTIAL);
}
@@ -519,7 +520,6 @@
if (mContentIntent != null) {
wrapped.addAction(mContentIntent, b.build(), null /* subtype */);
}
- wrapped.addHints(HINT_TITLE);
}
/**
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/ListContent.java b/slices/view/src/main/java/androidx/app/slice/widget/ListContent.java
index 5b29ab2..d74230c 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/ListContent.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/ListContent.java
@@ -17,13 +17,12 @@
package androidx.app.slice.widget;
import static android.app.slice.Slice.HINT_ACTIONS;
-import static android.app.slice.Slice.HINT_LIST;
import static android.app.slice.Slice.HINT_LIST_ITEM;
-import static android.app.slice.Slice.HINT_SUMMARY;
import static android.app.slice.Slice.SUBTYPE_COLOR;
import static android.app.slice.SliceItem.FORMAT_ACTION;
import static android.app.slice.SliceItem.FORMAT_INT;
import static android.app.slice.SliceItem.FORMAT_SLICE;
+import static android.app.slice.SliceItem.FORMAT_TEXT;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -43,10 +42,10 @@
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class ListContent {
+ private SliceItem mHeaderItem;
private SliceItem mColorItem;
- private SliceItem mSummaryItem;
private ArrayList<SliceItem> mRowItems = new ArrayList<>();
- private boolean mHasHeader;
+ private List<SliceItem> mSliceActions;
public ListContent(Slice slice) {
populate(slice);
@@ -57,9 +56,8 @@
*/
public void reset() {
mColorItem = null;
- mSummaryItem = null;
+ mHeaderItem = null;
mRowItems.clear();
- mHasHeader = false;
}
/**
@@ -68,24 +66,36 @@
public boolean populate(Slice slice) {
reset();
mColorItem = SliceQuery.findSubtype(slice, FORMAT_INT, SUBTYPE_COLOR);
- // Find summary
- SliceItem summaryItem = getSummaryItem(slice);
- mSummaryItem = summaryItem;
+ // Find slice actions
+ SliceItem actionGroup = SliceQuery.find(slice, FORMAT_SLICE, HINT_ACTIONS, null);
+ if (actionGroup != null) {
+ // TODO: actually use the actions
+ mSliceActions = SliceQuery.findAll(actionGroup, FORMAT_ACTION, HINT_ACTIONS, null);
+ }
+ // Find header
+ mHeaderItem = findHeaderItem(slice);
+ if (mHeaderItem != null) {
+ mRowItems.add(mHeaderItem);
+ }
// Filter + create row items
List<SliceItem> children = slice.getItems();
for (int i = 0; i < children.size(); i++) {
final SliceItem child = children.get(i);
final String format = child.getFormat();
- if (!child.hasAnyHints(HINT_SUMMARY, HINT_ACTIONS)
+ if (!child.hasAnyHints(HINT_ACTIONS)
&& (FORMAT_ACTION.equals(format) || FORMAT_SLICE.equals(format))) {
- if (!mHasHeader && !child.hasHint(HINT_LIST_ITEM)) {
- mHasHeader = true;
+ if (mHeaderItem == null && !child.hasHint(HINT_LIST_ITEM)) {
+ mHeaderItem = child;
mRowItems.add(0, child);
- } else {
+ } else if (child.hasHint(HINT_LIST_ITEM)) {
mRowItems.add(child);
}
}
}
+ // Ensure we have something for the header -- use first row
+ if (mHeaderItem == null && mRowItems.size() >= 1) {
+ mHeaderItem = mRowItems.get(0);
+ }
return isValid();
}
@@ -93,8 +103,7 @@
* @return whether this list has content that is valid to display.
*/
public boolean isValid() {
- return mSummaryItem != null
- || mRowItems.size() > 0;
+ return mRowItems.size() > 0;
}
@Nullable
@@ -103,8 +112,13 @@
}
@Nullable
- public SliceItem getSummaryItem() {
- return mSummaryItem;
+ public SliceItem getHeaderItem() {
+ return mHeaderItem;
+ }
+
+ @Nullable
+ public List<SliceItem> getSliceActions() {
+ return mSliceActions;
}
public ArrayList<SliceItem> getRowItems() {
@@ -112,76 +126,28 @@
}
/**
- * @return whether this list has a header or not.
+ * @return whether this list has an explicit header (i.e. row item without HINT_LIST_ITEM)
*/
public boolean hasHeader() {
- return mHasHeader;
+ return mHeaderItem != null && isValidHeader(mHeaderItem);
}
- /**
- * @return A slice item of format slice that is hinted to be shown when the slice is in small
- * format, or is the best option if nothing is appropriately hinted.
- */
- private static SliceItem getSummaryItem(@NonNull Slice slice) {
- List<SliceItem> items = slice.getItems();
- // See if a summary is specified
- SliceItem summary = SliceQuery.find(slice, FORMAT_SLICE, HINT_SUMMARY, null);
- if (summary != null) {
- return summary;
+ @Nullable
+ private static SliceItem findHeaderItem(@NonNull Slice slice) {
+ // See if header is specified
+ SliceItem header = SliceQuery.find(slice, FORMAT_SLICE, null, HINT_LIST_ITEM);
+ if (header != null && isValidHeader(header)) {
+ return header;
}
- // Otherwise use the first non-color item and use it if it's a slice
- SliceItem firstSlice = null;
- for (int i = 0; i < items.size(); i++) {
- if (!FORMAT_INT.equals(items.get(i).getFormat())) {
- firstSlice = items.get(i);
- break;
- }
- }
- if (firstSlice != null && FORMAT_SLICE.equals(firstSlice.getFormat())) {
- // Check if this slice is appropriate to use to populate small template
- if (firstSlice.hasHint(HINT_LIST)) {
- // Check for header, use that if it exists
- SliceItem listHeader = SliceQuery.find(firstSlice, FORMAT_SLICE,
- null,
- new String[] {
- HINT_LIST_ITEM, HINT_LIST
- });
- if (listHeader != null) {
- return findFirstSlice(listHeader);
- } else {
- // Otherwise use the first list item
- SliceItem newFirst = firstSlice.getSlice().getItems().get(0);
- return findFirstSlice(newFirst);
- }
- } else {
- // Not a list, find first slice with non-slice children
- return findFirstSlice(firstSlice);
- }
- }
- // Fallback, just use this and convert to SliceItem type slice
- Slice.Builder sb = new Slice.Builder(slice.getUri());
- Slice s = sb.addSubSlice(slice).build();
- return s.getItems().get(0);
+ return null;
}
- /**
- * @return Finds the first slice that has non-slice children.
- */
- private static SliceItem findFirstSlice(SliceItem slice) {
- if (!FORMAT_SLICE.equals(slice.getFormat())) {
- return slice;
+ private static boolean isValidHeader(SliceItem sliceItem) {
+ if (FORMAT_SLICE.equals(sliceItem.getFormat()) && !sliceItem.hasHint(HINT_LIST_ITEM)) {
+ // Minimum valid header is a slice with text
+ SliceItem item = SliceQuery.find(sliceItem, FORMAT_TEXT, (String) null, null);
+ return item != null;
}
- List<SliceItem> items = slice.getSlice().getItems();
- for (int i = 0; i < items.size(); i++) {
- if (FORMAT_SLICE.equals(items.get(i).getFormat())) {
- SliceItem childSlice = items.get(i);
- return findFirstSlice(childSlice);
- } else {
- // Doesn't have slice children so return it
- return slice;
- }
- }
- // Slices all the way down, just return it
- return slice;
+ return false;
}
}
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/RowContent.java b/slices/view/src/main/java/androidx/app/slice/widget/RowContent.java
index 2d6954e..735db8e 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/RowContent.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/RowContent.java
@@ -16,6 +16,7 @@
package androidx.app.slice.widget;
+import static android.app.slice.Slice.HINT_SUMMARY;
import static android.app.slice.Slice.HINT_TITLE;
import static android.app.slice.SliceItem.FORMAT_ACTION;
import static android.app.slice.SliceItem.FORMAT_IMAGE;
@@ -48,12 +49,14 @@
private SliceItem mStartItem;
private SliceItem mTitleItem;
private SliceItem mSubtitleItem;
+ private SliceItem mSummaryItem;
private ArrayList<SliceItem> mEndItems = new ArrayList<>();
private boolean mEndItemsContainAction;
private SliceItem mRange;
+ private boolean mIsHeader;
- public RowContent(SliceItem rowSlice, boolean showStartItem) {
- populate(rowSlice, showStartItem);
+ public RowContent(SliceItem rowSlice, boolean isHeader) {
+ populate(rowSlice, isHeader);
}
/**
@@ -65,13 +68,15 @@
mTitleItem = null;
mSubtitleItem = null;
mEndItems.clear();
+ mIsHeader = false;
}
/**
* @return whether this row has content that is valid to display.
*/
- public boolean populate(SliceItem rowSlice, boolean showStartItem) {
+ public boolean populate(SliceItem rowSlice, boolean isHeader) {
reset();
+ mIsHeader = isHeader;
if (!isValidRow(rowSlice)) {
Log.w(TAG, "Provided SliceItem is invalid for RowContent");
return false;
@@ -96,9 +101,7 @@
if (rowItems.size() > 0) {
// Start item
if (isStartType(rowItems.get(0))) {
- if (showStartItem) {
- mStartItem = rowItems.get(0);
- }
+ mStartItem = rowItems.get(0);
rowItems.remove(0);
}
// Text + end items
@@ -107,10 +110,12 @@
final SliceItem item = rowItems.get(i);
if (FORMAT_TEXT.equals(item.getFormat())) {
if ((mTitleItem == null || !mTitleItem.hasHint(HINT_TITLE))
- && item.hasHint(HINT_TITLE)) {
+ && item.hasHint(HINT_TITLE) && !item.hasHint(HINT_SUMMARY)) {
mTitleItem = item;
- } else if (mSubtitleItem == null) {
+ } else if (mSubtitleItem == null && !item.hasHint(HINT_SUMMARY)) {
mSubtitleItem = item;
+ } else if (mSummaryItem == null && item.hasHint(HINT_SUMMARY)) {
+ mSummaryItem = item;
}
} else {
endItems.add(item);
@@ -164,7 +169,7 @@
@Nullable
public SliceItem getStartItem() {
- return mStartItem;
+ return mIsHeader ? null : mStartItem;
}
@Nullable
@@ -177,6 +182,11 @@
return mSubtitleItem;
}
+ @Nullable
+ public SliceItem getSummaryItem() {
+ return mSummaryItem == null ? mSubtitleItem : mSummaryItem;
+ }
+
public ArrayList<SliceItem> getEndItems() {
return mEndItems;
}
@@ -192,6 +202,9 @@
* @return whether this is a valid item to use to populate a row of content.
*/
private static boolean isValidRow(SliceItem rowSlice) {
+ if (rowSlice == null) {
+ return false;
+ }
// Must be slice or action
if (FORMAT_SLICE.equals(rowSlice.getFormat())
|| FORMAT_ACTION.equals(rowSlice.getFormat())) {
@@ -237,8 +250,9 @@
*/
private static boolean isStartType(SliceItem item) {
final String type = item.getFormat();
- return (FORMAT_ACTION.equals(type) && (SliceQuery.find(item, FORMAT_IMAGE) != null))
- || FORMAT_IMAGE.equals(type)
- || FORMAT_TIMESTAMP.equals(type);
+ return item.hasHint(HINT_TITLE)
+ && ((FORMAT_ACTION.equals(type) && (SliceQuery.find(item, FORMAT_IMAGE) != null))
+ || FORMAT_IMAGE.equals(type)
+ || FORMAT_TIMESTAMP.equals(type));
}
}
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 f46354c..5956317 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
@@ -134,7 +134,7 @@
mInSmallMode = false;
mRowIndex = index;
mIsHeader = isHeader;
- mRowContent = new RowContent(slice, !mIsHeader /* showStartItem */);
+ mRowContent = new RowContent(slice, mIsHeader);
populateViews();
}
@@ -147,7 +147,7 @@
mRowIndex = 0;
mIsHeader = true;
ListContent lc = new ListContent(slice);
- mRowContent = new RowContent(lc.getSummaryItem(), false /* showStartItem */);
+ mRowContent = new RowContent(lc.getHeaderItem(), true /* isHeader */);
populateViews();
}
@@ -174,7 +174,9 @@
mPrimaryText.setTextColor(mTitleColor);
mPrimaryText.setVisibility(titleItem != null ? View.VISIBLE : View.GONE);
- final SliceItem subTitle = mRowContent.getSubtitleItem();
+ final SliceItem subTitle = mInSmallMode
+ ? mRowContent.getSummaryItem()
+ : mRowContent.getSubtitleItem();
if (subTitle != null) {
mSecondaryText.setText(subTitle.getText());
}