blob: 0e730a5e6993a7a13734018b39fb5a52d4d84b67 [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_TITLE;
Amin Shaikhf2cdc3a2018-01-17 09:35:23 -050020import static android.app.slice.Slice.SUBTYPE_SLIDER;
Mady Mellor6b5cd612017-12-14 11:36:59 -080021import static android.app.slice.SliceItem.FORMAT_ACTION;
22import static android.app.slice.SliceItem.FORMAT_IMAGE;
Amin Shaikhf2cdc3a2018-01-17 09:35:23 -050023import static android.app.slice.SliceItem.FORMAT_INT;
Mady Mellor6b5cd612017-12-14 11:36:59 -080024import static android.app.slice.SliceItem.FORMAT_REMOTE_INPUT;
25import static android.app.slice.SliceItem.FORMAT_SLICE;
26import static android.app.slice.SliceItem.FORMAT_TEXT;
27import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
28
29import android.support.annotation.Nullable;
30import android.support.annotation.RestrictTo;
31import android.util.Log;
32
33import java.util.ArrayList;
34import java.util.List;
35
36import androidx.app.slice.SliceItem;
37import androidx.app.slice.core.SliceHints;
38import androidx.app.slice.core.SliceQuery;
39
40/**
41 * Extracts information required to present content in a row format from a slice.
42 * @hide
43 */
44@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
45public class RowContent {
46 private static final String TAG = "RowContent";
47
48 private SliceItem mContentIntent;
49 private SliceItem mStartItem;
50 private SliceItem mTitleItem;
51 private SliceItem mSubtitleItem;
Mady Mellor6b5cd612017-12-14 11:36:59 -080052 private ArrayList<SliceItem> mEndItems = new ArrayList<>();
Amin Shaikhbfeddba2018-01-10 14:51:13 -050053 private boolean mEndItemsContainAction;
Amin Shaikhf2cdc3a2018-01-17 09:35:23 -050054 private SliceItem mSlider;
Mady Mellor6b5cd612017-12-14 11:36:59 -080055
56 public RowContent(SliceItem rowSlice, boolean showStartItem) {
57 populate(rowSlice, showStartItem);
58 }
59
60 /**
Mady Mellor32206132017-12-21 22:22:26 -080061 * Resets the content.
62 */
63 public void reset() {
64 mContentIntent = null;
65 mStartItem = null;
66 mTitleItem = null;
67 mSubtitleItem = null;
Mady Mellor32206132017-12-21 22:22:26 -080068 mEndItems.clear();
69 }
70
71 /**
Mady Mellor6b5cd612017-12-14 11:36:59 -080072 * @return whether this row has content that is valid to display.
73 */
74 public boolean populate(SliceItem rowSlice, boolean showStartItem) {
Mady Mellor32206132017-12-21 22:22:26 -080075 reset();
Mady Mellor6b5cd612017-12-14 11:36:59 -080076 if (!isValidRow(rowSlice)) {
77 Log.w(TAG, "Provided SliceItem is invalid for RowContent");
78 return false;
79 }
80 // Filter anything not viable for displaying in a row
Amin Shaikhf2cdc3a2018-01-17 09:35:23 -050081 ArrayList<SliceItem> rowItems = filterInvalidItems(rowSlice);
Mady Mellor6b5cd612017-12-14 11:36:59 -080082 // If we've only got one item that's a slice / action use those items instead
83 if (rowItems.size() == 1 && (FORMAT_ACTION.equals(rowItems.get(0).getFormat())
84 || FORMAT_SLICE.equals(rowItems.get(0).getFormat()))) {
85 if (isValidRow(rowItems.get(0))) {
86 rowSlice = rowItems.get(0);
Amin Shaikhf2cdc3a2018-01-17 09:35:23 -050087 rowItems = filterInvalidItems(rowSlice);
Mady Mellor6b5cd612017-12-14 11:36:59 -080088 }
89 }
90 // Content intent
91 if (FORMAT_ACTION.equals(rowSlice.getFormat())) {
92 mContentIntent = rowSlice;
93 }
Amin Shaikhf2cdc3a2018-01-17 09:35:23 -050094 if (SUBTYPE_SLIDER.equals(rowSlice.getSubType())) {
95 mSlider = rowSlice;
96 }
Mady Mellor6b5cd612017-12-14 11:36:59 -080097 if (rowItems.size() > 0) {
98 // Start item
Mady Mellor238b9b62018-01-09 16:15:40 -080099 if (isStartType(rowItems.get(0))) {
100 if (showStartItem) {
101 mStartItem = rowItems.get(0);
102 }
103 rowItems.remove(0);
Mady Mellor6b5cd612017-12-14 11:36:59 -0800104 }
105 // Text + end items
Mady Mellor238b9b62018-01-09 16:15:40 -0800106 ArrayList<SliceItem> endItems = new ArrayList<>();
Mady Mellor6b5cd612017-12-14 11:36:59 -0800107 for (int i = 0; i < rowItems.size(); i++) {
108 final SliceItem item = rowItems.get(i);
109 if (FORMAT_TEXT.equals(item.getFormat())) {
110 if ((mTitleItem == null || !mTitleItem.hasHint(HINT_TITLE))
111 && item.hasHint(HINT_TITLE)) {
112 mTitleItem = item;
113 } else if (mSubtitleItem == null) {
114 mSubtitleItem = item;
115 }
116 } else {
Mady Mellor238b9b62018-01-09 16:15:40 -0800117 endItems.add(item);
118 }
119 }
120 // Special rules for end items: only one timestamp, can't be mixture of icons / actions
121 boolean hasTimestamp = mStartItem != null
122 && FORMAT_TIMESTAMP.equals(mStartItem.getFormat());
123 String desiredFormat = null;
124 for (int i = 0; i < endItems.size(); i++) {
125 final SliceItem item = endItems.get(i);
126 if (FORMAT_TIMESTAMP.equals(item.getFormat())) {
127 if (!hasTimestamp) {
128 hasTimestamp = true;
129 mEndItems.add(item);
130 }
131 } else if (desiredFormat == null) {
132 desiredFormat = item.getFormat();
133 mEndItems.add(item);
134 } else if (desiredFormat.equals(item.getFormat())) {
Mady Mellor6b5cd612017-12-14 11:36:59 -0800135 mEndItems.add(item);
Amin Shaikhbfeddba2018-01-10 14:51:13 -0500136 mEndItemsContainAction |= FORMAT_ACTION.equals(item.getFormat());
Mady Mellor6b5cd612017-12-14 11:36:59 -0800137 }
138 }
139 }
Mady Mellor6b5cd612017-12-14 11:36:59 -0800140 return isValid();
141 }
142
Mady Mellor6b5cd612017-12-14 11:36:59 -0800143 /**
Amin Shaikhf2cdc3a2018-01-17 09:35:23 -0500144 * @return the {@link SliceItem} representing the slider in this row; can be null
145 */
146 @Nullable
147 public SliceItem getSlider() {
148 return mSlider;
149 }
150
151 /**
Mady Mellor6b5cd612017-12-14 11:36:59 -0800152 * @return whether this row has content that is valid to display.
153 */
154 public boolean isValid() {
155 return mStartItem != null
156 || mTitleItem != null
157 || mSubtitleItem != null
158 || mEndItems.size() > 0;
159 }
160
161 @Nullable
162 public SliceItem getContentIntent() {
163 return mContentIntent;
164 }
165
166 @Nullable
167 public SliceItem getStartItem() {
168 return mStartItem;
169 }
170
171 @Nullable
172 public SliceItem getTitleItem() {
173 return mTitleItem;
174 }
175
176 @Nullable
177 public SliceItem getSubtitleItem() {
178 return mSubtitleItem;
179 }
180
Mady Mellor6b5cd612017-12-14 11:36:59 -0800181 public ArrayList<SliceItem> getEndItems() {
182 return mEndItems;
183 }
184
185 /**
Amin Shaikhbfeddba2018-01-10 14:51:13 -0500186 * @return whether {@link #getEndItems()} contains a SliceItem with FORMAT_ACTION
187 */
188 public boolean endItemsContainAction() {
189 return mEndItemsContainAction;
190 }
191
192 /**
Mady Mellor6b5cd612017-12-14 11:36:59 -0800193 * @return whether this is a valid item to use to populate a row of content.
194 */
Amin Shaikhf2cdc3a2018-01-17 09:35:23 -0500195 private static boolean isValidRow(SliceItem rowSlice) {
Mady Mellor6b5cd612017-12-14 11:36:59 -0800196 // Must be slice or action
Amin Shaikhf2cdc3a2018-01-17 09:35:23 -0500197 if (FORMAT_SLICE.equals(rowSlice.getFormat())
198 || FORMAT_ACTION.equals(rowSlice.getFormat())) {
Mady Mellor6b5cd612017-12-14 11:36:59 -0800199 // Must have at least one legitimate child
Amin Shaikhf2cdc3a2018-01-17 09:35:23 -0500200 List<SliceItem> rowItems = rowSlice.getSlice().getItems();
Mady Mellor6b5cd612017-12-14 11:36:59 -0800201 for (int i = 0; i < rowItems.size(); i++) {
Amin Shaikhf2cdc3a2018-01-17 09:35:23 -0500202 if (isValidRowContent(rowSlice, rowItems.get(i))) {
Mady Mellor6b5cd612017-12-14 11:36:59 -0800203 return true;
204 }
205 }
206 }
Mady Mellor6b5cd612017-12-14 11:36:59 -0800207 return false;
208 }
209
Amin Shaikhf2cdc3a2018-01-17 09:35:23 -0500210 private static ArrayList<SliceItem> filterInvalidItems(SliceItem rowSlice) {
Mady Mellor6b5cd612017-12-14 11:36:59 -0800211 ArrayList<SliceItem> filteredList = new ArrayList<>();
Amin Shaikhf2cdc3a2018-01-17 09:35:23 -0500212 for (SliceItem i : rowSlice.getSlice().getItems()) {
213 if (isValidRowContent(rowSlice, i)) {
Mady Mellor6b5cd612017-12-14 11:36:59 -0800214 filteredList.add(i);
215 }
216 }
217 return filteredList;
218 }
219
220 /**
221 * @return whether this item has valid content to display in a row.
222 */
Amin Shaikhf2cdc3a2018-01-17 09:35:23 -0500223 private static boolean isValidRowContent(SliceItem slice, SliceItem item) {
Mady Mellor6b5cd612017-12-14 11:36:59 -0800224 // TODO -- filter for shortcut once that's in
225 final String itemFormat = item.getFormat();
226 // Must be a format that is presentable
227 return FORMAT_TEXT.equals(itemFormat)
228 || FORMAT_IMAGE.equals(itemFormat)
229 || FORMAT_TIMESTAMP.equals(itemFormat)
230 || FORMAT_REMOTE_INPUT.equals(itemFormat)
Amin Shaikhf2cdc3a2018-01-17 09:35:23 -0500231 || FORMAT_ACTION.equals(itemFormat)
232 || (FORMAT_INT.equals(itemFormat) && SUBTYPE_SLIDER.equals(slice.getSubType()));
Mady Mellor6b5cd612017-12-14 11:36:59 -0800233 }
234
235 /**
236 * @return Whether this item is appropriate to be considered a "start" item, i.e. go in the
237 * front slot of a row.
238 */
239 private static boolean isStartType(SliceItem item) {
240 final String type = item.getFormat();
241 return (!item.hasHint(SliceHints.SUBTYPE_TOGGLE)
Mady Mellor6b5cd612017-12-14 11:36:59 -0800242 && (FORMAT_ACTION.equals(type) && (SliceQuery.find(item, FORMAT_IMAGE) != null)))
243 || FORMAT_IMAGE.equals(type)
244 || FORMAT_TIMESTAMP.equals(type);
245 }
246}