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