1 /*
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 
17 package android.app.slice;
18 
19 import android.annotation.NonNull;
20 import android.annotation.StringDef;
21 import android.app.PendingIntent;
22 import android.app.RemoteInput;
23 import android.graphics.drawable.Icon;
24 import android.os.Bundle;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.text.TextUtils;
28 import android.util.Pair;
29 import android.widget.RemoteViews;
30 
31 import com.android.internal.util.ArrayUtils;
32 
33 import java.lang.annotation.Retention;
34 import java.lang.annotation.RetentionPolicy;
35 import java.util.Arrays;
36 import java.util.List;
37 
38 
39 /**
40  * A SliceItem is a single unit in the tree structure of a {@link Slice}.
41  *
42  * A SliceItem a piece of content and some hints about what that content
43  * means or how it should be displayed. The types of content can be:
44  * <li>{@link #FORMAT_SLICE}</li>
45  * <li>{@link #FORMAT_TEXT}</li>
46  * <li>{@link #FORMAT_IMAGE}</li>
47  * <li>{@link #FORMAT_ACTION}</li>
48  * <li>{@link #FORMAT_INT}</li>
49  * <li>{@link #FORMAT_LONG}</li>
50  * <li>{@link #FORMAT_REMOTE_INPUT}</li>
51  * <li>{@link #FORMAT_BUNDLE}</li>
52  *
53  * The hints that a {@link SliceItem} are a set of strings which annotate
54  * the content. The hints that are guaranteed to be understood by the system
55  * are defined on {@link Slice}.
56  * @deprecated Slice framework has been deprecated, it will not receive any updates from
57  *          {@link android.os.Build.VANILLA_ICE_CREAM} and forward. If you are looking for a
58  *          framework that sends displayable data from one app to another, consider using
59  *          {@link android.app.appsearch.AppSearchManager}.
60  */
61 @Deprecated
62 public final class SliceItem implements Parcelable {
63 
64     private static final String TAG = "SliceItem";
65 
66     /**
67      * @hide
68      */
69     @StringDef(prefix = { "FORMAT_" }, value = {
70             FORMAT_SLICE,
71             FORMAT_TEXT,
72             FORMAT_IMAGE,
73             FORMAT_ACTION,
74             FORMAT_INT,
75             FORMAT_LONG,
76             FORMAT_REMOTE_INPUT,
77             FORMAT_BUNDLE,
78     })
79     @Retention(RetentionPolicy.SOURCE)
80     public @interface SliceType {}
81 
82     /**
83      * A {@link SliceItem} that contains a {@link Slice}
84      */
85     public static final String FORMAT_SLICE = "slice";
86     /**
87      * A {@link SliceItem} that contains a {@link CharSequence}
88      */
89     public static final String FORMAT_TEXT = "text";
90     /**
91      * A {@link SliceItem} that contains an {@link Icon}
92      */
93     public static final String FORMAT_IMAGE = "image";
94     /**
95      * A {@link SliceItem} that contains a {@link PendingIntent}
96      *
97      * Note: Actions contain 2 pieces of data, In addition to the pending intent, the
98      * item contains a {@link Slice} that the action applies to.
99      */
100     public static final String FORMAT_ACTION = "action";
101     /**
102      * A {@link SliceItem} that contains an int.
103      */
104     public static final String FORMAT_INT = "int";
105     /**
106      * A {@link SliceItem} that contains a long.
107      */
108     public static final String FORMAT_LONG = "long";
109     /**
110      * A {@link SliceItem} that contains a {@link RemoteInput}.
111      */
112     public static final String FORMAT_REMOTE_INPUT = "input";
113     /**
114      * A {@link SliceItem} that contains a {@link Bundle}.
115      */
116     public static final String FORMAT_BUNDLE = "bundle";
117 
118     /**
119      * @hide
120      */
121     protected @Slice.SliceHint
122     String[] mHints;
123     private final String mFormat;
124     private final String mSubType;
125     private final Object mObj;
126 
127     /**
128      * @hide
129      */
SliceItem(Object obj, @SliceType String format, String subType, List<String> hints)130     public SliceItem(Object obj, @SliceType String format, String subType,
131             List<String> hints) {
132         this(obj, format, subType, hints.toArray(new String[hints.size()]));
133     }
134 
135     /**
136      * @hide
137      */
SliceItem(Object obj, @SliceType String format, String subType, @Slice.SliceHint String[] hints)138     public SliceItem(Object obj, @SliceType String format, String subType,
139             @Slice.SliceHint String[] hints) {
140         mHints = hints;
141         mFormat = format;
142         mSubType = subType;
143         mObj = obj;
144     }
145 
146     /**
147      * @hide
148      */
SliceItem(PendingIntent intent, Slice slice, String format, String subType, @Slice.SliceHint String[] hints)149     public SliceItem(PendingIntent intent, Slice slice, String format, String subType,
150             @Slice.SliceHint String[] hints) {
151         this(new Pair<>(intent, slice), format, subType, hints);
152     }
153 
154     /**
155      * Gets all hints associated with this SliceItem.
156      * @return Array of hints.
157      */
getHints()158     public @NonNull @Slice.SliceHint List<String> getHints() {
159         return Arrays.asList(mHints);
160     }
161 
162     /**
163      * Get the format of this SliceItem.
164      * <p>
165      * The format will be one of the following types supported by the platform:
166      * <li>{@link #FORMAT_SLICE}</li>
167      * <li>{@link #FORMAT_TEXT}</li>
168      * <li>{@link #FORMAT_IMAGE}</li>
169      * <li>{@link #FORMAT_ACTION}</li>
170      * <li>{@link #FORMAT_INT}</li>
171      * <li>{@link #FORMAT_LONG}</li>
172      * <li>{@link #FORMAT_REMOTE_INPUT}</li>
173      * <li>{@link #FORMAT_BUNDLE}</li>
174      * @see #getSubType() ()
175      */
getFormat()176     public String getFormat() {
177         return mFormat;
178     }
179 
180     /**
181      * Get the sub-type of this SliceItem.
182      * <p>
183      * Subtypes provide additional information about the type of this information beyond basic
184      * interpretations inferred by {@link #getFormat()}. For example a slice may contain
185      * many {@link #FORMAT_TEXT} items, but only some of them may be {@link Slice#SUBTYPE_MESSAGE}.
186      * @see #getFormat()
187      */
getSubType()188     public String getSubType() {
189         return mSubType;
190     }
191 
192     /**
193      * @return The text held by this {@link #FORMAT_TEXT} SliceItem
194      */
getText()195     public CharSequence getText() {
196         return (CharSequence) mObj;
197     }
198 
199     /**
200      * @return The parcelable held by this {@link #FORMAT_BUNDLE} SliceItem
201      */
getBundle()202     public Bundle getBundle() {
203         return (Bundle) mObj;
204     }
205 
206     /**
207      * @return The icon held by this {@link #FORMAT_IMAGE} SliceItem
208      */
getIcon()209     public Icon getIcon() {
210         return (Icon) mObj;
211     }
212 
213     /**
214      * @return The pending intent held by this {@link #FORMAT_ACTION} SliceItem
215      */
getAction()216     public PendingIntent getAction() {
217         return ((Pair<PendingIntent, Slice>) mObj).first;
218     }
219 
220     /**
221      * @hide This isn't final
222      */
getRemoteView()223     public RemoteViews getRemoteView() {
224         return (RemoteViews) mObj;
225     }
226 
227     /**
228      * @return The remote input held by this {@link #FORMAT_REMOTE_INPUT} SliceItem
229      */
getRemoteInput()230     public RemoteInput getRemoteInput() {
231         return (RemoteInput) mObj;
232     }
233 
234     /**
235      * @return The color held by this {@link #FORMAT_INT} SliceItem
236      */
getInt()237     public int getInt() {
238         return (Integer) mObj;
239     }
240 
241     /**
242      * @return The slice held by this {@link #FORMAT_ACTION} or {@link #FORMAT_SLICE} SliceItem
243      */
getSlice()244     public Slice getSlice() {
245         if (FORMAT_ACTION.equals(getFormat())) {
246             return ((Pair<PendingIntent, Slice>) mObj).second;
247         }
248         return (Slice) mObj;
249     }
250 
251     /**
252      * @return The long held by this {@link #FORMAT_LONG} SliceItem
253      */
getLong()254     public long getLong() {
255         return (Long) mObj;
256     }
257 
258     /**
259      * @param hint The hint to check for
260      * @return true if this item contains the given hint
261      */
hasHint(@lice.SliceHint String hint)262     public boolean hasHint(@Slice.SliceHint String hint) {
263         return ArrayUtils.contains(mHints, hint);
264     }
265 
266     /**
267      * @hide
268      */
SliceItem(Parcel in)269     public SliceItem(Parcel in) {
270         mHints = in.readStringArray();
271         mFormat = in.readString();
272         mSubType = in.readString();
273         mObj = readObj(mFormat, in);
274     }
275 
276     @Override
describeContents()277     public int describeContents() {
278         return 0;
279     }
280 
281     @Override
writeToParcel(Parcel dest, int flags)282     public void writeToParcel(Parcel dest, int flags) {
283         dest.writeStringArray(mHints);
284         dest.writeString(mFormat);
285         dest.writeString(mSubType);
286         writeObj(dest, flags, mObj, mFormat);
287     }
288 
289     /**
290      * @hide
291      */
hasHints(@lice.SliceHint String[] hints)292     public boolean hasHints(@Slice.SliceHint String[] hints) {
293         if (hints == null) return true;
294         for (String hint : hints) {
295             if (!TextUtils.isEmpty(hint) && !ArrayUtils.contains(mHints, hint)) {
296                 return false;
297             }
298         }
299         return true;
300     }
301 
302     /**
303      * @hide
304      */
hasAnyHints(@lice.SliceHint String[] hints)305     public boolean hasAnyHints(@Slice.SliceHint String[] hints) {
306         if (hints == null) return false;
307         for (String hint : hints) {
308             if (ArrayUtils.contains(mHints, hint)) {
309                 return true;
310             }
311         }
312         return false;
313     }
314 
getBaseType(String type)315     private static String getBaseType(String type) {
316         int index = type.indexOf('/');
317         if (index >= 0) {
318             return type.substring(0, index);
319         }
320         return type;
321     }
322 
writeObj(Parcel dest, int flags, Object obj, String type)323     private static void writeObj(Parcel dest, int flags, Object obj, String type) {
324         switch (getBaseType(type)) {
325             case FORMAT_SLICE:
326             case FORMAT_IMAGE:
327             case FORMAT_REMOTE_INPUT:
328             case FORMAT_BUNDLE:
329                 ((Parcelable) obj).writeToParcel(dest, flags);
330                 break;
331             case FORMAT_ACTION:
332                 ((Pair<PendingIntent, Slice>) obj).first.writeToParcel(dest, flags);
333                 ((Pair<PendingIntent, Slice>) obj).second.writeToParcel(dest, flags);
334                 break;
335             case FORMAT_TEXT:
336                 TextUtils.writeToParcel((CharSequence) obj, dest, flags);
337                 break;
338             case FORMAT_INT:
339                 dest.writeInt((Integer) obj);
340                 break;
341             case FORMAT_LONG:
342                 dest.writeLong((Long) obj);
343                 break;
344         }
345     }
346 
readObj(String type, Parcel in)347     private static Object readObj(String type, Parcel in) {
348         switch (getBaseType(type)) {
349             case FORMAT_SLICE:
350                 return Slice.CREATOR.createFromParcel(in);
351             case FORMAT_TEXT:
352                 return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
353             case FORMAT_IMAGE:
354                 return Icon.CREATOR.createFromParcel(in);
355             case FORMAT_ACTION:
356                 return new Pair<>(
357                         PendingIntent.CREATOR.createFromParcel(in),
358                         Slice.CREATOR.createFromParcel(in));
359             case FORMAT_INT:
360                 return in.readInt();
361             case FORMAT_LONG:
362                 return in.readLong();
363             case FORMAT_REMOTE_INPUT:
364                 return RemoteInput.CREATOR.createFromParcel(in);
365             case FORMAT_BUNDLE:
366                 return Bundle.CREATOR.createFromParcel(in);
367         }
368         throw new RuntimeException("Unsupported type " + type);
369     }
370 
371     public static final @android.annotation.NonNull Creator<SliceItem> CREATOR = new Creator<SliceItem>() {
372         @Override
373         public SliceItem createFromParcel(Parcel in) {
374             return new SliceItem(in);
375         }
376 
377         @Override
378         public SliceItem[] newArray(int size) {
379             return new SliceItem[size];
380         }
381     };
382 }
383