SliceItem.java revision 106387f0b283beeb5318f05e2033f15798e274b3
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
17package android.app.slice;
18
19import android.annotation.NonNull;
20import android.annotation.StringDef;
21import android.app.PendingIntent;
22import android.app.RemoteInput;
23import android.graphics.drawable.Icon;
24import android.os.Bundle;
25import android.os.Parcel;
26import android.os.Parcelable;
27import android.text.TextUtils;
28import android.util.Pair;
29import android.widget.RemoteViews;
30
31import com.android.internal.util.ArrayUtils;
32
33import java.lang.annotation.Retention;
34import java.lang.annotation.RetentionPolicy;
35import java.util.Arrays;
36import 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_TIMESTAMP}</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 */
57public final class SliceItem implements Parcelable {
58
59    private static final String TAG = "SliceItem";
60
61    /**
62     * @hide
63     */
64    @StringDef(prefix = { "FORMAT_" }, value = {
65            FORMAT_SLICE,
66            FORMAT_TEXT,
67            FORMAT_IMAGE,
68            FORMAT_ACTION,
69            FORMAT_INT,
70            FORMAT_LONG,
71            FORMAT_REMOTE_INPUT,
72            FORMAT_BUNDLE,
73    })
74    @Retention(RetentionPolicy.SOURCE)
75    public @interface SliceType {}
76
77    /**
78     * A {@link SliceItem} that contains a {@link Slice}
79     */
80    public static final String FORMAT_SLICE = "slice";
81    /**
82     * A {@link SliceItem} that contains a {@link CharSequence}
83     */
84    public static final String FORMAT_TEXT = "text";
85    /**
86     * A {@link SliceItem} that contains an {@link Icon}
87     */
88    public static final String FORMAT_IMAGE = "image";
89    /**
90     * A {@link SliceItem} that contains a {@link PendingIntent}
91     *
92     * Note: Actions contain 2 pieces of data, In addition to the pending intent, the
93     * item contains a {@link Slice} that the action applies to.
94     */
95    public static final String FORMAT_ACTION = "action";
96    /**
97     * A {@link SliceItem} that contains an int.
98     */
99    public static final String FORMAT_INT = "int";
100    /**
101     * A {@link SliceItem} that contains a long.
102     */
103    public static final String FORMAT_LONG = "long";
104    /**
105     * @deprecated TO BE REMOVED
106     */
107    @Deprecated
108    public static final String FORMAT_TIMESTAMP = FORMAT_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     */
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     */
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     */
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     */
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_TIMESTAMP}</li>
172     * <li>{@link #FORMAT_REMOTE_INPUT}</li>
173     * <li>{@link #FORMAT_BUNDLE}</li>
174     * @see #getSubType() ()
175     */
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     */
188    public String getSubType() {
189        return mSubType;
190    }
191
192    /**
193     * @return The text held by this {@link #FORMAT_TEXT} SliceItem
194     */
195    public CharSequence getText() {
196        return (CharSequence) mObj;
197    }
198
199    /**
200     * @return The parcelable held by this {@link #FORMAT_BUNDLE} SliceItem
201     */
202    public Bundle getBundle() {
203        return (Bundle) mObj;
204    }
205
206    /**
207     * @return The icon held by this {@link #FORMAT_IMAGE} SliceItem
208     */
209    public Icon getIcon() {
210        return (Icon) mObj;
211    }
212
213    /**
214     * @return The pending intent held by this {@link #FORMAT_ACTION} SliceItem
215     */
216    public PendingIntent getAction() {
217        return ((Pair<PendingIntent, Slice>) mObj).first;
218    }
219
220    /**
221     * @hide This isn't final
222     */
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     */
230    public RemoteInput getRemoteInput() {
231        return (RemoteInput) mObj;
232    }
233
234    /**
235     * @return The color held by this {@link #FORMAT_INT} SliceItem
236     */
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     */
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 timestamp held by this {@link #FORMAT_TIMESTAMP} SliceItem
253     */
254    public long getTimestamp() {
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     */
262    public boolean hasHint(@Slice.SliceHint String hint) {
263        return ArrayUtils.contains(mHints, hint);
264    }
265
266    /**
267     * @hide
268     */
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
277    public int describeContents() {
278        return 0;
279    }
280
281    @Override
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     */
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     */
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
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
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_TIMESTAMP:
342                dest.writeLong((Long) obj);
343                break;
344        }
345    }
346
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_TIMESTAMP:
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 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