Slice.java revision 6356d81d685e0e35f360f8e2aee1991c06274a0b
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 androidx.slice; 18 19import static android.app.slice.Slice.HINT_ACTIONS; 20import static android.app.slice.Slice.HINT_HORIZONTAL; 21import static android.app.slice.Slice.HINT_LARGE; 22import static android.app.slice.Slice.HINT_LIST; 23import static android.app.slice.Slice.HINT_LIST_ITEM; 24import static android.app.slice.Slice.HINT_NO_TINT; 25import static android.app.slice.Slice.HINT_PARTIAL; 26import static android.app.slice.Slice.HINT_SEE_MORE; 27import static android.app.slice.Slice.HINT_SELECTED; 28import static android.app.slice.Slice.HINT_SHORTCUT; 29import static android.app.slice.Slice.HINT_SUMMARY; 30import static android.app.slice.Slice.HINT_TITLE; 31import static android.app.slice.SliceItem.FORMAT_ACTION; 32import static android.app.slice.SliceItem.FORMAT_IMAGE; 33import static android.app.slice.SliceItem.FORMAT_INT; 34import static android.app.slice.SliceItem.FORMAT_REMOTE_INPUT; 35import static android.app.slice.SliceItem.FORMAT_SLICE; 36import static android.app.slice.SliceItem.FORMAT_TEXT; 37import static android.app.slice.SliceItem.FORMAT_TIMESTAMP; 38 39import static androidx.slice.SliceConvert.unwrap; 40import static androidx.slice.core.SliceHints.HINT_KEY_WORDS; 41 42import android.app.PendingIntent; 43import android.app.RemoteInput; 44import android.app.slice.SliceManager; 45import android.content.Context; 46import android.graphics.drawable.Icon; 47import android.net.Uri; 48import android.os.Bundle; 49import android.os.Parcelable; 50 51import java.util.ArrayList; 52import java.util.Arrays; 53import java.util.List; 54 55import androidx.annotation.NonNull; 56import androidx.annotation.Nullable; 57import androidx.annotation.RequiresApi; 58import androidx.annotation.RestrictTo; 59import androidx.annotation.RestrictTo.Scope; 60import androidx.annotation.StringDef; 61import androidx.core.os.BuildCompat; 62import androidx.slice.compat.SliceProviderCompat; 63 64/** 65 * A slice is a piece of app content and actions that can be surfaced outside of the app. 66 * 67 * <p>They are constructed using {@link androidx.slice.builders.TemplateSliceBuilder}s 68 * in a tree structure that provides the OS some information about how the content should be 69 * displayed. 70 */ 71public final class Slice { 72 73 private static final String HINTS = "hints"; 74 private static final String ITEMS = "items"; 75 private static final String URI = "uri"; 76 private static final String SPEC_TYPE = "type"; 77 private static final String SPEC_REVISION = "revision"; 78 private final SliceSpec mSpec; 79 80 /** 81 * @hide 82 */ 83 @RestrictTo(Scope.LIBRARY) 84 @StringDef({HINT_TITLE, HINT_LIST, HINT_LIST_ITEM, HINT_LARGE, HINT_ACTIONS, HINT_SELECTED, 85 HINT_HORIZONTAL, HINT_NO_TINT, HINT_PARTIAL, HINT_SUMMARY, HINT_SEE_MORE, 86 HINT_SHORTCUT, HINT_KEY_WORDS}) 87 public @interface SliceHint{ } 88 89 private final SliceItem[] mItems; 90 private final @SliceHint String[] mHints; 91 private Uri mUri; 92 93 /** 94 * @hide 95 */ 96 @RestrictTo(Scope.LIBRARY) 97 Slice(ArrayList<SliceItem> items, @SliceHint String[] hints, Uri uri, 98 SliceSpec spec) { 99 mHints = hints; 100 mItems = items.toArray(new SliceItem[items.size()]); 101 mUri = uri; 102 mSpec = spec; 103 } 104 105 /** 106 * @hide 107 */ 108 @RestrictTo(Scope.LIBRARY) 109 public Slice(Bundle in) { 110 mHints = in.getStringArray(HINTS); 111 Parcelable[] items = in.getParcelableArray(ITEMS); 112 mItems = new SliceItem[items.length]; 113 for (int i = 0; i < mItems.length; i++) { 114 if (items[i] instanceof Bundle) { 115 mItems[i] = new SliceItem((Bundle) items[i]); 116 } 117 } 118 mUri = in.getParcelable(URI); 119 mSpec = in.containsKey(SPEC_TYPE) 120 ? new SliceSpec(in.getString(SPEC_TYPE), in.getInt(SPEC_REVISION)) 121 : null; 122 } 123 124 /** 125 * @hide 126 */ 127 @RestrictTo(Scope.LIBRARY) 128 public Bundle toBundle() { 129 Bundle b = new Bundle(); 130 b.putStringArray(HINTS, mHints); 131 Parcelable[] p = new Parcelable[mItems.length]; 132 for (int i = 0; i < mItems.length; i++) { 133 p[i] = mItems[i].toBundle(); 134 } 135 b.putParcelableArray(ITEMS, p); 136 b.putParcelable(URI, mUri); 137 if (mSpec != null) { 138 b.putString(SPEC_TYPE, mSpec.getType()); 139 b.putInt(SPEC_REVISION, mSpec.getRevision()); 140 } 141 return b; 142 } 143 144 /** 145 * @return The spec for this slice 146 * @hide 147 */ 148 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 149 public @Nullable SliceSpec getSpec() { 150 return mSpec; 151 } 152 153 /** 154 * @return The Uri that this Slice represents. 155 */ 156 public Uri getUri() { 157 return mUri; 158 } 159 160 /** 161 * @return All child {@link SliceItem}s that this Slice contains. 162 */ 163 public List<SliceItem> getItems() { 164 return Arrays.asList(mItems); 165 } 166 167 /** 168 * @return All hints associated with this Slice. 169 */ 170 public @SliceHint List<String> getHints() { 171 return Arrays.asList(mHints); 172 } 173 174 /** 175 * @hide 176 */ 177 @RestrictTo(Scope.LIBRARY_GROUP) 178 public boolean hasHint(@SliceHint String hint) { 179 return ArrayUtils.contains(mHints, hint); 180 } 181 182 /** 183 * A Builder used to construct {@link Slice}s 184 * @hide 185 */ 186 @RestrictTo(Scope.LIBRARY_GROUP) 187 public static class Builder { 188 189 private final Uri mUri; 190 private ArrayList<SliceItem> mItems = new ArrayList<>(); 191 private @SliceHint ArrayList<String> mHints = new ArrayList<>(); 192 private SliceSpec mSpec; 193 194 /** 195 * Create a builder which will construct a {@link Slice} for the Given Uri. 196 * @param uri Uri to tag for this slice. 197 */ 198 public Builder(@NonNull Uri uri) { 199 mUri = uri; 200 } 201 202 /** 203 * Create a builder for a {@link Slice} that is a sub-slice of the slice 204 * being constructed by the provided builder. 205 * @param parent The builder constructing the parent slice 206 */ 207 public Builder(@NonNull Slice.Builder parent) { 208 mUri = parent.mUri.buildUpon().appendPath("_gen") 209 .appendPath(String.valueOf(mItems.size())).build(); 210 } 211 212 /** 213 * Add the spec for this slice. 214 * @hide 215 */ 216 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 217 public Builder setSpec(SliceSpec spec) { 218 mSpec = spec; 219 return this; 220 } 221 222 /** 223 * Add hints to the Slice being constructed 224 */ 225 public Builder addHints(@SliceHint String... hints) { 226 mHints.addAll(Arrays.asList(hints)); 227 return this; 228 } 229 230 /** 231 * Add hints to the Slice being constructed 232 */ 233 public Builder addHints(@SliceHint List<String> hints) { 234 return addHints(hints.toArray(new String[hints.size()])); 235 } 236 237 /** 238 * Add a sub-slice to the slice being constructed 239 */ 240 public Builder addSubSlice(@NonNull Slice slice) { 241 return addSubSlice(slice, null); 242 } 243 244 /** 245 * Add a sub-slice to the slice being constructed 246 * @param subType Optional template-specific type information 247 * @see {@link SliceItem#getSubType()} 248 */ 249 public Builder addSubSlice(@NonNull Slice slice, String subType) { 250 mItems.add(new SliceItem(slice, FORMAT_SLICE, subType, slice.getHints().toArray( 251 new String[slice.getHints().size()]))); 252 return this; 253 } 254 255 /** 256 * Add an action to the slice being constructed 257 * @param subType Optional template-specific type information 258 * @see {@link SliceItem#getSubType()} 259 */ 260 public Slice.Builder addAction(@NonNull PendingIntent action, 261 @NonNull Slice s, @Nullable String subType) { 262 @SliceHint String[] hints = s != null 263 ? s.getHints().toArray(new String[s.getHints().size()]) : new String[0]; 264 mItems.add(new SliceItem(action, s, FORMAT_ACTION, subType, hints)); 265 return this; 266 } 267 268 /** 269 * Add text to the slice being constructed 270 * @param subType Optional template-specific type information 271 * @see {@link SliceItem#getSubType()} 272 */ 273 public Builder addText(CharSequence text, @Nullable String subType, 274 @SliceHint String... hints) { 275 mItems.add(new SliceItem(text, FORMAT_TEXT, subType, hints)); 276 return this; 277 } 278 279 /** 280 * Add text to the slice being constructed 281 * @param subType Optional template-specific type information 282 * @see {@link SliceItem#getSubType()} 283 */ 284 public Builder addText(CharSequence text, @Nullable String subType, 285 @SliceHint List<String> hints) { 286 return addText(text, subType, hints.toArray(new String[hints.size()])); 287 } 288 289 /** 290 * Add an image to the slice being constructed 291 * @param subType Optional template-specific type information 292 * @see {@link SliceItem#getSubType()} 293 */ 294 public Builder addIcon(Icon icon, @Nullable String subType, 295 @SliceHint String... hints) { 296 mItems.add(new SliceItem(icon, FORMAT_IMAGE, subType, hints)); 297 return this; 298 } 299 300 /** 301 * Add an image to the slice being constructed 302 * @param subType Optional template-specific type information 303 * @see {@link SliceItem#getSubType()} 304 */ 305 public Builder addIcon(Icon icon, @Nullable String subType, 306 @SliceHint List<String> hints) { 307 return addIcon(icon, subType, hints.toArray(new String[hints.size()])); 308 } 309 310 /** 311 * Add remote input to the slice being constructed 312 * @param subType Optional template-specific type information 313 * @see {@link SliceItem#getSubType()} 314 * @hide 315 */ 316 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 317 public Slice.Builder addRemoteInput(RemoteInput remoteInput, @Nullable String subType, 318 @SliceHint List<String> hints) { 319 return addRemoteInput(remoteInput, subType, hints.toArray(new String[hints.size()])); 320 } 321 322 /** 323 * Add remote input to the slice being constructed 324 * @param subType Optional template-specific type information 325 * @see {@link SliceItem#getSubType()} 326 * @hide 327 */ 328 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 329 public Slice.Builder addRemoteInput(RemoteInput remoteInput, @Nullable String subType, 330 @SliceHint String... hints) { 331 mItems.add(new SliceItem(remoteInput, FORMAT_REMOTE_INPUT, subType, hints)); 332 return this; 333 } 334 335 /** 336 * Add a int to the slice being constructed 337 * @param subType Optional template-specific type information 338 * @see {@link SliceItem#getSubType()} 339 */ 340 public Builder addInt(int value, @Nullable String subType, 341 @SliceHint String... hints) { 342 mItems.add(new SliceItem(value, FORMAT_INT, subType, hints)); 343 return this; 344 } 345 346 /** 347 * Add a int to the slice being constructed 348 * @param subType Optional template-specific type information 349 * @see {@link SliceItem#getSubType()} 350 */ 351 public Builder addInt(int value, @Nullable String subType, 352 @SliceHint List<String> hints) { 353 return addInt(value, subType, hints.toArray(new String[hints.size()])); 354 } 355 356 /** 357 * Add a timestamp to the slice being constructed 358 * @param subType Optional template-specific type information 359 * @see {@link SliceItem#getSubType()} 360 */ 361 public Slice.Builder addTimestamp(long time, @Nullable String subType, 362 @SliceHint String... hints) { 363 mItems.add(new SliceItem(time, FORMAT_TIMESTAMP, subType, hints)); 364 return this; 365 } 366 367 /** 368 * Add a timestamp to the slice being constructed 369 * @param subType Optional template-specific type information 370 * @see {@link SliceItem#getSubType()} 371 */ 372 public Slice.Builder addTimestamp(long time, @Nullable String subType, 373 @SliceHint List<String> hints) { 374 return addTimestamp(time, subType, hints.toArray(new String[hints.size()])); 375 } 376 377 /** 378 * Add a SliceItem to the slice being constructed. 379 * @hide 380 */ 381 @RestrictTo(Scope.LIBRARY) 382 public Slice.Builder addItem(SliceItem item) { 383 mItems.add(item); 384 return this; 385 } 386 387 /** 388 * Construct the slice. 389 */ 390 public Slice build() { 391 return new Slice(mItems, mHints.toArray(new String[mHints.size()]), mUri, mSpec); 392 } 393 } 394 395 /** 396 * @hide 397 * @return A string representation of this slice. 398 */ 399 @RestrictTo(Scope.LIBRARY) 400 @Override 401 public String toString() { 402 return toString(""); 403 } 404 405 /** 406 * @hide 407 */ 408 @RestrictTo(Scope.LIBRARY) 409 public String toString(String indent) { 410 StringBuilder sb = new StringBuilder(); 411 for (int i = 0; i < mItems.length; i++) { 412 sb.append(indent); 413 if (FORMAT_SLICE.equals(mItems[i].getFormat())) { 414 sb.append("slice:\n"); 415 sb.append(mItems[i].getSlice().toString(indent + " ")); 416 } else if (FORMAT_ACTION.equals(mItems[i].getFormat())) { 417 sb.append("action:\n"); 418 sb.append(mItems[i].getSlice().toString(indent + " ")); 419 } else if (FORMAT_TEXT.equals(mItems[i].getFormat())) { 420 sb.append("text: "); 421 sb.append(mItems[i].getText()); 422 sb.append("\n"); 423 } else { 424 sb.append(SliceItem.typeToString(mItems[i].getFormat())); 425 sb.append("\n"); 426 } 427 } 428 return sb.toString(); 429 } 430 431 /** 432 * Turns a slice Uri into slice content. 433 * 434 * @hide 435 * @param context Context to be used. 436 * @param uri The URI to a slice provider 437 * @return The Slice provided by the app or null if none is given. 438 * @see Slice 439 */ 440 @RestrictTo(Scope.LIBRARY_GROUP) 441 @SuppressWarnings("NewApi") // Lint doesn't understand BuildCompat. 442 @Nullable 443 public static Slice bindSlice(Context context, @NonNull Uri uri, 444 List<SliceSpec> supportedSpecs) { 445 if (BuildCompat.isAtLeastP()) { 446 return callBindSlice(context, uri, supportedSpecs); 447 } else { 448 return SliceProviderCompat.bindSlice(context, uri, supportedSpecs); 449 } 450 } 451 452 @RequiresApi(28) 453 private static Slice callBindSlice(Context context, Uri uri, 454 List<SliceSpec> supportedSpecs) { 455 return SliceConvert.wrap(context.getSystemService(SliceManager.class) 456 .bindSlice(uri, unwrap(supportedSpecs))); 457 } 458} 459