blob: 86e9409b753c93f38046efc386f076663a16b8a0 [file] [log] [blame]
Mady Mellor6b5cd612017-12-14 11:36:59 -08001/*
2 * Copyright 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.app.slice.widget;
18
19import static android.app.slice.Slice.HINT_ACTIONS;
20import static android.app.slice.Slice.HINT_LIST;
21import static android.app.slice.Slice.HINT_LIST_ITEM;
22import static android.app.slice.Slice.SUBTYPE_COLOR;
23import static android.app.slice.SliceItem.FORMAT_ACTION;
24import static android.app.slice.SliceItem.FORMAT_INT;
25import static android.app.slice.SliceItem.FORMAT_SLICE;
26
27import android.support.annotation.NonNull;
28import android.support.annotation.Nullable;
29import android.support.annotation.RestrictTo;
30
31import java.util.ArrayList;
32import java.util.List;
33
34import androidx.app.slice.Slice;
35import androidx.app.slice.SliceItem;
36import androidx.app.slice.core.SliceHints;
37import androidx.app.slice.core.SliceQuery;
38
39/**
40 * Extracts information required to present content in a list format from a slice.
41 * @hide
42 */
43@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
44public class ListContent {
45
46 private SliceItem mColorItem;
47 private SliceItem mSummaryItem;
48 private ArrayList<SliceItem> mRowItems = new ArrayList<>();
49 private boolean mHasHeader;
50
51 public ListContent(Slice slice) {
52 populate(slice);
53 }
54
55 /**
Mady Mellor32206132017-12-21 22:22:26 -080056 * Resets the content.
57 */
58 public void reset() {
59 mColorItem = null;
60 mSummaryItem = null;
61 mRowItems.clear();
62 mHasHeader = false;
63 }
64
65 /**
Mady Mellor6b5cd612017-12-14 11:36:59 -080066 * @return whether this row has content that is valid to display.
67 */
68 public boolean populate(Slice slice) {
Mady Mellor32206132017-12-21 22:22:26 -080069 reset();
Mady Mellor6b5cd612017-12-14 11:36:59 -080070 mColorItem = SliceQuery.findSubtype(slice, FORMAT_INT, SUBTYPE_COLOR);
71 // Find summary
72 SliceItem summaryItem = getSummaryItem(slice);
73 mSummaryItem = summaryItem;
74 // Filter + create row items
75 List<SliceItem> children = slice.getItems();
76 for (int i = 0; i < children.size(); i++) {
77 final SliceItem child = children.get(i);
78 final String format = child.getFormat();
79 if (!child.hasAnyHints(SliceHints.HINT_SUMMARY, HINT_ACTIONS)
80 && (FORMAT_ACTION.equals(format) || FORMAT_SLICE.equals(format))) {
81 if (!mHasHeader && !child.hasHint(HINT_LIST_ITEM)) {
82 mHasHeader = true;
83 mRowItems.add(0, child);
84 } else {
85 mRowItems.add(child);
86 }
87 }
88 }
89 return isValid();
90 }
91
92 /**
93 * @return whether this list has content that is valid to display.
94 */
95 public boolean isValid() {
96 return mSummaryItem != null
97 || mRowItems.size() > 0;
98 }
99
100 @Nullable
101 public SliceItem getColorItem() {
102 return mColorItem;
103 }
104
105 @Nullable
106 public SliceItem getSummaryItem() {
107 return mSummaryItem;
108 }
109
110 public ArrayList<SliceItem> getRowItems() {
111 return mRowItems;
112 }
113
114 /**
115 * @return whether this list has a header or not.
116 */
117 public boolean hasHeader() {
118 return mHasHeader;
119 }
120
121 /**
122 * @return A slice item of format slice that is hinted to be shown when the slice is in small
123 * format, or is the best option if nothing is appropriately hinted.
124 */
125 private static SliceItem getSummaryItem(@NonNull Slice slice) {
126 List<SliceItem> items = slice.getItems();
127 // See if a summary is specified
128 SliceItem summary = SliceQuery.find(slice, FORMAT_SLICE, SliceHints.HINT_SUMMARY, null);
129 if (summary != null) {
130 return summary;
131 }
132 // Otherwise use the first non-color item and use it if it's a slice
133 SliceItem firstSlice = null;
134 for (int i = 0; i < items.size(); i++) {
135 if (!FORMAT_INT.equals(items.get(i).getFormat())) {
136 firstSlice = items.get(i);
137 break;
138 }
139 }
140 if (firstSlice != null && FORMAT_SLICE.equals(firstSlice.getFormat())) {
141 // Check if this slice is appropriate to use to populate small template
142 if (firstSlice.hasHint(HINT_LIST)) {
143 // Check for header, use that if it exists
144 SliceItem listHeader = SliceQuery.find(firstSlice, FORMAT_SLICE,
145 null,
146 new String[] {
147 HINT_LIST_ITEM, HINT_LIST
148 });
149 if (listHeader != null) {
150 return findFirstSlice(listHeader);
151 } else {
152 // Otherwise use the first list item
153 SliceItem newFirst = firstSlice.getSlice().getItems().get(0);
154 return findFirstSlice(newFirst);
155 }
156 } else {
157 // Not a list, find first slice with non-slice children
158 return findFirstSlice(firstSlice);
159 }
160 }
161 // Fallback, just use this and convert to SliceItem type slice
162 Slice.Builder sb = new Slice.Builder(slice.getUri());
163 Slice s = sb.addSubSlice(slice).build();
164 return s.getItems().get(0);
165 }
166
167 /**
168 * @return Finds the first slice that has non-slice children.
169 */
170 private static SliceItem findFirstSlice(SliceItem slice) {
171 if (!FORMAT_SLICE.equals(slice.getFormat())) {
172 return slice;
173 }
174 List<SliceItem> items = slice.getSlice().getItems();
175 for (int i = 0; i < items.size(); i++) {
176 if (FORMAT_SLICE.equals(items.get(i).getFormat())) {
177 SliceItem childSlice = items.get(i);
178 return findFirstSlice(childSlice);
179 } else {
180 // Doesn't have slice children so return it
181 return slice;
182 }
183 }
184 // Slices all the way down, just return it
185 return slice;
186 }
187}