GuidedAction.java revision be6eb618b4ba8a74d69fa04c77c717b1fcbea818
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14package android.support.v17.leanback.widget;
15
16import android.support.v17.leanback.R;
17
18import android.content.Context;
19import android.content.Intent;
20import android.graphics.drawable.Drawable;
21import android.text.InputType;
22import android.util.Log;
23
24import java.util.List;
25
26/**
27 * A data class which represents an action within a {@link
28 * android.support.v17.leanback.app.GuidedStepFragment}. GuidedActions contain at minimum a title
29 * and a description, and typically also an icon.
30 * <p>
31 * A GuidedAction typically represents a single action a user may take, but may also represent a
32 * possible choice out of a group of mutually exclusive choices (similar to radio buttons), or an
33 * information-only label (in which case the item cannot be clicked).
34 * <p>
35 * GuidedActions may optionally be checked. They may also indicate that they will request further
36 * user input on selection, in which case they will be displayed with a chevron indicator.
37 */
38public class GuidedAction extends Action {
39
40    private static final String TAG = "GuidedAction";
41
42    /**
43     * Special check set Id that is neither checkbox nor radio.
44     */
45    public static final int NO_CHECK_SET = 0;
46    /**
47     * Default checkset Id for radio.
48     */
49    public static final int DEFAULT_CHECK_SET_ID = 1;
50    /**
51     * Checkset Id for checkbox.
52     */
53    public static final int CHECKBOX_CHECK_SET_ID = -1;
54
55    /**
56     * When finishing editing, goes to next action.
57     */
58    public static final long ACTION_ID_NEXT = -2;
59    /**
60     * When finishing editing, stay on current action.
61     */
62    public static final long ACTION_ID_CURRENT = -3;
63
64    /**
65     * Id of standard OK action.
66     */
67    public static final long ACTION_ID_OK = -4;
68
69    /**
70     * Id of standard Cancel action.
71     */
72    public static final long ACTION_ID_CANCEL = -5;
73
74    /**
75     * Id of standard Finish action.
76     */
77    public static final long ACTION_ID_FINISH = -6;
78
79    /**
80     * Id of standard Finish action.
81     */
82    public static final long ACTION_ID_CONTINUE = -7;
83
84    /**
85     * Id of standard Yes action.
86     */
87    public static final long ACTION_ID_YES = -8;
88
89    /**
90     * Id of standard No action.
91     */
92    public static final long ACTION_ID_NO = -9;
93
94    /**
95     * Builds a {@link GuidedAction} object.  When subclass GuidedAction, you may override this
96     * Builder class and call {@link #applyValues(GuidedAction)}.
97     */
98    public static class Builder {
99        private long mId;
100        private CharSequence mTitle;
101        private CharSequence mEditTitle;
102        private CharSequence mDescription;
103        private CharSequence mEditDescription;
104        private Drawable mIcon;
105        private boolean mChecked;
106        private boolean mMultilineDescription;
107        private boolean mHasNext;
108        private boolean mInfoOnly;
109        private boolean mEditable = false;
110        private boolean mDescriptionEditable = false;
111        private int mInputType = InputType.TYPE_CLASS_TEXT;
112        private int mDescriptionInputType = InputType.TYPE_CLASS_TEXT;
113        private int mEditInputType = InputType.TYPE_CLASS_TEXT;
114        private int mDescriptionEditInputType = InputType.TYPE_CLASS_TEXT;
115        private int mCheckSetId = NO_CHECK_SET;
116        private boolean mEnabled = true;
117        private boolean mFocusable = true;
118        private List<GuidedAction> mSubActions;
119        private Intent mIntent;
120
121        /**
122         * Builds the GuidedAction corresponding to this Builder.
123         * @return the GuidedAction as configured through this Builder.
124         */
125        public final GuidedAction build() {
126            GuidedAction action = new GuidedAction();
127            applyValues(action);
128            return action;
129        }
130
131        /**
132         * Subclass Builder may call this function to apply values.
133         * @param action GuidedAction to apply Builder values.
134         */
135        protected final void applyValues(GuidedAction action) {
136            // Base Action values
137            action.setId(mId);
138            action.setLabel1(mTitle);
139            action.setEditTitle(mEditTitle);
140            action.setLabel2(mDescription);
141            action.setEditDescription(mEditDescription);
142            action.setIcon(mIcon);
143
144            // Subclass values
145            action.mIntent = mIntent;
146            action.mEditable = mEditable;
147            action.mDescriptionEditable = mDescriptionEditable;
148            action.mInputType = mInputType;
149            action.mDescriptionInputType = mDescriptionInputType;
150            action.mEditInputType = mEditInputType;
151            action.mDescriptionEditInputType = mDescriptionEditInputType;
152            action.mChecked = mChecked;
153            action.mCheckSetId = mCheckSetId;
154            action.mMultilineDescription = mMultilineDescription;
155            action.mHasNext = mHasNext;
156            action.mInfoOnly = mInfoOnly;
157            action.mEnabled = mEnabled;
158            action.mFocusable = mFocusable;
159            action.mSubActions = mSubActions;
160        }
161
162        /**
163         * Construct a standard "OK" action with {@link GuidedAction#ACTION_ID_OK}.
164         * @param context Context for loading action title.
165         * @return The same Builder object.
166         */
167        public Builder constructOK(Context context) {
168            mId = ACTION_ID_OK;
169            mTitle = context.getString(android.R.string.ok);
170            return this;
171        }
172
173        /**
174         * Construct a standard "Cancel" action with {@link GuidedAction#ACTION_ID_CANCEL}.
175         * @param context Context for loading action title.
176         * @return The same Builder object.
177         */
178        public Builder constructCancel(Context context) {
179            mId = ACTION_ID_CANCEL;
180            mTitle = context.getString(android.R.string.cancel);
181            return this;
182        }
183
184        /**
185         * Construct a standard "Finish" action with {@link GuidedAction#ACTION_ID_FINISH}.
186         * @param context Context for loading action title.
187         * @return The same Builder object.
188         */
189        public Builder constructFinish(Context context) {
190            mId = ACTION_ID_FINISH;
191            mTitle = context.getString(R.string.lb_guidedaction_finish_title);
192            return this;
193        }
194
195        /**
196         * Construct a standard "Continue" action with {@link GuidedAction#ACTION_ID_CONTINUE}.
197         * @param context Context for loading action title.
198         * @return The same Builder object.
199         */
200        public Builder constructContinue(Context context) {
201            mId = ACTION_ID_CONTINUE;
202            mHasNext = true;
203            mTitle = context.getString(R.string.lb_guidedaction_continue_title);
204            return this;
205        }
206
207        /**
208         * Construct a standard "Yes" action with {@link GuidedAction#ACTION_ID_YES}.
209         * @param context Context for loading action title.
210         * @return The same Builder object.
211         */
212        public Builder constructYes(Context context) {
213            mId = ACTION_ID_YES;
214            mTitle = context.getString(android.R.string.yes);
215            return this;
216        }
217
218        /**
219         * Construct a standard "No" action with {@link GuidedAction#ACTION_ID_NO}.
220         * @param context Context for loading action title.
221         * @return The same Builder object.
222         */
223        public Builder constructNo(Context context) {
224            mId = ACTION_ID_NO;
225            mTitle = context.getString(android.R.string.no);
226            return this;
227        }
228
229        /**
230         * Sets the ID associated with this action.  The ID can be any value the client wishes;
231         * it is typically used to determine what to do when an action is clicked.
232         * @param id The ID to associate with this action.
233         */
234        public Builder id(long id) {
235            mId = id;
236            return this;
237        }
238
239        /**
240         * Sets the title for this action.  The title is typically a short string indicating the
241         * action to be taken on click, e.g. "Continue" or "Cancel".
242         * @param title The title for this action.
243         */
244        public Builder title(CharSequence title) {
245            mTitle = title;
246            return this;
247        }
248
249        /**
250         * Sets the optional title text to edit.  When TextView is activated, the edit title
251         * replaces the string of title.
252         */
253        public Builder editTitle(CharSequence editTitle) {
254            mEditTitle = editTitle;
255            return this;
256        }
257
258        /**
259         * Sets the description for this action.  The description is typically a longer string
260         * providing extra information on what the action will do.
261         * @param description The description for this action.
262         */
263        public Builder description(CharSequence description) {
264            mDescription = description;
265            return this;
266        }
267
268        /**
269         * Sets the optional description text to edit.  When TextView is activated, the edit
270         * description replaces the string of description.
271         * @param description The description to edit for this action.
272         */
273        public Builder editDescription(CharSequence description) {
274            mEditDescription = description;
275            return this;
276        }
277
278        /**
279         * Sets the intent associated with this action.  Clients would typically fire this intent
280         * directly when the action is clicked.
281         * @param intent The intent associated with this action.
282         */
283        public Builder intent(Intent intent) {
284            mIntent = intent;
285            return this;
286        }
287
288        /**
289         * Sets the action's icon drawable.
290         * @param icon The drawable for the icon associated with this action.
291         */
292        public Builder icon(Drawable icon) {
293            mIcon = icon;
294            return this;
295        }
296
297        /**
298         * Sets the action's icon drawable by retrieving it by resource ID from the specified
299         * context. This is a convenience function that simply looks up the drawable and calls
300         * {@link #icon}.
301         * @param iconResourceId The resource ID for the icon associated with this action.
302         * @param context The context whose resource ID should be retrieved.
303         */
304        public Builder iconResourceId(int iconResourceId, Context context) {
305            return icon(context.getResources().getDrawable(iconResourceId));
306        }
307
308        /**
309         * Indicates whether this action title is editable. Note: Editable actions cannot also be
310         * checked, or belong to a check set.
311         * @param editable Whether this action is editable.
312         */
313        public Builder editable(boolean editable) {
314            mEditable = editable;
315            if (mChecked || mCheckSetId != NO_CHECK_SET) {
316                throw new IllegalArgumentException("Editable actions cannot also be checked");
317            }
318            return this;
319        }
320
321        /**
322         * Indicates whether this action's description is editable
323         * @param editable Whether this action description is editable.
324         */
325        public Builder descriptionEditable(boolean editable) {
326            mDescriptionEditable = editable;
327            if (mChecked || mCheckSetId != NO_CHECK_SET) {
328                throw new IllegalArgumentException("Editable actions cannot also be checked");
329            }
330            return this;
331        }
332
333        /**
334         * Sets {@link InputType} of this action title not in editing.
335         *
336         * @param inputType InputType for the action title not in editing.
337         */
338        public Builder inputType(int inputType) {
339            mInputType = inputType;
340            return this;
341        }
342
343        /**
344         * Sets {@link InputType} of this action description not in editing.
345         *
346         * @param inputType InputType for the action description not in editing.
347         */
348        public Builder descriptionInputType(int inputType) {
349            mDescriptionInputType = inputType;
350            return this;
351        }
352
353
354        /**
355         * Sets {@link InputType} of this action title in editing.
356         *
357         * @param inputType InputType for the action title in editing.
358         */
359        public Builder editInputType(int inputType) {
360            mEditInputType = inputType;
361            return this;
362        }
363
364        /**
365         * Sets {@link InputType} of this action description in editing.
366         *
367         * @param inputType InputType for the action description in editing.
368         */
369        public Builder descriptionEditInputType(int inputType) {
370            mDescriptionEditInputType = inputType;
371            return this;
372        }
373
374
375        /**
376         * Indicates whether this action is initially checked.
377         * @param checked Whether this action is checked.
378         */
379        public Builder checked(boolean checked) {
380            mChecked = checked;
381            if (mEditable || mDescriptionEditable) {
382                throw new IllegalArgumentException("Editable actions cannot also be checked");
383            }
384            return this;
385        }
386
387        /**
388         * Indicates whether this action is part of a single-select group similar to radio buttons
389         * or this action is a checkbox. When one item in a check set is checked, all others with
390         * the same check set ID will be nchecked automatically.
391         * @param checkSetId The check set ID, or {@link GuidedAction#NO_CHECK_SET} to indicate not
392         * radio or checkbox, or {@link GuidedAction#CHECKBOX_CHECK_SET_ID} to indicate a checkbox.
393         */
394        public Builder checkSetId(int checkSetId) {
395            mCheckSetId = checkSetId;
396            if (mEditable || mDescriptionEditable) {
397                throw new IllegalArgumentException("Editable actions cannot also be in check sets");
398            }
399            return this;
400        }
401
402        /**
403         * Indicates whether the title and description are long, and should be displayed
404         * appropriately.
405         * @param multilineDescription Whether this action has a multiline description.
406         */
407        public Builder multilineDescription(boolean multilineDescription) {
408            mMultilineDescription = multilineDescription;
409            return this;
410        }
411
412        /**
413         * Indicates whether this action has a next state and should display a chevron.
414         * @param hasNext Whether this action has a next state.
415         */
416        public Builder hasNext(boolean hasNext) {
417            mHasNext = hasNext;
418            return this;
419        }
420
421        /**
422         * Indicates whether this action is for information purposes only and cannot be clicked.
423         * @param infoOnly Whether this action has a next state.
424         */
425        public Builder infoOnly(boolean infoOnly) {
426            mInfoOnly = infoOnly;
427            return this;
428        }
429
430        /**
431         * Indicates whether this action is enabled.  If not enabled, an action cannot be clicked.
432         * @param enabled Whether the action is enabled.
433         */
434        public Builder enabled(boolean enabled) {
435            mEnabled = enabled;
436            return this;
437        }
438
439        /**
440         * Indicates whether this action can take focus.
441         * @param focusable
442         * @return The same Builder object.
443         */
444        public Builder focusable(boolean focusable) {
445            mFocusable = focusable;
446            return this;
447        }
448
449        /**
450         * Sets sub actions list.
451         * @param subActions
452         * @return The same Builder object.
453         */
454        public Builder subActions(List<GuidedAction> subActions) {
455            mSubActions = subActions;
456            return this;
457        }
458    }
459
460    private CharSequence mEditTitle;
461    private CharSequence mEditDescription;
462    private boolean mEditable;
463    private boolean mDescriptionEditable;
464    private int mInputType;
465    private int mDescriptionInputType;
466    private int mEditInputType;
467    private int mDescriptionEditInputType;
468    private boolean mMultilineDescription;
469    private boolean mHasNext;
470    private boolean mChecked;
471    private boolean mInfoOnly;
472    private int mCheckSetId;
473    private boolean mEnabled;
474    private boolean mFocusable;
475    private List<GuidedAction> mSubActions;
476
477    private Intent mIntent;
478
479    protected GuidedAction() {
480        super(0);
481    }
482
483    /**
484     * Returns the title of this action.
485     * @return The title set when this action was built.
486     */
487    public CharSequence getTitle() {
488        return getLabel1();
489    }
490
491    /**
492     * Sets the title of this action.
493     * @param title The title set when this action was built.
494     */
495    public void setTitle(CharSequence title) {
496        setLabel1(title);
497    }
498
499    /**
500     * Returns the optional title text to edit.  When not null, it is being edited instead of
501     * {@link #getTitle()}.
502     * @return Optional title text to edit instead of {@link #getTitle()}.
503     */
504    public CharSequence getEditTitle() {
505        return mEditTitle;
506    }
507
508    /**
509     * Sets the optional title text to edit instead of {@link #setTitle(CharSequence)}.
510     * @param editTitle Optional title text to edit instead of {@link #setTitle(CharSequence)}.
511     */
512    public void setEditTitle(CharSequence editTitle) {
513        mEditTitle = editTitle;
514    }
515
516    /**
517     * Returns the optional description text to edit.  When not null, it is being edited instead of
518     * {@link #getDescription()}.
519     * @return Optional description text to edit instead of {@link #getDescription()}.
520     */
521    public CharSequence getEditDescription() {
522        return mEditDescription;
523    }
524
525    /**
526     * Sets the optional description text to edit instead of {@link #setDescription(CharSequence)}.
527     * @param editDescription Optional description text to edit instead of
528     * {@link #setDescription(CharSequence)}.
529     */
530    public void setEditDescription(CharSequence editDescription) {
531        mEditDescription = editDescription;
532    }
533
534    /**
535     * Returns true if {@link #getEditTitle()} is not null.  When true, the {@link #getEditTitle()}
536     * is being edited instead of {@link #getTitle()}.
537     * @return true if {@link #getEditTitle()} is not null.
538     */
539    public boolean isEditTitleUsed() {
540        return mEditTitle != null;
541    }
542
543    /**
544     * Returns the description of this action.
545     * @return The description of this action.
546     */
547    public CharSequence getDescription() {
548        return getLabel2();
549    }
550
551    /**
552     * Sets the description of this action.
553     * @param description The description of the action.
554     */
555    public void setDescription(CharSequence description) {
556        setLabel2(description);
557    }
558
559    /**
560     * Returns the intent associated with this action.
561     * @return The intent set when this action was built.
562     */
563    public Intent getIntent() {
564        return mIntent;
565    }
566
567    /**
568     * Returns whether this action title is editable.
569     * @return true if the action title is editable, false otherwise.
570     */
571    public boolean isEditable() {
572        return mEditable;
573    }
574
575    /**
576     * Returns whether this action description is editable.
577     * @return true if the action description is editable, false otherwise.
578     */
579    public boolean isDescriptionEditable() {
580        return mDescriptionEditable;
581    }
582
583    /**
584     * Returns InputType of action title in editing; only valid when {@link #isEditable()} is true.
585     * @return InputType of action title in editing.
586     */
587    public int getEditInputType() {
588        return mEditInputType;
589    }
590
591    /**
592     * Returns InputType of action description in editing; only valid when
593     * {@link #isDescriptionEditable()} is true.
594     * @return InputType of action description in editing.
595     */
596    public int getDescriptionEditInputType() {
597        return mDescriptionEditInputType;
598    }
599
600    /**
601     * Returns InputType of action title not in editing.
602     * @return InputType of action title not in editing.
603     */
604    public int getInputType() {
605        return mInputType;
606    }
607
608    /**
609     * Returns InputType of action description not in editing.
610     * @return InputType of action description not in editing.
611     */
612    public int getDescriptionInputType() {
613        return mDescriptionInputType;
614    }
615
616    /**
617     * Returns whether this action is checked.
618     * @return true if the action is currently checked, false otherwise.
619     */
620    public boolean isChecked() {
621        return mChecked;
622    }
623
624    /**
625     * Sets whether this action is checked.
626     * @param checked Whether this action should be checked.
627     */
628    public void setChecked(boolean checked) {
629        mChecked = checked;
630    }
631
632    /**
633     * Returns the check set id this action is a part of. All actions in the same list with the same
634     * check set id are considered linked. When one of the actions within that set is selected, that
635     * action becomes checked, while all the other actions become unchecked.
636     *
637     * @return an integer representing the check set this action is a part of, or
638     *         {@link #CHECKBOX_CHECK_SET_ID} if this is a checkbox, or {@link #NO_CHECK_SET} if
639     *         this action is not a checkbox or radiobutton.
640     */
641    public int getCheckSetId() {
642        return mCheckSetId;
643    }
644
645    /**
646     * Returns whether this action is has a multiline description.
647     * @return true if the action was constructed as having a multiline description, false
648     * otherwise.
649     */
650    public boolean hasMultilineDescription() {
651        return mMultilineDescription;
652    }
653
654    /**
655     * Returns whether this action is enabled.
656     * @return true if the action is currently enabled, false otherwise.
657     */
658    public boolean isEnabled() {
659        return mEnabled;
660    }
661
662    /**
663     * Sets whether this action is enabled.
664     * @param enabled Whether this action should be enabled.
665     */
666    public void setEnabled(boolean enabled) {
667        mEnabled = enabled;
668    }
669
670    /**
671     * Returns whether this action is focusable.
672     * @return true if the action is currently focusable, false otherwise.
673     */
674    public boolean isFocusable() {
675        return mFocusable;
676    }
677
678    /**
679     * Sets whether this action is focusable.
680     * @param focusable Whether this action should be focusable.
681     */
682    public void setFocusable(boolean focusable) {
683        mFocusable = focusable;
684    }
685
686    /**
687     * Returns whether this action will request further user input when selected, such as showing
688     * another GuidedStepFragment or launching a new activity. Configured during construction.
689     * @return true if the action will request further user input when selected, false otherwise.
690     */
691    public boolean hasNext() {
692        return mHasNext;
693    }
694
695    /**
696     * Returns whether the action will only display information and is thus not clickable. If both
697     * this and {@link #hasNext()} are true, infoOnly takes precedence. The default is false. For
698     * example, this might represent e.g. the amount of storage a document uses, or the cost of an
699     * app.
700     * @return true if will only display information, false otherwise.
701     */
702    public boolean infoOnly() {
703        return mInfoOnly;
704    }
705
706    /**
707     * Change sub actions list.
708     * @param actions Sub actions list to set on this action.  Sets null to disable sub actions.
709     */
710    public void setSubActions(List<GuidedAction> actions) {
711        mSubActions = actions;
712    }
713
714    /**
715     * @return List of sub actions or null if sub actions list is not enabled.
716     */
717    public List<GuidedAction> getSubActions() {
718        return mSubActions;
719    }
720
721    /**
722     * @return True if has sub actions list, even it's currently empty.
723     */
724    public boolean hasSubActions() {
725        return mSubActions != null;
726    }
727
728}
729