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 */ 57 public 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 * @removed 107 */ 108 @Deprecated 109 public static final String FORMAT_TIMESTAMP = FORMAT_LONG; 110 /** 111 * A {@link SliceItem} that contains a {@link RemoteInput}. 112 */ 113 public static final String FORMAT_REMOTE_INPUT = "input"; 114 /** 115 * A {@link SliceItem} that contains a {@link Bundle}. 116 */ 117 public static final String FORMAT_BUNDLE = "bundle"; 118 119 /** 120 * @hide 121 */ 122 protected @Slice.SliceHint 123 String[] mHints; 124 private final String mFormat; 125 private final String mSubType; 126 private final Object mObj; 127 128 /** 129 * @hide 130 */ SliceItem(Object obj, @SliceType String format, String subType, List<String> hints)131 public SliceItem(Object obj, @SliceType String format, String subType, 132 List<String> hints) { 133 this(obj, format, subType, hints.toArray(new String[hints.size()])); 134 } 135 136 /** 137 * @hide 138 */ SliceItem(Object obj, @SliceType String format, String subType, @Slice.SliceHint String[] hints)139 public SliceItem(Object obj, @SliceType String format, String subType, 140 @Slice.SliceHint String[] hints) { 141 mHints = hints; 142 mFormat = format; 143 mSubType = subType; 144 mObj = obj; 145 } 146 147 /** 148 * @hide 149 */ SliceItem(PendingIntent intent, Slice slice, String format, String subType, @Slice.SliceHint String[] hints)150 public SliceItem(PendingIntent intent, Slice slice, String format, String subType, 151 @Slice.SliceHint String[] hints) { 152 this(new Pair<>(intent, slice), format, subType, hints); 153 } 154 155 /** 156 * Gets all hints associated with this SliceItem. 157 * @return Array of hints. 158 */ getHints()159 public @NonNull @Slice.SliceHint List<String> getHints() { 160 return Arrays.asList(mHints); 161 } 162 163 /** 164 * Get the format of this SliceItem. 165 * <p> 166 * The format will be one of the following types supported by the platform: 167 * <li>{@link #FORMAT_SLICE}</li> 168 * <li>{@link #FORMAT_TEXT}</li> 169 * <li>{@link #FORMAT_IMAGE}</li> 170 * <li>{@link #FORMAT_ACTION}</li> 171 * <li>{@link #FORMAT_INT}</li> 172 * <li>{@link #FORMAT_LONG}</li> 173 * <li>{@link #FORMAT_REMOTE_INPUT}</li> 174 * <li>{@link #FORMAT_BUNDLE}</li> 175 * @see #getSubType() () 176 */ getFormat()177 public String getFormat() { 178 return mFormat; 179 } 180 181 /** 182 * Get the sub-type of this SliceItem. 183 * <p> 184 * Subtypes provide additional information about the type of this information beyond basic 185 * interpretations inferred by {@link #getFormat()}. For example a slice may contain 186 * many {@link #FORMAT_TEXT} items, but only some of them may be {@link Slice#SUBTYPE_MESSAGE}. 187 * @see #getFormat() 188 */ getSubType()189 public String getSubType() { 190 return mSubType; 191 } 192 193 /** 194 * @return The text held by this {@link #FORMAT_TEXT} SliceItem 195 */ getText()196 public CharSequence getText() { 197 return (CharSequence) mObj; 198 } 199 200 /** 201 * @return The parcelable held by this {@link #FORMAT_BUNDLE} SliceItem 202 */ getBundle()203 public Bundle getBundle() { 204 return (Bundle) mObj; 205 } 206 207 /** 208 * @return The icon held by this {@link #FORMAT_IMAGE} SliceItem 209 */ getIcon()210 public Icon getIcon() { 211 return (Icon) mObj; 212 } 213 214 /** 215 * @return The pending intent held by this {@link #FORMAT_ACTION} SliceItem 216 */ getAction()217 public PendingIntent getAction() { 218 return ((Pair<PendingIntent, Slice>) mObj).first; 219 } 220 221 /** 222 * @hide This isn't final 223 */ getRemoteView()224 public RemoteViews getRemoteView() { 225 return (RemoteViews) mObj; 226 } 227 228 /** 229 * @return The remote input held by this {@link #FORMAT_REMOTE_INPUT} SliceItem 230 */ getRemoteInput()231 public RemoteInput getRemoteInput() { 232 return (RemoteInput) mObj; 233 } 234 235 /** 236 * @return The color held by this {@link #FORMAT_INT} SliceItem 237 */ getInt()238 public int getInt() { 239 return (Integer) mObj; 240 } 241 242 /** 243 * @return The slice held by this {@link #FORMAT_ACTION} or {@link #FORMAT_SLICE} SliceItem 244 */ getSlice()245 public Slice getSlice() { 246 if (FORMAT_ACTION.equals(getFormat())) { 247 return ((Pair<PendingIntent, Slice>) mObj).second; 248 } 249 return (Slice) mObj; 250 } 251 252 /** 253 * @return The long held by this {@link #FORMAT_LONG} SliceItem 254 */ getLong()255 public long getLong() { 256 return (Long) mObj; 257 } 258 259 /** 260 * @deprecated replaced by {@link #getLong()} 261 * @removed 262 */ 263 @Deprecated getTimestamp()264 public long getTimestamp() { 265 return (Long) mObj; 266 } 267 268 /** 269 * @param hint The hint to check for 270 * @return true if this item contains the given hint 271 */ hasHint(@lice.SliceHint String hint)272 public boolean hasHint(@Slice.SliceHint String hint) { 273 return ArrayUtils.contains(mHints, hint); 274 } 275 276 /** 277 * @hide 278 */ SliceItem(Parcel in)279 public SliceItem(Parcel in) { 280 mHints = in.readStringArray(); 281 mFormat = in.readString(); 282 mSubType = in.readString(); 283 mObj = readObj(mFormat, in); 284 } 285 286 @Override describeContents()287 public int describeContents() { 288 return 0; 289 } 290 291 @Override writeToParcel(Parcel dest, int flags)292 public void writeToParcel(Parcel dest, int flags) { 293 dest.writeStringArray(mHints); 294 dest.writeString(mFormat); 295 dest.writeString(mSubType); 296 writeObj(dest, flags, mObj, mFormat); 297 } 298 299 /** 300 * @hide 301 */ hasHints(@lice.SliceHint String[] hints)302 public boolean hasHints(@Slice.SliceHint String[] hints) { 303 if (hints == null) return true; 304 for (String hint : hints) { 305 if (!TextUtils.isEmpty(hint) && !ArrayUtils.contains(mHints, hint)) { 306 return false; 307 } 308 } 309 return true; 310 } 311 312 /** 313 * @hide 314 */ hasAnyHints(@lice.SliceHint String[] hints)315 public boolean hasAnyHints(@Slice.SliceHint String[] hints) { 316 if (hints == null) return false; 317 for (String hint : hints) { 318 if (ArrayUtils.contains(mHints, hint)) { 319 return true; 320 } 321 } 322 return false; 323 } 324 getBaseType(String type)325 private static String getBaseType(String type) { 326 int index = type.indexOf('/'); 327 if (index >= 0) { 328 return type.substring(0, index); 329 } 330 return type; 331 } 332 writeObj(Parcel dest, int flags, Object obj, String type)333 private static void writeObj(Parcel dest, int flags, Object obj, String type) { 334 switch (getBaseType(type)) { 335 case FORMAT_SLICE: 336 case FORMAT_IMAGE: 337 case FORMAT_REMOTE_INPUT: 338 case FORMAT_BUNDLE: 339 ((Parcelable) obj).writeToParcel(dest, flags); 340 break; 341 case FORMAT_ACTION: 342 ((Pair<PendingIntent, Slice>) obj).first.writeToParcel(dest, flags); 343 ((Pair<PendingIntent, Slice>) obj).second.writeToParcel(dest, flags); 344 break; 345 case FORMAT_TEXT: 346 TextUtils.writeToParcel((CharSequence) obj, dest, flags); 347 break; 348 case FORMAT_INT: 349 dest.writeInt((Integer) obj); 350 break; 351 case FORMAT_TIMESTAMP: 352 dest.writeLong((Long) obj); 353 break; 354 } 355 } 356 readObj(String type, Parcel in)357 private static Object readObj(String type, Parcel in) { 358 switch (getBaseType(type)) { 359 case FORMAT_SLICE: 360 return Slice.CREATOR.createFromParcel(in); 361 case FORMAT_TEXT: 362 return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 363 case FORMAT_IMAGE: 364 return Icon.CREATOR.createFromParcel(in); 365 case FORMAT_ACTION: 366 return new Pair<>( 367 PendingIntent.CREATOR.createFromParcel(in), 368 Slice.CREATOR.createFromParcel(in)); 369 case FORMAT_INT: 370 return in.readInt(); 371 case FORMAT_TIMESTAMP: 372 return in.readLong(); 373 case FORMAT_REMOTE_INPUT: 374 return RemoteInput.CREATOR.createFromParcel(in); 375 case FORMAT_BUNDLE: 376 return Bundle.CREATOR.createFromParcel(in); 377 } 378 throw new RuntimeException("Unsupported type " + type); 379 } 380 381 public static final @android.annotation.NonNull Creator<SliceItem> CREATOR = new Creator<SliceItem>() { 382 @Override 383 public SliceItem createFromParcel(Parcel in) { 384 return new SliceItem(in); 385 } 386 387 @Override 388 public SliceItem[] newArray(int size) { 389 return new SliceItem[size]; 390 } 391 }; 392 } 393