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