1 /* 2 * Copyright (C) 2019 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 com.android.tv.twopanelsettings.slices; 18 19 import static android.app.slice.Slice.HINT_PARTIAL; 20 import static android.app.slice.Slice.HINT_SUMMARY; 21 import static android.app.slice.Slice.HINT_TITLE; 22 import static android.app.slice.Slice.SUBTYPE_CONTENT_DESCRIPTION; 23 import static android.app.slice.SliceItem.FORMAT_ACTION; 24 import static android.app.slice.SliceItem.FORMAT_IMAGE; 25 import static android.app.slice.SliceItem.FORMAT_INT; 26 import static android.app.slice.SliceItem.FORMAT_LONG; 27 import static android.app.slice.SliceItem.FORMAT_SLICE; 28 import static android.app.slice.SliceItem.FORMAT_TEXT; 29 30 import static com.android.tv.twopanelsettings.slices.HasCustomContentDescription.CONTENT_DESCRIPTION_SEPARATOR; 31 import static com.android.tv.twopanelsettings.slices.SlicesConstants.CHECKMARK; 32 import static com.android.tv.twopanelsettings.slices.SlicesConstants.EXTRA_ACTION_ID; 33 import static com.android.tv.twopanelsettings.slices.SlicesConstants.EXTRA_ADD_INFO_STATUS; 34 import static com.android.tv.twopanelsettings.slices.SlicesConstants.EXTRA_PAGE_ID; 35 import static com.android.tv.twopanelsettings.slices.SlicesConstants.EXTRA_PREFERENCE_INFO_IMAGE; 36 import static com.android.tv.twopanelsettings.slices.SlicesConstants.EXTRA_PREFERENCE_INFO_STATUS; 37 import static com.android.tv.twopanelsettings.slices.SlicesConstants.EXTRA_PREFERENCE_INFO_SUMMARY; 38 import static com.android.tv.twopanelsettings.slices.SlicesConstants.EXTRA_PREFERENCE_INFO_TEXT; 39 import static com.android.tv.twopanelsettings.slices.SlicesConstants.EXTRA_PREFERENCE_INFO_TITLE_ICON; 40 import static com.android.tv.twopanelsettings.slices.SlicesConstants.RADIO; 41 import static com.android.tv.twopanelsettings.slices.SlicesConstants.SEEKBAR; 42 import static com.android.tv.twopanelsettings.slices.SlicesConstants.SWITCH; 43 44 import android.graphics.drawable.Drawable; 45 import android.graphics.drawable.Icon; 46 import android.net.Uri; 47 import android.os.Bundle; 48 import android.text.TextUtils; 49 import android.util.Pair; 50 import android.view.ContextThemeWrapper; 51 52 import androidx.core.graphics.drawable.IconCompat; 53 import androidx.preference.Preference; 54 import androidx.slice.Slice; 55 import androidx.slice.SliceItem; 56 import androidx.slice.core.SliceActionImpl; 57 import androidx.slice.core.SliceQuery; 58 import androidx.slice.widget.SliceContent; 59 60 import com.android.tv.twopanelsettings.IconUtil; 61 import com.android.tv.twopanelsettings.R; 62 63 import java.util.ArrayList; 64 import java.util.List; 65 66 /** 67 * Generate corresponding preference based upon the slice data. 68 */ 69 public final class SlicePreferencesUtil { 70 getPreference(SliceItem item, ContextThemeWrapper contextThemeWrapper, String className, boolean isTwoPanel)71 static Preference getPreference(SliceItem item, ContextThemeWrapper contextThemeWrapper, 72 String className, boolean isTwoPanel) { 73 Preference preference = null; 74 if (item == null) { 75 return null; 76 } 77 Data data = extract(item); 78 if (item.getSubType() != null) { 79 String subType = item.getSubType(); 80 if (subType.equals(SlicesConstants.TYPE_PREFERENCE) 81 || subType.equals(SlicesConstants.TYPE_PREFERENCE_EMBEDDED) 82 || subType.equals(SlicesConstants.TYPE_PREFERENCE_EMBEDDED_PLACEHOLDER)) { 83 // TODO: Figure out all the possible cases and reorganize the logic 84 if (data.mInfoItems.size() > 0) { 85 preference = new InfoPreference( 86 contextThemeWrapper, getInfoList(data.mInfoItems)); 87 } else if (data.mIntentItem != null) { 88 SliceActionImpl action = new SliceActionImpl(data.mIntentItem); 89 if (action != null) { 90 // Currently if we don't set icon for the SliceAction, slice lib will 91 // automatically treat it as a toggle. To distinguish preference action and 92 // toggle action, we need to add a subtype if this is a preference action. 93 preference = new SlicePreference(contextThemeWrapper); 94 ((SlicePreference) preference).setSliceAction(action); 95 ((SlicePreference) preference).setActionId(getActionId(item)); 96 if (data.mFollowupIntentItem != null) { 97 SliceActionImpl followUpAction = 98 new SliceActionImpl(data.mFollowupIntentItem); 99 ((SlicePreference) preference).setFollowupSliceAction(followUpAction); 100 } 101 } 102 } else if (data.mEndItems.size() > 0 && data.mEndItems.get(0) != null) { 103 SliceActionImpl action = new SliceActionImpl(data.mEndItems.get(0)); 104 if (action != null) { 105 int buttonStyle = SlicePreferencesUtil.getButtonStyle(item); 106 switch (buttonStyle) { 107 case CHECKMARK : 108 preference = new SliceCheckboxPreference( 109 contextThemeWrapper, action); 110 break; 111 case SWITCH : 112 preference = new SliceSwitchPreference(contextThemeWrapper, action); 113 break; 114 case RADIO: 115 preference = new SliceRadioPreference(contextThemeWrapper, action); 116 preference.setLayoutResource(R.layout.preference_reversed_widget); 117 if (getRadioGroup(item) != null) { 118 ((SliceRadioPreference) preference).setRadioGroup( 119 getRadioGroup(item).toString()); 120 } 121 break; 122 case SEEKBAR : 123 int min = SlicePreferencesUtil.getSeekbarMin(item); 124 int max = SlicePreferencesUtil.getSeekbarMax(item); 125 int value = SlicePreferencesUtil.getSeekbarValue(item); 126 preference = new SliceSeekbarPreference( 127 contextThemeWrapper, action, min, max, value); 128 break; 129 } 130 if (preference instanceof HasSliceAction) { 131 ((HasSliceAction) preference).setActionId(getActionId(item)); 132 } 133 if (data.mFollowupIntentItem != null) { 134 SliceActionImpl followUpAction = 135 new SliceActionImpl(data.mFollowupIntentItem); 136 ((HasSliceAction) preference).setFollowupSliceAction(followUpAction); 137 138 } 139 } 140 } 141 142 CharSequence uri = getText(data.mTargetSliceItem); 143 if (uri == null || TextUtils.isEmpty(uri)) { 144 if (preference == null) { 145 preference = new CustomContentDescriptionPreference(contextThemeWrapper); 146 } 147 } else { 148 if (preference == null) { 149 if (subType.equals(SlicesConstants.TYPE_PREFERENCE_EMBEDDED_PLACEHOLDER)) { 150 preference = new EmbeddedSlicePreference(contextThemeWrapper, 151 String.valueOf(uri)); 152 } else { 153 preference = new SlicePreference(contextThemeWrapper); 154 } 155 if (hasEndIcon(data.mHasEndIconItem)) { 156 preference.setLayoutResource(R.layout.preference_reversed_icon); 157 } 158 } 159 ((HasSliceUri) preference).setUri(uri.toString()); 160 if (preference instanceof HasSliceAction) { 161 ((HasSliceAction) preference).setActionId(getActionId(item)); 162 } 163 preference.setFragment(className); 164 } 165 } else if (item.getSubType().equals(SlicesConstants.TYPE_PREFERENCE_CATEGORY)) { 166 preference = new CustomContentDescriptionPreferenceCategory(contextThemeWrapper); 167 } 168 } 169 170 if (preference != null) { 171 boolean isEnabled = enabled(item); 172 // Set whether preference is enabled. 173 if (preference instanceof InfoPreference || !isEnabled) { 174 preference.setEnabled(false); 175 } 176 // Set whether preference is selectable 177 if (!selectable(item) || !isEnabled) { 178 preference.setSelectable(false); 179 } 180 // Set the key for the preference 181 CharSequence key = getKey(item); 182 if (key != null) { 183 preference.setKey(key.toString()); 184 } 185 186 Icon icon = getIcon(data.mStartItem); 187 if (icon != null) { 188 boolean isIconNeedToBeProcessed = 189 SlicePreferencesUtil.isIconNeedsToBeProcessed(item); 190 Drawable iconDrawable = icon.loadDrawable(contextThemeWrapper); 191 if (isIconNeedToBeProcessed && isTwoPanel) { 192 preference.setIcon(IconUtil.getCompoundIcon(contextThemeWrapper, iconDrawable)); 193 } else { 194 preference.setIcon(iconDrawable); 195 } 196 } 197 198 if (data.mTitleItem != null) { 199 preference.setTitle(getText(data.mTitleItem)); 200 } 201 202 //Set summary 203 CharSequence subtitle = 204 data.mSubtitleItem != null ? data.mSubtitleItem.getText() : null; 205 boolean subtitleExists = !TextUtils.isEmpty(subtitle) 206 || (data.mSubtitleItem != null && data.mSubtitleItem.hasHint(HINT_PARTIAL)); 207 if (subtitleExists) { 208 preference.setSummary(subtitle); 209 } else { 210 if (data.mSummaryItem != null) { 211 preference.setSummary(getText(data.mSummaryItem)); 212 } 213 } 214 215 // Set preview info image and text 216 CharSequence infoText = getInfoText(item); 217 CharSequence infoSummary = getInfoSummary(item); 218 boolean addInfoStatus = addInfoStatus(item); 219 IconCompat infoImage = getInfoImage(item); 220 IconCompat infoTitleIcon = getInfoTitleIcon(item); 221 Bundle b = preference.getExtras(); 222 String fallbackInfoContentDescription = ""; 223 if (preference.getTitle() != null) { 224 fallbackInfoContentDescription += preference.getTitle().toString(); 225 } 226 if (infoImage != null) { 227 b.putParcelable(EXTRA_PREFERENCE_INFO_IMAGE, infoImage.toIcon()); 228 } 229 if (infoTitleIcon != null) { 230 b.putParcelable(EXTRA_PREFERENCE_INFO_TITLE_ICON, infoTitleIcon.toIcon()); 231 } 232 if (infoText != null) { 233 if (preference instanceof SliceSwitchPreference && addInfoStatus) { 234 b.putBoolean(InfoFragment.EXTRA_INFO_HAS_STATUS, true); 235 b.putBoolean(EXTRA_PREFERENCE_INFO_STATUS, 236 ((SliceSwitchPreference) preference).isChecked()); 237 } else { 238 b.putBoolean(InfoFragment.EXTRA_INFO_HAS_STATUS, false); 239 } 240 b.putCharSequence(EXTRA_PREFERENCE_INFO_TEXT, infoText); 241 if (preference.getTitle() != null 242 && !preference.getTitle().equals(infoText.toString())) { 243 fallbackInfoContentDescription += 244 CONTENT_DESCRIPTION_SEPARATOR + infoText.toString(); 245 } 246 247 } 248 if (infoSummary != null) { 249 b.putCharSequence(EXTRA_PREFERENCE_INFO_SUMMARY, infoSummary); 250 fallbackInfoContentDescription += 251 CONTENT_DESCRIPTION_SEPARATOR + infoSummary; 252 } 253 String contentDescription = getInfoContentDescription(item); 254 // Respect the content description values provided by slice. 255 // If not provided, for SlicePreference, SliceSwitchPreference, 256 // CustomContentDescriptionPreference, use the fallback value. 257 // Otherwise, do not set the contentDescription for preference. Rely on the talkback 258 // framework to generate the value itself. 259 if (!TextUtils.isEmpty(contentDescription)) { 260 if (preference instanceof HasCustomContentDescription) { 261 ((HasCustomContentDescription) preference).setContentDescription( 262 contentDescription); 263 } 264 } else { 265 if ((preference instanceof SlicePreference) 266 || (preference instanceof SliceSwitchPreference) 267 || (preference instanceof CustomContentDescriptionPreference)) { 268 ((HasCustomContentDescription) preference).setContentDescription( 269 fallbackInfoContentDescription); 270 } 271 } 272 if (infoImage != null || infoText != null || infoSummary != null) { 273 preference.setFragment(InfoFragment.class.getCanonicalName()); 274 } 275 } 276 277 return preference; 278 } 279 280 static class Data { 281 SliceItem mStartItem; 282 SliceItem mTitleItem; 283 SliceItem mSubtitleItem; 284 SliceItem mSummaryItem; 285 SliceItem mTargetSliceItem; 286 SliceItem mRadioGroupItem; 287 SliceItem mIntentItem; 288 SliceItem mFollowupIntentItem; 289 SliceItem mHasEndIconItem; 290 List<SliceItem> mEndItems = new ArrayList<>(); 291 List<SliceItem> mInfoItems = new ArrayList<>(); 292 } 293 extract(SliceItem sliceItem)294 static Data extract(SliceItem sliceItem) { 295 Data data = new Data(); 296 List<SliceItem> possibleStartItems = 297 SliceQuery.findAll(sliceItem, null, HINT_TITLE, null); 298 if (possibleStartItems.size() > 0) { 299 // The start item will be at position 0 if it exists 300 String format = possibleStartItems.get(0).getFormat(); 301 if ((FORMAT_ACTION.equals(format) 302 && SliceQuery.find(possibleStartItems.get(0), FORMAT_IMAGE) != null) 303 || FORMAT_SLICE.equals(format) 304 || FORMAT_LONG.equals(format) 305 || FORMAT_IMAGE.equals(format)) { 306 data.mStartItem = possibleStartItems.get(0); 307 } 308 } 309 310 List<SliceItem> items = sliceItem.getSlice().getItems(); 311 for (int i = 0; i < items.size(); i++) { 312 final SliceItem item = items.get(i); 313 String subType = item.getSubType(); 314 if (subType != null) { 315 switch (subType) { 316 case SlicesConstants.SUBTYPE_INFO_PREFERENCE : 317 data.mInfoItems.add(item); 318 break; 319 case SlicesConstants.SUBTYPE_INTENT : 320 data.mIntentItem = item; 321 break; 322 case SlicesConstants.SUBTYPE_FOLLOWUP_INTENT : 323 data.mFollowupIntentItem = item; 324 break; 325 case SlicesConstants.TAG_TARGET_URI : 326 data.mTargetSliceItem = item; 327 break; 328 case SlicesConstants.EXTRA_HAS_END_ICON: 329 data.mHasEndIconItem = item; 330 break; 331 } 332 } else if (FORMAT_TEXT.equals(item.getFormat()) && (item.getSubType() == null)) { 333 if ((data.mTitleItem == null || !data.mTitleItem.hasHint(HINT_TITLE)) 334 && item.hasHint(HINT_TITLE) && !item.hasHint(HINT_SUMMARY)) { 335 data.mTitleItem = item; 336 } else if (data.mSubtitleItem == null && !item.hasHint(HINT_SUMMARY)) { 337 data.mSubtitleItem = item; 338 } else if (data.mSummaryItem == null && item.hasHint(HINT_SUMMARY)) { 339 data.mSummaryItem = item; 340 } 341 } else { 342 data.mEndItems.add(item); 343 } 344 } 345 data.mEndItems.remove(data.mStartItem); 346 return data; 347 } 348 getInfoList(List<SliceItem> sliceItems)349 private static List<Pair<CharSequence, CharSequence>> getInfoList(List<SliceItem> sliceItems) { 350 List<Pair<CharSequence, CharSequence>> infoList = new ArrayList<>(); 351 for (SliceItem item : sliceItems) { 352 Slice itemSlice = item.getSlice(); 353 if (itemSlice != null) { 354 CharSequence title = null; 355 CharSequence summary = null; 356 for (SliceItem element : itemSlice.getItems()) { 357 if (element.getHints().contains(HINT_TITLE)) { 358 title = element.getText(); 359 } else if (element.getHints().contains(HINT_SUMMARY)) { 360 summary = element.getText(); 361 } 362 } 363 infoList.add(new Pair<CharSequence, CharSequence>(title, summary)); 364 } 365 } 366 return infoList; 367 } 368 getKey(SliceItem item)369 private static CharSequence getKey(SliceItem item) { 370 SliceItem target = SliceQuery.findSubtype(item, FORMAT_TEXT, SlicesConstants.TAG_KEY); 371 return target != null ? target.getText() : null; 372 } 373 getRadioGroup(SliceItem item)374 private static CharSequence getRadioGroup(SliceItem item) { 375 SliceItem target = SliceQuery.findSubtype( 376 item, FORMAT_TEXT, SlicesConstants.TAG_RADIO_GROUP); 377 return target != null ? target.getText() : null; 378 } 379 380 /** 381 * Get the screen title item for the slice. 382 * @param sliceItems list of SliceItem extracted from slice data. 383 * @return screen title item. 384 */ getScreenTitleItem(List<SliceContent> sliceItems)385 static SliceItem getScreenTitleItem(List<SliceContent> sliceItems) { 386 for (SliceContent contentItem : sliceItems) { 387 SliceItem item = contentItem.getSliceItem(); 388 if (item.getSubType() != null 389 && item.getSubType().equals(SlicesConstants.TYPE_PREFERENCE_SCREEN_TITLE)) { 390 return item; 391 } 392 } 393 return null; 394 } 395 getRedirectSlice(List<SliceContent> sliceItems)396 static SliceItem getRedirectSlice(List<SliceContent> sliceItems) { 397 for (SliceContent contentItem : sliceItems) { 398 SliceItem item = contentItem.getSliceItem(); 399 if (item.getSubType() != null 400 && item.getSubType().equals(SlicesConstants.TYPE_REDIRECTED_SLICE_URI)) { 401 return item; 402 } 403 } 404 return null; 405 } 406 getFocusedPreferenceItem(List<SliceContent> sliceItems)407 static SliceItem getFocusedPreferenceItem(List<SliceContent> sliceItems) { 408 for (SliceContent contentItem : sliceItems) { 409 SliceItem item = contentItem.getSliceItem(); 410 if (item.getSubType() != null 411 && item.getSubType().equals(SlicesConstants.TYPE_FOCUSED_PREFERENCE)) { 412 return item; 413 } 414 } 415 return null; 416 } 417 getEmbeddedItem(List<SliceContent> sliceItems)418 static SliceItem getEmbeddedItem(List<SliceContent> sliceItems) { 419 for (SliceContent contentItem : sliceItems) { 420 SliceItem item = contentItem.getSliceItem(); 421 if (item.getSubType() != null 422 && item.getSubType().equals(SlicesConstants.TYPE_PREFERENCE_EMBEDDED)) { 423 return item; 424 } 425 } 426 return null; 427 } 428 isIconNeedsToBeProcessed(SliceItem sliceItem)429 private static boolean isIconNeedsToBeProcessed(SliceItem sliceItem) { 430 List<SliceItem> items = sliceItem.getSlice().getItems(); 431 for (SliceItem item : items) { 432 if (item.getSubType() != null && item.getSubType().equals( 433 SlicesConstants.SUBTYPE_ICON_NEED_TO_BE_PROCESSED)) { 434 return item.getInt() == 1; 435 } 436 } 437 return false; 438 } 439 getButtonStyle(SliceItem sliceItem)440 private static int getButtonStyle(SliceItem sliceItem) { 441 List<SliceItem> items = sliceItem.getSlice().getItems(); 442 for (SliceItem item : items) { 443 if (item.getSubType() != null 444 && item.getSubType().equals(SlicesConstants.SUBTYPE_BUTTON_STYLE)) { 445 return item.getInt(); 446 } 447 } 448 return -1; 449 } 450 getSeekbarMin(SliceItem sliceItem)451 private static int getSeekbarMin(SliceItem sliceItem) { 452 List<SliceItem> items = sliceItem.getSlice().getItems(); 453 for (SliceItem item : items) { 454 if (item.getSubType() != null 455 && item.getSubType().equals(SlicesConstants.SUBTYPE_SEEKBAR_MIN)) { 456 return item.getInt(); 457 } 458 } 459 return -1; 460 } 461 getSeekbarMax(SliceItem sliceItem)462 private static int getSeekbarMax(SliceItem sliceItem) { 463 List<SliceItem> items = sliceItem.getSlice().getItems(); 464 for (SliceItem item : items) { 465 if (item.getSubType() != null 466 && item.getSubType().equals(SlicesConstants.SUBTYPE_SEEKBAR_MAX)) { 467 return item.getInt(); 468 } 469 } 470 return -1; 471 } 472 getSeekbarValue(SliceItem sliceItem)473 private static int getSeekbarValue(SliceItem sliceItem) { 474 List<SliceItem> items = sliceItem.getSlice().getItems(); 475 for (SliceItem item : items) { 476 if (item.getSubType() != null 477 && item.getSubType().equals(SlicesConstants.SUBTYPE_SEEKBAR_VALUE)) { 478 return item.getInt(); 479 } 480 } 481 return -1; 482 } 483 enabled(SliceItem sliceItem)484 private static boolean enabled(SliceItem sliceItem) { 485 List<SliceItem> items = sliceItem.getSlice().getItems(); 486 for (SliceItem item : items) { 487 if (item.getSubType() != null 488 && item.getSubType().equals(SlicesConstants.SUBTYPE_IS_ENABLED)) { 489 return item.getInt() == 1; 490 } 491 } 492 return true; 493 } 494 selectable(SliceItem sliceItem)495 private static boolean selectable(SliceItem sliceItem) { 496 List<SliceItem> items = sliceItem.getSlice().getItems(); 497 for (SliceItem item : items) { 498 if (item.getSubType() != null 499 && item.getSubType().equals(SlicesConstants.SUBTYPE_IS_SELECTABLE)) { 500 return item.getInt() == 1; 501 } 502 } 503 return true; 504 } 505 addInfoStatus(SliceItem sliceItem)506 private static boolean addInfoStatus(SliceItem sliceItem) { 507 List<SliceItem> items = sliceItem.getSlice().getItems(); 508 for (SliceItem item : items) { 509 if (item.getSubType() != null 510 && item.getSubType().equals(EXTRA_ADD_INFO_STATUS)) { 511 return item.getInt() == 1; 512 } 513 } 514 return true; 515 } 516 hasEndIcon(SliceItem item)517 private static boolean hasEndIcon(SliceItem item) { 518 return item != null && item.getInt() > 0; 519 } 520 521 /** 522 * Checks if custom content description should be forced to be used if provided. This function 523 * can be extended with more cases if needed. 524 * 525 * @param item The {@link SliceItem} containing the necessary information. 526 * @return <code>true</code> if custom content description should be used. 527 */ shouldForceContentDescription(SliceItem sliceItem)528 private static boolean shouldForceContentDescription(SliceItem sliceItem) { 529 List<SliceItem> items = sliceItem.getSlice().getItems(); 530 for (SliceItem item : items) { 531 // Checks if an end icon has been set. 532 if (item.getSubType() != null 533 && item.getSubType().equals(SlicesConstants.EXTRA_HAS_END_ICON)) { 534 return hasEndIcon(item); 535 } 536 } 537 return false; 538 } 539 540 /** 541 * Get the text from the SliceItem. 542 */ getText(SliceItem item)543 static CharSequence getText(SliceItem item) { 544 if (item == null) { 545 return null; 546 } 547 return item.getText(); 548 } 549 550 /** Get the icon from the SliceItem if available */ getIcon(SliceItem startItem)551 static Icon getIcon(SliceItem startItem) { 552 if (startItem != null && startItem.getSlice() != null 553 && startItem.getSlice().getItems() != null 554 && startItem.getSlice().getItems().size() > 0) { 555 SliceItem iconItem = startItem.getSlice().getItems().get(0); 556 if (FORMAT_IMAGE.equals(iconItem.getFormat())) { 557 IconCompat icon = iconItem.getIcon(); 558 return icon.toIcon(); 559 } 560 } 561 return null; 562 } 563 getStatusPath(String uriString)564 static Uri getStatusPath(String uriString) { 565 Uri statusUri = Uri.parse(uriString) 566 .buildUpon().path("/" + SlicesConstants.PATH_STATUS).build(); 567 return statusUri; 568 } 569 getPageId(SliceItem item)570 static int getPageId(SliceItem item) { 571 SliceItem target = SliceQuery.findSubtype(item, FORMAT_INT, EXTRA_PAGE_ID); 572 return target != null ? target.getInt() : 0; 573 } 574 getActionId(SliceItem item)575 private static int getActionId(SliceItem item) { 576 SliceItem target = SliceQuery.findSubtype(item, FORMAT_INT, EXTRA_ACTION_ID); 577 return target != null ? target.getInt() : 0; 578 } 579 580 getInfoText(SliceItem item)581 private static CharSequence getInfoText(SliceItem item) { 582 SliceItem target = SliceQuery.findSubtype(item, FORMAT_TEXT, EXTRA_PREFERENCE_INFO_TEXT); 583 return target != null ? target.getText() : null; 584 } 585 getInfoSummary(SliceItem item)586 private static CharSequence getInfoSummary(SliceItem item) { 587 SliceItem target = SliceQuery.findSubtype(item, FORMAT_TEXT, EXTRA_PREFERENCE_INFO_SUMMARY); 588 return target != null ? target.getText() : null; 589 } 590 getInfoImage(SliceItem item)591 private static IconCompat getInfoImage(SliceItem item) { 592 SliceItem target = SliceQuery.findSubtype(item, FORMAT_IMAGE, EXTRA_PREFERENCE_INFO_IMAGE); 593 return target != null ? target.getIcon() : null; 594 } 595 getInfoTitleIcon(SliceItem item)596 private static IconCompat getInfoTitleIcon(SliceItem item) { 597 SliceItem target = SliceQuery.findSubtype( 598 item, FORMAT_IMAGE, EXTRA_PREFERENCE_INFO_TITLE_ICON); 599 return target != null ? target.getIcon() : null; 600 } 601 602 /** 603 * Get the content description from SliceItem if available 604 */ getInfoContentDescription( SliceItem sliceItem)605 private static String getInfoContentDescription( 606 SliceItem sliceItem) { 607 List<SliceItem> items = sliceItem.getSlice().getItems(); 608 for (SliceItem item : items) { 609 if (item.getSubType() != null 610 && item.getSubType().equals(SUBTYPE_CONTENT_DESCRIPTION)) { 611 return item.getText().toString(); 612 } 613 } 614 return null; 615 } 616 } 617