blob: e7d2729478fa59a8d9affeefccb2b30efff0f644 [file] [log] [blame]
Jason Monkdcb5e2f2017-11-15 20:19:43 -05001/*
2 * Copyright (C) 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;
18
19import static android.app.slice.SliceItem.FORMAT_ACTION;
Jason Monkdcb5e2f2017-11-15 20:19:43 -050020import static android.app.slice.SliceItem.FORMAT_IMAGE;
Jason Monk98ae4f82017-12-18 11:29:07 -050021import static android.app.slice.SliceItem.FORMAT_INT;
Jason Monkdcb5e2f2017-11-15 20:19:43 -050022import static android.app.slice.SliceItem.FORMAT_REMOTE_INPUT;
23import static android.app.slice.SliceItem.FORMAT_SLICE;
24import static android.app.slice.SliceItem.FORMAT_TEXT;
25import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
26
27import android.app.PendingIntent;
28import android.app.RemoteInput;
29import android.graphics.drawable.Icon;
30import android.os.Bundle;
31import android.os.Parcelable;
32import android.support.annotation.NonNull;
Jason Monk2a7d0fc2017-11-15 10:10:24 -050033import android.support.annotation.RequiresApi;
Jason Monkdcb5e2f2017-11-15 20:19:43 -050034import android.support.annotation.RestrictTo;
35import android.support.annotation.RestrictTo.Scope;
36import android.support.annotation.StringDef;
37import android.text.TextUtils;
38import android.util.Pair;
39
40import java.util.Arrays;
41import java.util.List;
42
43
44/**
45 * A SliceItem is a single unit in the tree structure of a {@link Slice}.
46 * <p>
47 * A SliceItem a piece of content and some hints about what that content
48 * means or how it should be displayed. The types of content can be:
49 * <li>{@link android.app.slice.SliceItem#FORMAT_SLICE}</li>
50 * <li>{@link android.app.slice.SliceItem#FORMAT_TEXT}</li>
51 * <li>{@link android.app.slice.SliceItem#FORMAT_IMAGE}</li>
52 * <li>{@link android.app.slice.SliceItem#FORMAT_ACTION}</li>
Jason Monk98ae4f82017-12-18 11:29:07 -050053 * <li>{@link android.app.slice.SliceItem#FORMAT_INT}</li>
Jason Monkdcb5e2f2017-11-15 20:19:43 -050054 * <li>{@link android.app.slice.SliceItem#FORMAT_TIMESTAMP}</li>
Jason Monkdcb5e2f2017-11-15 20:19:43 -050055 * <p>
56 * The hints that a {@link SliceItem} are a set of strings which annotate
57 * the content. The hints that are guaranteed to be understood by the system
58 * are defined on {@link Slice}.
59 */
60public class SliceItem {
61
62 private static final String HINTS = "hints";
63 private static final String FORMAT = "format";
64 private static final String SUBTYPE = "subtype";
65 private static final String OBJ = "obj";
66 private static final String OBJ_2 = "obj_2";
67
68 /**
69 * @hide
70 */
71 @RestrictTo(Scope.LIBRARY)
Jason Monk98ae4f82017-12-18 11:29:07 -050072 @StringDef({FORMAT_SLICE, FORMAT_TEXT, FORMAT_IMAGE, FORMAT_ACTION, FORMAT_INT,
Jason Monkdcb5e2f2017-11-15 20:19:43 -050073 FORMAT_TIMESTAMP, FORMAT_REMOTE_INPUT})
74 public @interface SliceType {
75 }
76
77 /**
78 * @hide
79 */
80 @RestrictTo(Scope.LIBRARY)
81 protected @Slice.SliceHint String[] mHints;
82 private final String mFormat;
83 private final String mSubType;
84 private final Object mObj;
85
86 /**
87 * @hide
88 */
89 @RestrictTo(Scope.LIBRARY)
90 public SliceItem(Object obj, @SliceType String format, String subType,
91 @Slice.SliceHint String[] hints) {
92 mHints = hints;
93 mFormat = format;
94 mSubType = subType;
95 mObj = obj;
96 }
97
98 /**
99 * @hide
100 */
101 @RestrictTo(Scope.LIBRARY)
Mady Mellor238b9b62018-01-09 16:15:40 -0800102 public SliceItem(Object obj, @SliceType String format, String subType,
103 @Slice.SliceHint List<String> hints) {
104 this (obj, format, subType, hints.toArray(new String[hints.size()]));
105 }
106
107 /**
108 * @hide
109 */
110 @RestrictTo(Scope.LIBRARY)
Jason Monkdcb5e2f2017-11-15 20:19:43 -0500111 public SliceItem(PendingIntent intent, Slice slice, String format, String subType,
112 @Slice.SliceHint String[] hints) {
113 this(new Pair<>(intent, slice), format, subType, hints);
114 }
115
116 /**
117 * Gets all hints associated with this SliceItem.
118 *
119 * @return Array of hints.
120 */
121 public @NonNull @Slice.SliceHint List<String> getHints() {
122 return Arrays.asList(mHints);
123 }
124
125 /**
126 * @hide
127 */
128 @RestrictTo(Scope.LIBRARY)
129 public void addHint(@Slice.SliceHint String hint) {
130 mHints = ArrayUtils.appendElement(String.class, mHints, hint);
131 }
132
133 /**
134 * @hide
135 */
136 @RestrictTo(Scope.LIBRARY)
137 public void removeHint(String hint) {
138 ArrayUtils.removeElement(String.class, mHints, hint);
139 }
140
141 /**
142 * Get the format of this SliceItem.
143 * <p>
144 * The format will be one of the following types supported by the platform:
145 * <li>{@link android.app.slice.SliceItem#FORMAT_SLICE}</li>
146 * <li>{@link android.app.slice.SliceItem#FORMAT_TEXT}</li>
147 * <li>{@link android.app.slice.SliceItem#FORMAT_IMAGE}</li>
148 * <li>{@link android.app.slice.SliceItem#FORMAT_ACTION}</li>
Jason Monk98ae4f82017-12-18 11:29:07 -0500149 * <li>{@link android.app.slice.SliceItem#FORMAT_INT}</li>
Jason Monkdcb5e2f2017-11-15 20:19:43 -0500150 * <li>{@link android.app.slice.SliceItem#FORMAT_TIMESTAMP}</li>
151 * <li>{@link android.app.slice.SliceItem#FORMAT_REMOTE_INPUT}</li>
152 * @see #getSubType() ()
153 */
154 public @SliceType String getFormat() {
155 return mFormat;
156 }
157
158 /**
159 * Get the sub-type of this SliceItem.
160 * <p>
161 * Subtypes provide additional information about the type of this information beyond basic
162 * interpretations inferred by {@link #getFormat()}. For example a slice may contain
163 * many {@link android.app.slice.SliceItem#FORMAT_TEXT} items, but only some of them may be
164 * {@link android.app.slice.Slice#SUBTYPE_MESSAGE}.
165 * @see #getFormat()
166 */
167 public String getSubType() {
168 return mSubType;
169 }
170
171 /**
172 * @return The text held by this {@link android.app.slice.SliceItem#FORMAT_TEXT} SliceItem
173 */
174 public CharSequence getText() {
175 return (CharSequence) mObj;
176 }
177
178 /**
179 * @return The icon held by this {@link android.app.slice.SliceItem#FORMAT_IMAGE} SliceItem
180 */
Jason Monk2a7d0fc2017-11-15 10:10:24 -0500181 @RequiresApi(23)
Jason Monkdcb5e2f2017-11-15 20:19:43 -0500182 public Icon getIcon() {
183 return (Icon) mObj;
184 }
185
186 /**
187 * @return The pending intent held by this {@link android.app.slice.SliceItem#FORMAT_ACTION}
188 * SliceItem
189 */
190 public PendingIntent getAction() {
191 return ((Pair<PendingIntent, Slice>) mObj).first;
192 }
193
194 /**
195 * @return The remote input held by this {@link android.app.slice.SliceItem#FORMAT_REMOTE_INPUT}
196 * SliceItem
Mady Mellorc7791012018-01-16 17:25:01 -0800197 * @hide
Jason Monkdcb5e2f2017-11-15 20:19:43 -0500198 */
Jason Monk2a7d0fc2017-11-15 10:10:24 -0500199 @RequiresApi(20)
Mady Mellorc7791012018-01-16 17:25:01 -0800200 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
Jason Monkdcb5e2f2017-11-15 20:19:43 -0500201 public RemoteInput getRemoteInput() {
202 return (RemoteInput) mObj;
203 }
204
205 /**
Jason Monk98ae4f82017-12-18 11:29:07 -0500206 * @return The color held by this {@link android.app.slice.SliceItem#FORMAT_INT} SliceItem
Jason Monkdcb5e2f2017-11-15 20:19:43 -0500207 */
Jason Monk98ae4f82017-12-18 11:29:07 -0500208 public int getInt() {
Jason Monkdcb5e2f2017-11-15 20:19:43 -0500209 return (Integer) mObj;
210 }
211
212 /**
213 * @return The slice held by this {@link android.app.slice.SliceItem#FORMAT_ACTION} or
214 * {@link android.app.slice.SliceItem#FORMAT_SLICE} SliceItem
215 */
216 public Slice getSlice() {
217 if (FORMAT_ACTION.equals(getFormat())) {
218 return ((Pair<PendingIntent, Slice>) mObj).second;
219 }
220 return (Slice) mObj;
221 }
222
223 /**
224 * @return The timestamp held by this {@link android.app.slice.SliceItem#FORMAT_TIMESTAMP}
225 * SliceItem
226 */
227 public long getTimestamp() {
228 return (Long) mObj;
229 }
230
231 /**
232 * @param hint The hint to check for
233 * @return true if this item contains the given hint
234 */
235 public boolean hasHint(@Slice.SliceHint String hint) {
236 return ArrayUtils.contains(mHints, hint);
237 }
238
239 /**
240 * @hide
241 */
242 @RestrictTo(Scope.LIBRARY)
243 public SliceItem(Bundle in) {
244 mHints = in.getStringArray(HINTS);
245 mFormat = in.getString(FORMAT);
246 mSubType = in.getString(SUBTYPE);
247 mObj = readObj(mFormat, in);
248 }
249
250 /**
251 * @hide
252 * @return
253 */
254 @RestrictTo(Scope.LIBRARY)
255 public Bundle toBundle() {
256 Bundle b = new Bundle();
257 b.putStringArray(HINTS, mHints);
258 b.putString(FORMAT, mFormat);
259 b.putString(SUBTYPE, mSubType);
260 writeObj(b, mObj, mFormat);
261 return b;
262 }
263
264 /**
265 * @hide
266 */
267 @RestrictTo(Scope.LIBRARY)
268 public boolean hasHints(@Slice.SliceHint String[] hints) {
269 if (hints == null) return true;
270 for (String hint : hints) {
271 if (!TextUtils.isEmpty(hint) && !ArrayUtils.contains(mHints, hint)) {
272 return false;
273 }
274 }
275 return true;
276 }
277
278 /**
279 * @hide
280 */
281 @RestrictTo(Scope.LIBRARY)
Mady Mellor71ef84d2017-12-11 13:33:36 -0800282 public boolean hasAnyHints(@Slice.SliceHint String... hints) {
Jason Monkdcb5e2f2017-11-15 20:19:43 -0500283 if (hints == null) return false;
284 for (String hint : hints) {
285 if (ArrayUtils.contains(mHints, hint)) {
286 return true;
287 }
288 }
289 return false;
290 }
291
292 private void writeObj(Bundle dest, Object obj, String type) {
293 switch (type) {
294 case FORMAT_IMAGE:
295 case FORMAT_REMOTE_INPUT:
296 dest.putParcelable(OBJ, (Parcelable) obj);
297 break;
298 case FORMAT_SLICE:
299 dest.putParcelable(OBJ, ((Slice) obj).toBundle());
300 break;
301 case FORMAT_ACTION:
302 dest.putParcelable(OBJ, ((Pair<PendingIntent, Slice>) obj).first);
303 dest.putBundle(OBJ_2, ((Pair<PendingIntent, Slice>) obj).second.toBundle());
304 break;
305 case FORMAT_TEXT:
306 dest.putCharSequence(OBJ, (CharSequence) obj);
307 break;
Jason Monk98ae4f82017-12-18 11:29:07 -0500308 case FORMAT_INT:
Jason Monkdcb5e2f2017-11-15 20:19:43 -0500309 dest.putInt(OBJ, (Integer) mObj);
310 break;
311 case FORMAT_TIMESTAMP:
312 dest.putLong(OBJ, (Long) mObj);
313 break;
314 }
315 }
316
317 private static Object readObj(String type, Bundle in) {
318 switch (type) {
319 case FORMAT_IMAGE:
320 case FORMAT_REMOTE_INPUT:
321 return in.getParcelable(OBJ);
322 case FORMAT_SLICE:
Jason Monk2a7d0fc2017-11-15 10:10:24 -0500323 return new Slice(in.getBundle(OBJ));
Jason Monkdcb5e2f2017-11-15 20:19:43 -0500324 case FORMAT_TEXT:
325 return in.getCharSequence(OBJ);
326 case FORMAT_ACTION:
Jason Monk2a7d0fc2017-11-15 10:10:24 -0500327 return new Pair<>(
328 (PendingIntent) in.getParcelable(OBJ),
Jason Monkdcb5e2f2017-11-15 20:19:43 -0500329 new Slice(in.getBundle(OBJ_2)));
Jason Monk98ae4f82017-12-18 11:29:07 -0500330 case FORMAT_INT:
Jason Monkdcb5e2f2017-11-15 20:19:43 -0500331 return in.getInt(OBJ);
332 case FORMAT_TIMESTAMP:
333 return in.getLong(OBJ);
334 }
335 throw new RuntimeException("Unsupported type " + type);
336 }
337
338 /**
339 * @hide
340 */
341 @RestrictTo(Scope.LIBRARY)
342 public static String typeToString(String format) {
343 switch (format) {
344 case FORMAT_SLICE:
345 return "Slice";
346 case FORMAT_TEXT:
347 return "Text";
348 case FORMAT_IMAGE:
349 return "Image";
350 case FORMAT_ACTION:
351 return "Action";
Jason Monk98ae4f82017-12-18 11:29:07 -0500352 case FORMAT_INT:
353 return "Int";
Jason Monkdcb5e2f2017-11-15 20:19:43 -0500354 case FORMAT_TIMESTAMP:
355 return "Timestamp";
356 case FORMAT_REMOTE_INPUT:
357 return "RemoteInput";
358 }
359 return "Unrecognized format: " + format;
360 }
Mady Mellor6b5cd612017-12-14 11:36:59 -0800361
362 /**
363 * @hide
364 * @return A string representation of this slice item.
365 */
366 @RestrictTo(Scope.LIBRARY)
367 @Override
368 public String toString() {
369 return toString("");
370 }
371
372 private String toString(String indent) {
373 StringBuilder sb = new StringBuilder();
374 sb.append(indent);
375 if (FORMAT_SLICE.equals(mFormat)) {
376 sb.append("slice:\n");
377 sb.append(getSlice().toString(indent + " "));
378 } else if (FORMAT_ACTION.equals(mFormat)) {
379 sb.append("action:\n");
380 sb.append(getSlice().toString(indent + " "));
381 } else if (FORMAT_TEXT.equals(mFormat)) {
382 sb.append("text: ");
383 sb.append(getText());
384 sb.append("\n");
385 } else {
386 sb.append(SliceItem.typeToString(getFormat()));
387 sb.append("\n");
388 }
389 return sb.toString();
390 }
Jason Monkdcb5e2f2017-11-15 20:19:43 -0500391}