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 // Set whether preference is enabled. 172 if (preference instanceof InfoPreference || !enabled(item)) { 173 preference.setEnabled(false); 174 } 175 // Set whether preference is selectable 176 if (!selectable(item)) { 177 preference.setSelectable(false); 178 } 179 // Set the key for the preference 180 CharSequence key = getKey(item); 181 if (key != null) { 182 preference.setKey(key.toString()); 183 } 184 185 Icon icon = getIcon(data.mStartItem); 186 if (icon != null) { 187 boolean isIconNeedToBeProcessed = 188 SlicePreferencesUtil.isIconNeedsToBeProcessed(item); 189 Drawable iconDrawable = icon.loadDrawable(contextThemeWrapper); 190 if (isIconNeedToBeProcessed && isTwoPanel) { 191 preference.setIcon(IconUtil.getCompoundIcon(contextThemeWrapper, iconDrawable)); 192 } else { 193 preference.setIcon(iconDrawable); 194 } 195 } 196 197 if (data.mTitleItem != null) { 198 preference.setTitle(getText(data.mTitleItem)); 199 } 200 201 //Set summary 202 CharSequence subtitle = 203 data.mSubtitleItem != null ? data.mSubtitleItem.getText() : null; 204 boolean subtitleExists = !TextUtils.isEmpty(subtitle) 205 || (data.mSubtitleItem != null && data.mSubtitleItem.hasHint(HINT_PARTIAL)); 206 if (subtitleExists) { 207 preference.setSummary(subtitle); 208 } else { 209 if (data.mSummaryItem != null) { 210 preference.setSummary(getText(data.mSummaryItem)); 211 } 212 } 213 214 // Set preview info image and text 215 CharSequence infoText = getInfoText(item); 216 CharSequence infoSummary = getInfoSummary(item); 217 boolean addInfoStatus = addInfoStatus(item); 218 IconCompat infoImage = getInfoImage(item); 219 IconCompat infoTitleIcon = getInfoTitleIcon(item); 220 Bundle b = preference.getExtras(); 221 String fallbackInfoContentDescription = ""; 222 if (preference.getTitle() != null) { 223 fallbackInfoContentDescription += preference.getTitle().toString(); 224 } 225 if (infoImage != null) { 226 b.putParcelable(EXTRA_PREFERENCE_INFO_IMAGE, infoImage.toIcon()); 227 } 228 if (infoTitleIcon != null) { 229 b.putParcelable(EXTRA_PREFERENCE_INFO_TITLE_ICON, infoTitleIcon.toIcon()); 230 } 231 if (infoText != null) { 232 if (preference instanceof SliceSwitchPreference && addInfoStatus) { 233 b.putBoolean(InfoFragment.EXTRA_INFO_HAS_STATUS, true); 234 b.putBoolean(EXTRA_PREFERENCE_INFO_STATUS, 235 ((SliceSwitchPreference) preference).isChecked()); 236 } else { 237 b.putBoolean(InfoFragment.EXTRA_INFO_HAS_STATUS, false); 238 } 239 b.putCharSequence(EXTRA_PREFERENCE_INFO_TEXT, infoText); 240 if (preference.getTitle() != null 241 && !preference.getTitle().equals(infoText.toString())) { 242 fallbackInfoContentDescription += 243 CONTENT_DESCRIPTION_SEPARATOR + infoText.toString(); 244 } 245 246 } 247 if (infoSummary != null) { 248 b.putCharSequence(EXTRA_PREFERENCE_INFO_SUMMARY, infoSummary); 249 fallbackInfoContentDescription += 250 CONTENT_DESCRIPTION_SEPARATOR + infoSummary; 251 } 252 String contentDescription = getInfoContentDescription(item); 253 // Respect the content description values provided by slice. 254 // If not provided, for SlicePreference, SliceSwitchPreference, 255 // CustomContentDescriptionPreference, use the fallback value. 256 // Otherwise, do not set the contentDescription for preference. Rely on the talkback 257 // framework to generate the value itself. 258 if (!TextUtils.isEmpty(contentDescription)) { 259 if (preference instanceof HasCustomContentDescription) { 260 ((HasCustomContentDescription) preference).setContentDescription( 261 contentDescription); 262 } 263 } else { 264 if ((preference instanceof SlicePreference) 265 || (preference instanceof SliceSwitchPreference) 266 || (preference instanceof CustomContentDescriptionPreference)) { 267 ((HasCustomContentDescription) preference).setContentDescription( 268 fallbackInfoContentDescription); 269 } 270 } 271 if (infoImage != null || infoText != null || infoSummary != null) { 272 preference.setFragment(InfoFragment.class.getCanonicalName()); 273 } 274 } 275 276 return preference; 277 } 278 279 static class Data { 280 SliceItem mStartItem; 281 SliceItem mTitleItem; 282 SliceItem mSubtitleItem; 283 SliceItem mSummaryItem; 284 SliceItem mTargetSliceItem; 285 SliceItem mRadioGroupItem; 286 SliceItem mIntentItem; 287 SliceItem mFollowupIntentItem; 288 SliceItem mHasEndIconItem; 289 List<SliceItem> mEndItems = new ArrayList<>(); 290 List<SliceItem> mInfoItems = new ArrayList<>(); 291 } 292 extract(SliceItem sliceItem)293 static Data extract(SliceItem sliceItem) { 294 Data data = new Data(); 295 List<SliceItem> possibleStartItems = 296 SliceQuery.findAll(sliceItem, null, HINT_TITLE, null); 297 if (possibleStartItems.size() > 0) { 298 // The start item will be at position 0 if it exists 299 String format = possibleStartItems.get(0).getFormat(); 300 if ((FORMAT_ACTION.equals(format) 301 && SliceQuery.find(possibleStartItems.get(0), FORMAT_IMAGE) != null) 302 || FORMAT_SLICE.equals(format) 303 || FORMAT_LONG.equals(format) 304 || FORMAT_IMAGE.equals(format)) { 305 data.mStartItem = possibleStartItems.get(0); 306 } 307 } 308 309 List<SliceItem> items = sliceItem.getSlice().getItems(); 310 for (int i = 0; i < items.size(); i++) { 311 final SliceItem item = items.get(i); 312 String subType = item.getSubType(); 313 if (subType != null) { 314 switch (subType) { 315 case SlicesConstants.SUBTYPE_INFO_PREFERENCE : 316 data.mInfoItems.add(item); 317 break; 318 case SlicesConstants.SUBTYPE_INTENT : 319 data.mIntentItem = item; 320 break; 321 case SlicesConstants.SUBTYPE_FOLLOWUP_INTENT : 322 data.mFollowupIntentItem = item; 323 break; 324 case SlicesConstants.TAG_TARGET_URI : 325 data.mTargetSliceItem = item; 326 break; 327 case SlicesConstants.EXTRA_HAS_END_ICON: 328 data.mHasEndIconItem = item; 329 break; 330 } 331 } else if (FORMAT_TEXT.equals(item.getFormat()) && (item.getSubType() == null)) { 332 if ((data.mTitleItem == null || !data.mTitleItem.hasHint(HINT_TITLE)) 333 && item.hasHint(HINT_TITLE) && !item.hasHint(HINT_SUMMARY)) { 334 data.mTitleItem = item; 335 } else if (data.mSubtitleItem == null && !item.hasHint(HINT_SUMMARY)) { 336 data.mSubtitleItem = item; 337 } else if (data.mSummaryItem == null && item.hasHint(HINT_SUMMARY)) { 338 data.mSummaryItem = item; 339 } 340 } else { 341 data.mEndItems.add(item); 342 } 343 } 344 data.mEndItems.remove(data.mStartItem); 345 return data; 346 } 347 getInfoList(List<SliceItem> sliceItems)348 private static List<Pair<CharSequence, CharSequence>> getInfoList(List<SliceItem> sliceItems) { 349 List<Pair<CharSequence, CharSequence>> infoList = new ArrayList<>(); 350 for (SliceItem item : sliceItems) { 351 Slice itemSlice = item.getSlice(); 352 if (itemSlice != null) { 353 CharSequence title = null; 354 CharSequence summary = null; 355 for (SliceItem element : itemSlice.getItems()) { 356 if (element.getHints().contains(HINT_TITLE)) { 357 title = element.getText(); 358 } else if (element.getHints().contains(HINT_SUMMARY)) { 359 summary = element.getText(); 360 } 361 } 362 infoList.add(new Pair<CharSequence, CharSequence>(title, summary)); 363 } 364 } 365 return infoList; 366 } 367 getKey(SliceItem item)368 private static CharSequence getKey(SliceItem item) { 369 SliceItem target = SliceQuery.findSubtype(item, FORMAT_TEXT, SlicesConstants.TAG_KEY); 370 return target != null ? target.getText() : null; 371 } 372 getRadioGroup(SliceItem item)373 private static CharSequence getRadioGroup(SliceItem item) { 374 SliceItem target = SliceQuery.findSubtype( 375 item, FORMAT_TEXT, SlicesConstants.TAG_RADIO_GROUP); 376 return target != null ? target.getText() : null; 377 } 378 379 /** 380 * Get the screen title item for the slice. 381 * @param sliceItems list of SliceItem extracted from slice data. 382 * @return screen title item. 383 */ getScreenTitleItem(List<SliceContent> sliceItems)384 static SliceItem getScreenTitleItem(List<SliceContent> sliceItems) { 385 for (SliceContent contentItem : sliceItems) { 386 SliceItem item = contentItem.getSliceItem(); 387 if (item.getSubType() != null 388 && item.getSubType().equals(SlicesConstants.TYPE_PREFERENCE_SCREEN_TITLE)) { 389 return item; 390 } 391 } 392 return null; 393 } 394 getRedirectSlice(List<SliceContent> sliceItems)395 static SliceItem getRedirectSlice(List<SliceContent> sliceItems) { 396 for (SliceContent contentItem : sliceItems) { 397 SliceItem item = contentItem.getSliceItem(); 398 if (item.getSubType() != null 399 && item.getSubType().equals(SlicesConstants.TYPE_REDIRECTED_SLICE_URI)) { 400 return item; 401 } 402 } 403 return null; 404 } 405 getFocusedPreferenceItem(List<SliceContent> sliceItems)406 static SliceItem getFocusedPreferenceItem(List<SliceContent> sliceItems) { 407 for (SliceContent contentItem : sliceItems) { 408 SliceItem item = contentItem.getSliceItem(); 409 if (item.getSubType() != null 410 && item.getSubType().equals(SlicesConstants.TYPE_FOCUSED_PREFERENCE)) { 411 return item; 412 } 413 } 414 return null; 415 } 416 getEmbeddedItem(List<SliceContent> sliceItems)417 static SliceItem getEmbeddedItem(List<SliceContent> sliceItems) { 418 for (SliceContent contentItem : sliceItems) { 419 SliceItem item = contentItem.getSliceItem(); 420 if (item.getSubType() != null 421 && item.getSubType().equals(SlicesConstants.TYPE_PREFERENCE_EMBEDDED)) { 422 return item; 423 } 424 } 425 return null; 426 } 427 isIconNeedsToBeProcessed(SliceItem sliceItem)428 private static boolean isIconNeedsToBeProcessed(SliceItem sliceItem) { 429 List<SliceItem> items = sliceItem.getSlice().getItems(); 430 for (SliceItem item : items) { 431 if (item.getSubType() != null && item.getSubType().equals( 432 SlicesConstants.SUBTYPE_ICON_NEED_TO_BE_PROCESSED)) { 433 return item.getInt() == 1; 434 } 435 } 436 return false; 437 } 438 getButtonStyle(SliceItem sliceItem)439 private static int getButtonStyle(SliceItem sliceItem) { 440 List<SliceItem> items = sliceItem.getSlice().getItems(); 441 for (SliceItem item : items) { 442 if (item.getSubType() != null 443 && item.getSubType().equals(SlicesConstants.SUBTYPE_BUTTON_STYLE)) { 444 return item.getInt(); 445 } 446 } 447 return -1; 448 } 449 getSeekbarMin(SliceItem sliceItem)450 private static int getSeekbarMin(SliceItem sliceItem) { 451 List<SliceItem> items = sliceItem.getSlice().getItems(); 452 for (SliceItem item : items) { 453 if (item.getSubType() != null 454 && item.getSubType().equals(SlicesConstants.SUBTYPE_SEEKBAR_MIN)) { 455 return item.getInt(); 456 } 457 } 458 return -1; 459 } 460 getSeekbarMax(SliceItem sliceItem)461 private static int getSeekbarMax(SliceItem sliceItem) { 462 List<SliceItem> items = sliceItem.getSlice().getItems(); 463 for (SliceItem item : items) { 464 if (item.getSubType() != null 465 && item.getSubType().equals(SlicesConstants.SUBTYPE_SEEKBAR_MAX)) { 466 return item.getInt(); 467 } 468 } 469 return -1; 470 } 471 getSeekbarValue(SliceItem sliceItem)472 private static int getSeekbarValue(SliceItem sliceItem) { 473 List<SliceItem> items = sliceItem.getSlice().getItems(); 474 for (SliceItem item : items) { 475 if (item.getSubType() != null 476 && item.getSubType().equals(SlicesConstants.SUBTYPE_SEEKBAR_VALUE)) { 477 return item.getInt(); 478 } 479 } 480 return -1; 481 } 482 enabled(SliceItem sliceItem)483 private static boolean enabled(SliceItem sliceItem) { 484 List<SliceItem> items = sliceItem.getSlice().getItems(); 485 for (SliceItem item : items) { 486 if (item.getSubType() != null 487 && item.getSubType().equals(SlicesConstants.SUBTYPE_IS_ENABLED)) { 488 return item.getInt() == 1; 489 } 490 } 491 return true; 492 } 493 selectable(SliceItem sliceItem)494 private static boolean selectable(SliceItem sliceItem) { 495 List<SliceItem> items = sliceItem.getSlice().getItems(); 496 for (SliceItem item : items) { 497 if (item.getSubType() != null 498 && item.getSubType().equals(SlicesConstants.SUBTYPE_IS_SELECTABLE)) { 499 return item.getInt() == 1; 500 } 501 } 502 return true; 503 } 504 addInfoStatus(SliceItem sliceItem)505 private static boolean addInfoStatus(SliceItem sliceItem) { 506 List<SliceItem> items = sliceItem.getSlice().getItems(); 507 for (SliceItem item : items) { 508 if (item.getSubType() != null 509 && item.getSubType().equals(EXTRA_ADD_INFO_STATUS)) { 510 return item.getInt() == 1; 511 } 512 } 513 return true; 514 } 515 hasEndIcon(SliceItem item)516 private static boolean hasEndIcon(SliceItem item) { 517 return item != null && item.getInt() > 0; 518 } 519 520 /** 521 * Checks if custom content description should be forced to be used if provided. This function 522 * can be extended with more cases if needed. 523 * 524 * @param item The {@link SliceItem} containing the necessary information. 525 * @return <code>true</code> if custom content description should be used. 526 */ shouldForceContentDescription(SliceItem sliceItem)527 private static boolean shouldForceContentDescription(SliceItem sliceItem) { 528 List<SliceItem> items = sliceItem.getSlice().getItems(); 529 for (SliceItem item : items) { 530 // Checks if an end icon has been set. 531 if (item.getSubType() != null 532 && item.getSubType().equals(SlicesConstants.EXTRA_HAS_END_ICON)) { 533 return hasEndIcon(item); 534 } 535 } 536 return false; 537 } 538 539 /** 540 * Get the text from the SliceItem. 541 */ getText(SliceItem item)542 static CharSequence getText(SliceItem item) { 543 if (item == null) { 544 return null; 545 } 546 return item.getText(); 547 } 548 549 /** Get the icon from the SliceItem if available */ getIcon(SliceItem startItem)550 static Icon getIcon(SliceItem startItem) { 551 if (startItem != null && startItem.getSlice() != null 552 && startItem.getSlice().getItems() != null 553 && startItem.getSlice().getItems().size() > 0) { 554 SliceItem iconItem = startItem.getSlice().getItems().get(0); 555 if (FORMAT_IMAGE.equals(iconItem.getFormat())) { 556 IconCompat icon = iconItem.getIcon(); 557 return icon.toIcon(); 558 } 559 } 560 return null; 561 } 562 getStatusPath(String uriString)563 static Uri getStatusPath(String uriString) { 564 Uri statusUri = Uri.parse(uriString) 565 .buildUpon().path("/" + SlicesConstants.PATH_STATUS).build(); 566 return statusUri; 567 } 568 getPageId(SliceItem item)569 static int getPageId(SliceItem item) { 570 SliceItem target = SliceQuery.findSubtype(item, FORMAT_INT, EXTRA_PAGE_ID); 571 return target != null ? target.getInt() : 0; 572 } 573 getActionId(SliceItem item)574 private static int getActionId(SliceItem item) { 575 SliceItem target = SliceQuery.findSubtype(item, FORMAT_INT, EXTRA_ACTION_ID); 576 return target != null ? target.getInt() : 0; 577 } 578 579 getInfoText(SliceItem item)580 private static CharSequence getInfoText(SliceItem item) { 581 SliceItem target = SliceQuery.findSubtype(item, FORMAT_TEXT, EXTRA_PREFERENCE_INFO_TEXT); 582 return target != null ? target.getText() : null; 583 } 584 getInfoSummary(SliceItem item)585 private static CharSequence getInfoSummary(SliceItem item) { 586 SliceItem target = SliceQuery.findSubtype(item, FORMAT_TEXT, EXTRA_PREFERENCE_INFO_SUMMARY); 587 return target != null ? target.getText() : null; 588 } 589 getInfoImage(SliceItem item)590 private static IconCompat getInfoImage(SliceItem item) { 591 SliceItem target = SliceQuery.findSubtype(item, FORMAT_IMAGE, EXTRA_PREFERENCE_INFO_IMAGE); 592 return target != null ? target.getIcon() : null; 593 } 594 getInfoTitleIcon(SliceItem item)595 private static IconCompat getInfoTitleIcon(SliceItem item) { 596 SliceItem target = SliceQuery.findSubtype( 597 item, FORMAT_IMAGE, EXTRA_PREFERENCE_INFO_TITLE_ICON); 598 return target != null ? target.getIcon() : null; 599 } 600 601 /** 602 * Get the content description from SliceItem if available 603 */ getInfoContentDescription( SliceItem sliceItem)604 private static String getInfoContentDescription( 605 SliceItem sliceItem) { 606 List<SliceItem> items = sliceItem.getSlice().getItems(); 607 for (SliceItem item : items) { 608 if (item.getSubType() != null 609 && item.getSubType().equals(SUBTYPE_CONTENT_DESCRIPTION)) { 610 return item.getText().toString(); 611 } 612 } 613 return null; 614 } 615 } 616