• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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