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.builders; 18 19 import static com.android.tv.twopanelsettings.slices.SlicesConstants.BUTTONSTYLE; 20 import static com.android.tv.twopanelsettings.slices.SlicesConstants.CHECKMARK; 21 import static com.android.tv.twopanelsettings.slices.SlicesConstants.RADIO; 22 import static com.android.tv.twopanelsettings.slices.SlicesConstants.SEEKBAR; 23 import static com.android.tv.twopanelsettings.slices.SlicesConstants.SWITCH; 24 25 import android.app.PendingIntent; 26 import android.content.Context; 27 import android.net.Uri; 28 import android.view.View; 29 30 import androidx.annotation.IntDef; 31 import androidx.annotation.NonNull; 32 import androidx.annotation.Nullable; 33 import androidx.annotation.RestrictTo; 34 import androidx.core.graphics.drawable.IconCompat; 35 import androidx.core.util.Pair; 36 import androidx.slice.Slice; 37 import androidx.slice.SliceSpecs; 38 import androidx.slice.builders.ListBuilder; 39 import androidx.slice.builders.SliceAction; 40 import androidx.slice.core.SliceHints; 41 42 import java.lang.annotation.Retention; 43 import java.lang.annotation.RetentionPolicy; 44 import java.time.Duration; 45 import java.util.ArrayList; 46 import java.util.List; 47 48 // TODO: Remove unused code and add test. 49 /** 50 * Builder for constructing slices composed of rows of TvSettings style preferences. 51 */ 52 public class PreferenceSliceBuilder extends TemplateSliceBuilder { 53 54 private PreferenceSliceBuilderImpl mImpl; 55 56 /** 57 * Constant representing infinity. 58 */ 59 public static final long INFINITY = SliceHints.INFINITY; 60 61 /** 62 * @hide 63 */ 64 @RestrictTo(RestrictTo.Scope.LIBRARY) 65 @IntDef({ 66 View.LAYOUT_DIRECTION_RTL, View.LAYOUT_DIRECTION_LTR, View.LAYOUT_DIRECTION_INHERIT, 67 View.LAYOUT_DIRECTION_LOCALE 68 }) 69 @Retention(RetentionPolicy.SOURCE) 70 public @interface LayoutDirection { 71 72 } 73 74 /** 75 * Create a builder which will construct a slice made up of rows of content. 76 * 77 * @param uri Uri to tag for this slice. 78 * @hide 79 */ PreferenceSliceBuilder(@onNull Context context, @NonNull Uri uri)80 public PreferenceSliceBuilder(@NonNull Context context, @NonNull Uri uri) { 81 super(context, uri); 82 } 83 84 /** 85 * Create a ListBuilder for constructing slice content. 86 * <p> 87 * A slice requires an associated time-to-live, i.e. how long the data contained in the slice 88 * can remain fresh. If your slice has content that is not time sensitive, set a TTL of {@link 89 * #INFINITY}. 90 * 91 * @param uri Uri to tag for this slice. 92 * @param ttl the length in milliseconds that the content in this slice can live for. 93 */ PreferenceSliceBuilder(@onNull Context context, @NonNull Uri uri, long ttl)94 public PreferenceSliceBuilder(@NonNull Context context, @NonNull Uri uri, long ttl) { 95 super(context, uri); 96 mImpl.setTtl(ttl); 97 } 98 99 /** 100 * Create a ListBuilder for constructing slice content. 101 * <p> 102 * A slice requires an associated time-to-live, i.e. how long the data contained in the slice 103 * can remain fresh. If your slice has content that is not time sensitive, set {@link Duration} 104 * to null and the TTL will be {@link #INFINITY}. 105 * 106 * @param uri Uri to tag for this slice. 107 * @param ttl the {@link Duration} that the content in this slice can live for. 108 */ PreferenceSliceBuilder(@onNull Context context, @NonNull Uri uri, @Nullable Duration ttl)109 public PreferenceSliceBuilder(@NonNull Context context, @NonNull Uri uri, 110 @Nullable Duration ttl) { 111 super(context, uri); 112 mImpl.setTtl(ttl); 113 } 114 115 @Override selectImpl(Uri uri)116 protected TemplateBuilderImpl selectImpl(Uri uri) { 117 return new PreferenceSliceBuilderImpl(getBuilder(), SliceSpecs.LIST, getClock()); 118 } 119 120 /** 121 * Construct the slice defined by this PreferenceSliceBuilder 122 */ 123 @NonNull 124 @Override build()125 public Slice build() { 126 return mImpl.build(); 127 } 128 129 @Override setImpl(TemplateBuilderImpl impl)130 void setImpl(TemplateBuilderImpl impl) { 131 mImpl = (PreferenceSliceBuilderImpl) impl; 132 } 133 134 /** 135 * Add a preference builder. If the row is expected to be actionable, it is recommended to 136 * invoke setActionId() for the RowBuilder to help with logging. 137 */ addPreference(RowBuilder builder)138 public PreferenceSliceBuilder addPreference(RowBuilder builder) { 139 mImpl.addPreference(builder); 140 return this; 141 } 142 143 /** 144 * Add a preferenceCategory builder. 145 */ addPreferenceCategory(RowBuilder builder)146 public PreferenceSliceBuilder addPreferenceCategory(RowBuilder builder) { 147 mImpl.addPreferenceCategory(builder); 148 return this; 149 } 150 151 /** 152 * Set a custom screen title for slice. It is recommended to invoke setPageId() for the 153 * RowBuilder in addition to setTitle(), to help with logging. 154 */ addScreenTitle(RowBuilder builder)155 public PreferenceSliceBuilder addScreenTitle(RowBuilder builder) { 156 mImpl.addScreenTitle(builder); 157 return this; 158 } 159 160 /** 161 * Add an embedded preference placeholder where another slice can control the properties via 162 * setEmbeddedPreference. 163 */ addEmbeddedPreference(RowBuilder builder)164 public PreferenceSliceBuilder addEmbeddedPreference(RowBuilder builder) { 165 mImpl.addEmbeddedPreference(builder); 166 return this; 167 } 168 169 /** 170 * Set the focused preference for slice. 171 * @param key key of the focused preference. 172 */ setFocusedPreference(CharSequence key)173 public PreferenceSliceBuilder setFocusedPreference(CharSequence key) { 174 mImpl.setFocusedPreference(key); 175 return this; 176 } 177 178 /** Add a preference which can be embedded in other settings items. **/ setEmbeddedPreference(RowBuilder builder)179 public PreferenceSliceBuilder setEmbeddedPreference(RowBuilder builder) { 180 mImpl.setEmbeddedPreference(builder); 181 return this; 182 } 183 184 /** Indicates that the slice is not ready yet **/ setNotReady()185 public PreferenceSliceBuilder setNotReady() { 186 mImpl.setNotReady(); 187 return this; 188 } 189 190 191 /** 192 * Set the redirected slice uri. Settings would render the slice from the redirected slice uri. 193 * @param redirectedSliceUri the redirected slice uri. 194 */ 195 @NonNull setRedirectedSliceUri(@onNull CharSequence redirectedSliceUri)196 public PreferenceSliceBuilder setRedirectedSliceUri(@NonNull CharSequence redirectedSliceUri) { 197 mImpl.setRedirectedSliceUri(redirectedSliceUri); 198 return this; 199 } 200 201 public static class RowBuilder { 202 203 private final Uri mUri; 204 private boolean mHasEndActionOrToggle; 205 private boolean mHasEndImage; 206 private boolean mHasDefaultToggle; 207 private boolean mHasEndIcon; 208 private boolean mTitleItemLoading; 209 private IconCompat mTitleIcon; 210 private SliceAction mTitleAction; 211 private SliceAction mPrimaryAction; 212 private SliceAction mFollowupAction; 213 private int mActionId; 214 private int mPageId; 215 private CharSequence mTitle; 216 private boolean mTitleLoading; 217 private CharSequence mSubtitle; 218 private boolean mSubtitleLoading; 219 private CharSequence mContentDescription; 220 private CharSequence mInfoTitle; 221 private CharSequence mInfoSummary; 222 private IconCompat mInfoImage; 223 private IconCompat mInfoTitleIcon; 224 private int mLayoutDirection = -1; 225 private List<Object> mEndItems = new ArrayList<>(); 226 private List<Integer> mEndTypes = new ArrayList<>(); 227 private List<Boolean> mEndLoads = new ArrayList<>(); 228 private List<Pair<String, String>> mInfoItems = new ArrayList<>(); 229 private boolean mTitleActionLoading; 230 private CharSequence mTargetSliceUri; 231 private CharSequence mKey; 232 private boolean mIconNeedsToBeProcessed; 233 private @BUTTONSTYLE int mButtonStyle; 234 private int mSeekbarMin; 235 private int mSeekbarMax; 236 private int mSeekbarValue; 237 private CharSequence mRadioGroup; 238 private boolean mEnabled; 239 private boolean mSelectable; 240 private boolean mAddInfoStatus; 241 private CharSequence mRedirectSliceUri; 242 243 public static final int TYPE_ICON = 1; 244 public static final int TYPE_ACTION = 2; 245 246 /** 247 * Builder to construct a row. 248 */ RowBuilder()249 public RowBuilder() { 250 mEnabled = true; 251 mSelectable = true; 252 mUri = null; 253 } 254 255 /** 256 * Builder to construct a normal row. 257 * 258 * @param uri Uri to tag for this slice. 259 */ RowBuilder(Uri uri)260 public RowBuilder(Uri uri) { 261 mEnabled = true; 262 mSelectable = true; 263 mUri = uri; 264 } 265 266 /** 267 * Builder to construct a row. 268 * 269 * @param parent The builder constructing the parent slice. 270 */ RowBuilder(@onNull ListBuilder parent)271 public RowBuilder(@NonNull ListBuilder parent) { 272 this(); 273 } 274 275 /** 276 * Builder to construct a row. 277 * 278 * @param uri Uri to tag for this slice. 279 */ RowBuilder(@onNull ListBuilder parent, @NonNull Uri uri)280 public RowBuilder(@NonNull ListBuilder parent, @NonNull Uri uri) { 281 this(uri); 282 } 283 284 /** 285 * Builder to construct a normal row. 286 * 287 * @param uri Uri to tag for this slice. 288 */ RowBuilder(@onNull Context context, @NonNull Uri uri)289 public RowBuilder(@NonNull Context context, @NonNull Uri uri) { 290 this(uri); 291 } 292 293 /** 294 * Sets the title item to be the provided icon. There can only be one title item, this will 295 * replace any other title items that may have been set. 296 * 297 * @param icon the image to display. 298 */ setTitleItem(@onNull IconCompat icon)299 private RowBuilder setTitleItem(@NonNull IconCompat icon) { 300 return setTitleItem(icon, false /* isLoading */); 301 } 302 303 /** 304 * Sets the title item to be the provided icon. There can only be one title item, this will 305 * replace any other title items that may have been set. 306 * <p> 307 * When set to true, the parameter {@code isLoading} indicates that the app is doing work to 308 * load this content in the background, in this case the template displays a placeholder 309 * until updated. 310 * 311 * @param icon the image to display. 312 * @param isLoading whether this content is being loaded in the background. 313 */ 314 @NonNull setTitleItem(@ullable IconCompat icon, boolean isLoading)315 private RowBuilder setTitleItem(@Nullable IconCompat icon, boolean isLoading) { 316 mTitleAction = null; 317 mTitleIcon = icon; 318 mTitleItemLoading = isLoading; 319 return this; 320 } 321 322 /** 323 * Sets the title item to be a tappable icon. There can only be one title item, this will 324 * replace any other title items that may have been set. 325 */ 326 @NonNull setTitleItem(@onNull SliceAction action)327 private RowBuilder setTitleItem(@NonNull SliceAction action) { 328 return setTitleItem(action, false /* isLoading */); 329 } 330 331 /** 332 * Sets the title item to be a tappable icon. There can only be one title item, this will 333 * replace any other title items that may have been set. 334 * <p> 335 * Use this method to specify content that will appear in the template once it's been 336 * loaded. 337 * </p> 338 * 339 * @param isLoading indicates whether the app is doing work to load the added content in the 340 * background or not. 341 */ 342 @NonNull setTitleItem(@onNull SliceAction action, boolean isLoading)343 private RowBuilder setTitleItem(@NonNull SliceAction action, boolean isLoading) { 344 mTitleAction = action; 345 mTitleIcon = null; 346 mTitleActionLoading = isLoading; 347 return this; 348 } 349 350 /** 351 * Sets the icon for the preference builder. There can only be one icon, 352 * this will replace any other icon that may have been set. 353 * This means that you should only provide an icon using one of the two functions 354 * {@link #setIcon(IconCompat)} or {@link #setEndIcon(IconCompat)}. 355 * 356 * @param icon the image to display. 357 */ 358 @NonNull setIcon(@onNull IconCompat icon)359 public RowBuilder setIcon(@NonNull IconCompat icon) { 360 mHasEndIcon = false; 361 return setTitleItem(icon); 362 } 363 364 /** 365 * Sets the icon to the end for the preference builder. There can only be one icon, 366 * this will replace any other icon that may have been set. 367 * This means that you should only provide an icon using one of the two functions 368 * {@link #setIcon(IconCompat)} or {@link #setEndIcon(IconCompat)}. 369 * 370 * @param icon the image to display. 371 */ 372 @NonNull setEndIcon(@onNull IconCompat icon)373 public RowBuilder setEndIcon(@NonNull IconCompat icon) { 374 mHasEndIcon = true; 375 return setTitleItem(icon); 376 } 377 378 /** 379 * Set the information image for the preference builder. 380 * The image would be displayed at the top of preview screen. 381 */ 382 @NonNull setInfoImage(@onNull IconCompat icon)383 public RowBuilder setInfoImage(@NonNull IconCompat icon) { 384 mInfoImage = icon; 385 return this; 386 } 387 388 /** 389 * Set the information title icon for the preference builder. 390 * The icon will be displayed to the left of description text title 391 */ 392 @NonNull setInfoTitleIcon(@onNull IconCompat icon)393 public RowBuilder setInfoTitleIcon(@NonNull IconCompat icon) { 394 mInfoTitleIcon = icon; 395 return this; 396 } 397 398 /** 399 * Set the information text for the preference builder. 400 */ 401 @Deprecated 402 @NonNull setInfoText(CharSequence text)403 public RowBuilder setInfoText(CharSequence text) { 404 mInfoTitle = text; 405 return this; 406 } 407 408 /** 409 * Set the information text title for the preference builder. 410 * 411 * It is strongly recommended to also invoke setContentDescription() for a11y 412 * purposes. Please see setContentDescription() for more details. 413 */ 414 @NonNull setInfoTitle(CharSequence text)415 public RowBuilder setInfoTitle(CharSequence text) { 416 mInfoTitle = text; 417 return this; 418 } 419 420 /** 421 * Set the information text summary for the preference builder. 422 * 423 * It is strongly recommended to also invoke setContentDescription() for a11y 424 * purposes. Please see setContentDescription() for more details. 425 */ 426 @NonNull setInfoSummary(CharSequence text)427 public RowBuilder setInfoSummary(CharSequence text) { 428 mInfoSummary = text; 429 return this; 430 } 431 432 /** 433 * Set whether need to add info status. If set true, info status would be automatically 434 * generated based upon the on/off status of the switch. 435 */ 436 @NonNull setAddInfoStatus(boolean addInfoStatus)437 public RowBuilder setAddInfoStatus(boolean addInfoStatus) { 438 mAddInfoStatus = addInfoStatus; 439 return this; 440 } 441 442 /** 443 * The action specified here will be sent when the whole row is clicked. 444 * <p> 445 * If this is the first row in a {@link ListBuilder} this action will also be used to define 446 * the {@link androidx.slice.widget.SliceView#MODE_SHORTCUT} representation of the slice. 447 */ 448 @NonNull setPrimaryAction(@onNull SliceAction action)449 private RowBuilder setPrimaryAction(@NonNull SliceAction action) { 450 mPrimaryAction = action; 451 return this; 452 } 453 454 /** 455 * Set a pendingIntent for the preference builder. 456 * @param pendingIntent pendingIntent 457 * @return builder 458 */ 459 @NonNull setPendingIntent(@onNull PendingIntent pendingIntent)460 public RowBuilder setPendingIntent(@NonNull PendingIntent pendingIntent) { 461 return setPrimaryAction(new SliceAction(pendingIntent, "", false)); 462 } 463 464 /** 465 * Set a followup pendingIntent for the preference builder. After the initial pendingIntent 466 * is launched and result is retrieved by TvSettings, TvSettings will pack the result into 467 * the followup PendingIntent and launch it. 468 * @param pendingIntent followup pendingIntent 469 * @return builder 470 */ 471 @NonNull setFollowupPendingIntent(@onNull PendingIntent pendingIntent)472 public RowBuilder setFollowupPendingIntent(@NonNull PendingIntent pendingIntent) { 473 mFollowupAction = new SliceAction(pendingIntent, "", false); 474 return this; 475 } 476 477 /** 478 * Set the action ID for the row builder. If this is invoked for building an actionable row, 479 * it will be digested as actionId for logging purpose when the action is triggered. 480 * 481 * @param actionId pre-defined ID of an action 482 */ 483 @NonNull setActionId(int actionId)484 public RowBuilder setActionId(int actionId) { 485 mActionId = actionId; 486 return this; 487 } 488 489 /** 490 * Set the page ID for the row builder. If this is invoked for building the screen title 491 * row, it will be digested as pageId for logging purpose when the SliceFragment is focused. 492 * 493 * @param pageId pre-defined ID of a page 494 */ 495 @NonNull setPageId(int pageId)496 public RowBuilder setPageId(int pageId) { 497 mPageId = pageId; 498 return this; 499 } 500 501 /** 502 * Sets the title for the row builder. A title should fit on a single line and is ellipsized 503 * if too long. 504 */ 505 @NonNull setTitle(@onNull CharSequence title)506 public RowBuilder setTitle(@NonNull CharSequence title) { 507 return setTitle(title, false); 508 } 509 510 /** 511 * Sets the title for the row builder. A title should fit on a single line and is ellipsized 512 * if too long. 513 * <p> 514 * Use this method to specify content that will appear in the template once it's been 515 * loaded. 516 * </p> 517 * 518 * @param isLoading indicates whether the app is doing work to load the added content in the 519 * background or not. 520 */ 521 @NonNull setTitle(@ullable CharSequence title, boolean isLoading)522 public RowBuilder setTitle(@Nullable CharSequence title, boolean isLoading) { 523 mTitle = title; 524 mTitleLoading = isLoading; 525 return this; 526 } 527 528 /** 529 * Sets the subtitle for the row builder. A subtitle should fit on a single line and is 530 * ellipsized if too long. 531 */ 532 @NonNull setSubtitle(@onNull CharSequence subtitle)533 public RowBuilder setSubtitle(@NonNull CharSequence subtitle) { 534 return setSubtitle(subtitle, false /* isLoading */); 535 } 536 537 /** 538 * Sets the subtitle for the row builder. A subtitle should fit on a single line and is 539 * ellipsized if too long. 540 * <p> 541 * Use this method to specify content that will appear in the template once it's been 542 * loaded. 543 * </p> 544 * 545 * @param isLoading indicates whether the app is doing work to load the added content in the 546 * background or not. 547 */ 548 @NonNull setSubtitle(@ullable CharSequence subtitle, boolean isLoading)549 public RowBuilder setSubtitle(@Nullable CharSequence subtitle, boolean isLoading) { 550 mSubtitle = subtitle; 551 mSubtitleLoading = isLoading; 552 return this; 553 } 554 555 /** 556 * Adds an icon to the end items of the row builder. 557 * 558 * @param icon the image to display. 559 */ 560 @NonNull addEndItem(@onNull IconCompat icon)561 private RowBuilder addEndItem(@NonNull IconCompat icon) { 562 return addEndItem(icon, false /* isLoading */); 563 } 564 565 /** 566 * Adds an icon to the end items of the row builder. 567 * <p> 568 * When set to true, the parameter {@code isLoading} indicates that the app is doing work to 569 * load this content in the background, in this case the template displays a placeholder 570 * until updated. 571 * 572 * @param icon the image to display. 573 * @param isLoading whether this content is being loaded in the background. 574 */ 575 @NonNull addEndItem(@ullable IconCompat icon, boolean isLoading)576 private RowBuilder addEndItem(@Nullable IconCompat icon, boolean isLoading) { 577 if (mHasEndActionOrToggle) { 578 throw new IllegalArgumentException("Trying to add an icon to end items when an" 579 + "action has already been added. End items cannot have a mixture of " 580 + "actions and icons."); 581 } 582 mEndItems.add(new Pair<>(icon, 0)); 583 mEndTypes.add(TYPE_ICON); 584 mEndLoads.add(isLoading); 585 mHasEndImage = true; 586 return this; 587 } 588 589 /** 590 * Adds an action to the end items of the row builder. A mixture of icons and actions is not 591 * permitted. If an icon has already been added, this will throw {@link 592 * IllegalArgumentException}. 593 */ 594 @NonNull addEndItem(@onNull SliceAction action)595 private RowBuilder addEndItem(@NonNull SliceAction action) { 596 return addEndItem(action, false /* isLoading */); 597 } 598 599 600 /** 601 * Add an item to the RowBuilder. Each item would contain title and summary. 602 */ addInfoItem(String title, String summary)603 public RowBuilder addInfoItem(String title, String summary) { 604 mInfoItems.add(new Pair<>(title, summary)); 605 return this; 606 } 607 608 /** 609 * Add a radio button to the RowBuilder. 610 * @param pendingIntent pendingIntent to launch when radio is clicked. 611 * @param isChecked Initial state of the radio button 612 */ addRadioButton( PendingIntent pendingIntent, boolean isChecked)613 public RowBuilder addRadioButton( 614 PendingIntent pendingIntent, boolean isChecked) { 615 return addButton(pendingIntent, isChecked, RADIO); 616 } 617 618 /** 619 * Add a radio button to the RowBuilder. 620 * @param pendingIntent pendingIntent to launch when radio is clicked. 621 * @param isChecked Initial state of the radio button 622 * @param radioGroup group of the radio 623 */ addRadioButton( PendingIntent pendingIntent, boolean isChecked, CharSequence radioGroup)624 public RowBuilder addRadioButton( 625 PendingIntent pendingIntent, boolean isChecked, CharSequence radioGroup) { 626 return addButton(pendingIntent, isChecked, RADIO).setRadioGroup(radioGroup); 627 } 628 629 /** 630 * Add a checkmark to the RowBuilder. 631 * @param pendingIntent pendingIntent to launch when checkmark is clicked. 632 * @param isChecked Initial state of the check mark. 633 */ addCheckMark( PendingIntent pendingIntent, boolean isChecked)634 public RowBuilder addCheckMark( 635 PendingIntent pendingIntent, boolean isChecked) { 636 return addButton(pendingIntent, isChecked, CHECKMARK); 637 } 638 639 /** 640 * Add a switch to the RowBuilder. 641 * @param pendingIntent pendingIntent to launch when switch is clicked. 642 * @param isChecked Initial state of the switch. 643 */ addSwitch( PendingIntent pendingIntent, boolean isChecked)644 public RowBuilder addSwitch( 645 PendingIntent pendingIntent, boolean isChecked) { 646 return addButton(pendingIntent, isChecked, SWITCH); 647 } 648 addButton( PendingIntent pendingIntent, boolean isChecked, @BUTTONSTYLE int style)649 private RowBuilder addButton( 650 PendingIntent pendingIntent, boolean isChecked, @BUTTONSTYLE int style) { 651 SliceAction switchAction = new SliceAction(pendingIntent, "", isChecked); 652 mButtonStyle = style; 653 return addEndItem(switchAction); 654 } 655 656 /** 657 * Add a switch for the preference. 658 * @param pendingIntent pendingIntent 659 * @param actionTitle title for the switch, also used for contentDescription. 660 * @param isChecked the state of the switch 661 * @return 662 */ 663 @NonNull addSwitch( PendingIntent pendingIntent, @NonNull CharSequence actionTitle, boolean isChecked)664 public PreferenceSliceBuilder.RowBuilder addSwitch( 665 PendingIntent pendingIntent, @NonNull CharSequence actionTitle, boolean isChecked) { 666 SliceAction switchAction = new SliceAction(pendingIntent, actionTitle, isChecked); 667 mButtonStyle = SWITCH; 668 return addEndItem(switchAction); 669 } 670 addSeekBar( PendingIntent pendingIntent, int min, int max, int value)671 public PreferenceSliceBuilder.RowBuilder addSeekBar( 672 PendingIntent pendingIntent, int min, int max, int value) { 673 SliceAction seekbarAction = new SliceAction(pendingIntent, "", false); 674 mButtonStyle = SEEKBAR; 675 mSeekbarMin = min; 676 mSeekbarMax = max; 677 mSeekbarValue = value; 678 return addEndItem(seekbarAction); 679 } 680 681 /** 682 * Adds an action to the end items of the row builder. A mixture of icons and actions is not 683 * permitted. If an icon has already been added, this will throw {@link 684 * IllegalArgumentException}. 685 * <p> 686 * Use this method to specify content that will appear in the template once it's been 687 * loaded. 688 * </p> 689 * 690 * @param isLoading indicates whether the app is doing work to load the added content in the 691 * background or not. 692 */ 693 @NonNull addEndItem(@onNull SliceAction action, boolean isLoading)694 private RowBuilder addEndItem(@NonNull SliceAction action, boolean isLoading) { 695 if (mHasEndImage) { 696 throw new IllegalArgumentException("Trying to add an action to end items when an" 697 + "icon has already been added. End items cannot have a mixture of " 698 + "actions and icons."); 699 } 700 if (mHasDefaultToggle) { 701 throw new IllegalStateException("Only one non-custom toggle can be added " 702 + "in a single row. If you would like to include multiple toggles " 703 + "in a row, set a custom icon for each toggle."); 704 } 705 mEndItems.add(action); 706 mEndTypes.add(TYPE_ACTION); 707 mEndLoads.add(isLoading); 708 mHasDefaultToggle = action.getImpl().isDefaultToggle(); 709 mHasEndActionOrToggle = true; 710 return this; 711 } 712 713 /** 714 * Sets the content description for the row. 715 * 716 * Although TvSettings will try to construct the content description to its best extent 717 * if it's not set, it is strongly recommended to invoke this method with info items 718 * folded in the content description for the Roy for a11y purposes, as the info items 719 * may be unfocusable when talkback is on. 720 * 721 * By default, this method will assign the full info item title and summary to the 722 * content description if one is not specified. 723 */ 724 @NonNull setContentDescription(@onNull CharSequence description)725 public RowBuilder setContentDescription(@NonNull CharSequence description) { 726 mContentDescription = description; 727 return this; 728 } 729 730 /** 731 * Set the target slice uri for the builder. 732 * @param targetSliceUri indicates the target slice uri when the preference is focused. 733 * @return builder 734 */ setTargetSliceUri(@onNull CharSequence targetSliceUri)735 public RowBuilder setTargetSliceUri(@NonNull CharSequence targetSliceUri) { 736 mTargetSliceUri = targetSliceUri; 737 return this; 738 } 739 740 /** 741 * Set the key for the builder. 742 * @param key indicates the key for the preference. 743 * @return builder 744 */ setKey(@onNull CharSequence key)745 public RowBuilder setKey(@NonNull CharSequence key) { 746 mKey = key; 747 return this; 748 } 749 750 /** 751 * Sets the desired layout direction for the content in this row. 752 * 753 * @param layoutDirection the layout direction to set. 754 */ 755 @NonNull setLayoutDirection(@ayoutDirection int layoutDirection)756 public RowBuilder setLayoutDirection(@LayoutDirection int layoutDirection) { 757 mLayoutDirection = layoutDirection; 758 return this; 759 } 760 761 /** 762 * Set whether the toggle use a checkmark style. Otherwise, a switch style is used. 763 * @param isCheckMark use checkmark. 764 * @deprecated use {@link PreferenceSliceBuilder.RowBuilder#setButtonStyle(int)} 765 */ 766 @Deprecated 767 @NonNull setCheckmark(boolean isCheckMark)768 public RowBuilder setCheckmark(boolean isCheckMark) { 769 if (isCheckMark) { 770 mButtonStyle = CHECKMARK; 771 } else { 772 mButtonStyle = SWITCH; 773 } 774 return this; 775 } 776 777 /** 778 * Set the button style. 779 * @param buttonStyle 780 */ setButtonStyle(@UTTONSTYLE int buttonStyle)781 public RowBuilder setButtonStyle(@BUTTONSTYLE int buttonStyle) { 782 mButtonStyle = buttonStyle; 783 return this; 784 } 785 786 /** 787 * Set whether the icon needs to be processed by TvSettings. 788 * @param needed if true, TvSettings will add a round border around the given icon 789 */ 790 @NonNull setIconNeedsToBeProcessed(boolean needed)791 public RowBuilder setIconNeedsToBeProcessed(boolean needed) { 792 mIconNeedsToBeProcessed = needed; 793 return this; 794 } 795 796 /** 797 * Set radio group for radio 798 */ 799 @NonNull setRadioGroup(CharSequence radioGroup)800 public RowBuilder setRadioGroup(CharSequence radioGroup) { 801 mRadioGroup = radioGroup; 802 return this; 803 } 804 805 /** 806 * Set whether this item is enabled. 807 */ 808 @NonNull setEnabled(boolean enabled)809 public RowBuilder setEnabled(boolean enabled) { 810 mEnabled = enabled; 811 return this; 812 } 813 814 /** 815 * Set whether this item is selectable. 816 * @param selectable 817 */ 818 @NonNull setSelectable(boolean selectable)819 public RowBuilder setSelectable(boolean selectable) { 820 mSelectable = selectable; 821 return this; 822 } 823 824 /** 825 * 826 */ iconNeedsToBeProcessed()827 public boolean iconNeedsToBeProcessed() { 828 return mIconNeedsToBeProcessed; 829 } 830 831 /** 832 * 833 */ getButtonStyle()834 public int getButtonStyle() { 835 return mButtonStyle; 836 } 837 838 /** 839 * 840 */ getSeekbarMin()841 public int getSeekbarMin() { 842 return mSeekbarMin; 843 } 844 845 /** 846 * 847 */ getSeekbarMax()848 public int getSeekbarMax() { 849 return mSeekbarMax; 850 } 851 852 /** 853 * 854 */ getSeekbarValue()855 public int getSeekbarValue() { 856 return mSeekbarValue; 857 } 858 859 /** 860 * Get the target slice uri. 861 */ getTargetSliceUri()862 public CharSequence getTargetSliceUri() { 863 return mTargetSliceUri; 864 } 865 866 /** Get the key for the builder */ getKey()867 public CharSequence getKey() { 868 return mKey; 869 } 870 getUri()871 public Uri getUri() { 872 return mUri; 873 } 874 hasEndActionOrToggle()875 public boolean hasEndActionOrToggle() { 876 return mHasEndActionOrToggle; 877 } 878 hasEndImage()879 public boolean hasEndImage() { 880 return mHasEndImage; 881 } 882 hasDefaultToggle()883 public boolean hasDefaultToggle() { 884 return mHasDefaultToggle; 885 } 886 887 /** 888 * Checks if item has an end icon. 889 */ hasEndIcon()890 public boolean hasEndIcon() { 891 return mHasEndIcon; 892 } 893 getRadioGroup()894 public CharSequence getRadioGroup() { 895 return mRadioGroup; 896 } 897 isEnabled()898 public boolean isEnabled() { 899 return mEnabled; 900 } 901 isSelectable()902 public boolean isSelectable() { 903 return mSelectable; 904 } 905 addInfoStatus()906 public boolean addInfoStatus() { 907 return mAddInfoStatus; 908 } 909 isTitleItemLoading()910 public boolean isTitleItemLoading() { 911 return mTitleItemLoading; 912 } 913 getTitleIcon()914 public IconCompat getTitleIcon() { 915 return mTitleIcon; 916 } 917 getTitleAction()918 public SliceAction getTitleAction() { 919 return mTitleAction; 920 } 921 getPrimaryAction()922 public SliceAction getPrimaryAction() { 923 return mPrimaryAction; 924 } 925 getFollowupAction()926 public SliceAction getFollowupAction() { 927 return mFollowupAction; 928 } 929 getActionId()930 public int getActionId() { 931 return mActionId; 932 } 933 getPageId()934 public int getPageId() { 935 return mPageId; 936 } 937 getRedirectSliceUri()938 public CharSequence getRedirectSliceUri() { 939 return mRedirectSliceUri; 940 } 941 getTitle()942 public CharSequence getTitle() { 943 return mTitle; 944 } 945 isTitleLoading()946 public boolean isTitleLoading() { 947 return mTitleLoading; 948 } 949 getSubtitle()950 public CharSequence getSubtitle() { 951 return mSubtitle; 952 } 953 isSubtitleLoading()954 public boolean isSubtitleLoading() { 955 return mSubtitleLoading; 956 } 957 getContentDescription()958 public CharSequence getContentDescription() { 959 return mContentDescription; 960 } 961 getLayoutDirection()962 public int getLayoutDirection() { 963 return mLayoutDirection; 964 } 965 getInfoText()966 public CharSequence getInfoText() { 967 return mInfoTitle; 968 } 969 getInfoTitle()970 public CharSequence getInfoTitle() { 971 return mInfoTitle; 972 } 973 getInfoSummary()974 public CharSequence getInfoSummary() { 975 return mInfoSummary; 976 } 977 getInfoImage()978 public IconCompat getInfoImage() { 979 return mInfoImage; 980 } 981 getInfoTitleIcon()982 public IconCompat getInfoTitleIcon() { 983 return mInfoTitleIcon; 984 } 985 getEndItems()986 public List<Object> getEndItems() { 987 return mEndItems; 988 } 989 getInfoItems()990 public List<Pair<String, String>> getInfoItems() { 991 return mInfoItems; 992 } 993 getEndTypes()994 public List<Integer> getEndTypes() { 995 return mEndTypes; 996 } 997 getEndLoads()998 public List<Boolean> getEndLoads() { 999 return mEndLoads; 1000 } 1001 isTitleActionLoading()1002 public boolean isTitleActionLoading() { 1003 return mTitleActionLoading; 1004 } 1005 } 1006 } 1007