• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
14 package android.support.v17.leanback.widget;
15 
16 import android.os.Bundle;
17 import android.support.annotation.DrawableRes;
18 import android.support.annotation.StringRes;
19 import android.support.v17.leanback.R;
20 import android.support.v4.content.ContextCompat;
21 
22 import android.content.Context;
23 import android.content.Intent;
24 import android.graphics.drawable.Drawable;
25 import android.text.InputType;
26 
27 import java.util.List;
28 
29 /**
30  * A data class which represents an action within a {@link
31  * android.support.v17.leanback.app.GuidedStepFragment}. GuidedActions contain at minimum a title
32  * and a description, and typically also an icon.
33  * <p>
34  * A GuidedAction typically represents a single action a user may take, but may also represent a
35  * possible choice out of a group of mutually exclusive choices (similar to radio buttons), or an
36  * information-only label (in which case the item cannot be clicked).
37  * <p>
38  * GuidedActions may optionally be checked. They may also indicate that they will request further
39  * user input on selection, in which case they will be displayed with a chevron indicator.
40  * <p>
41  * GuidedAction recommends to use {@link Builder}. When application subclass GuidedAction, it
42  * can subclass {@link BuilderBase}, implement its own builder() method where it should
43  * call {@link BuilderBase#applyValues(GuidedAction)}.
44  */
45 public class GuidedAction extends Action {
46 
47     private static final String TAG = "GuidedAction";
48 
49     /**
50      * Special check set Id that is neither checkbox nor radio.
51      */
52     public static final int NO_CHECK_SET = 0;
53     /**
54      * Default checkset Id for radio.
55      */
56     public static final int DEFAULT_CHECK_SET_ID = 1;
57     /**
58      * Checkset Id for checkbox.
59      */
60     public static final int CHECKBOX_CHECK_SET_ID = -1;
61 
62     /**
63      * When finishing editing, goes to next action.
64      */
65     public static final long ACTION_ID_NEXT = -2;
66     /**
67      * When finishing editing, stay on current action.
68      */
69     public static final long ACTION_ID_CURRENT = -3;
70 
71     /**
72      * Id of standard OK action.
73      */
74     public static final long ACTION_ID_OK = -4;
75 
76     /**
77      * Id of standard Cancel action.
78      */
79     public static final long ACTION_ID_CANCEL = -5;
80 
81     /**
82      * Id of standard Finish action.
83      */
84     public static final long ACTION_ID_FINISH = -6;
85 
86     /**
87      * Id of standard Finish action.
88      */
89     public static final long ACTION_ID_CONTINUE = -7;
90 
91     /**
92      * Id of standard Yes action.
93      */
94     public static final long ACTION_ID_YES = -8;
95 
96     /**
97      * Id of standard No action.
98      */
99     public static final long ACTION_ID_NO = -9;
100 
101     static final int EDITING_NONE = 0;
102     static final int EDITING_TITLE = 1;
103     static final int EDITING_DESCRIPTION = 2;
104     static final int EDITING_ACTIVATOR_VIEW = 3;
105 
106     /**
107      * Base builder class to build a {@link GuidedAction} object.  When subclass GuidedAction, you
108      * can override this BuilderBase class, implements your build() method which should call
109      * {@link #applyValues(GuidedAction)}.  When using GuidedAction directly, use {@link Builder}.
110      */
111     public abstract static class BuilderBase<B extends BuilderBase> {
112         private Context mContext;
113         private long mId;
114         private CharSequence mTitle;
115         private CharSequence mEditTitle;
116         private CharSequence mDescription;
117         private CharSequence mEditDescription;
118         private Drawable mIcon;
119         /**
120          * The mActionFlags holds various action states such as whether title or description are
121          * editable, or the action is focusable.
122          *
123          */
124         private int mActionFlags;
125 
126         private int mEditable = EDITING_NONE;
127         private int mInputType = InputType.TYPE_CLASS_TEXT;
128         private int mDescriptionInputType = InputType.TYPE_CLASS_TEXT;
129         private int mEditInputType = InputType.TYPE_CLASS_TEXT;
130         private int mDescriptionEditInputType = InputType.TYPE_CLASS_TEXT;
131         private int mCheckSetId = NO_CHECK_SET;
132         private List<GuidedAction> mSubActions;
133         private Intent mIntent;
134 
135         /**
136          * Creates a BuilderBase for GuidedAction or its subclass.
137          * @param context Context object used to build the GuidedAction.
138          */
BuilderBase(Context context)139         public BuilderBase(Context context) {
140             mContext = context;
141             mActionFlags = PF_ENABLED | PF_FOCUSABLE | PF_AUTORESTORE;
142         }
143 
144         /**
145          * Returns Context of this Builder.
146          * @return Context of this Builder.
147          */
getContext()148         public Context getContext() {
149             return mContext;
150         }
151 
setFlags(int flag, int mask)152         private void setFlags(int flag, int mask) {
153             mActionFlags = (mActionFlags & ~mask) | (flag & mask);
154         }
155 
156         /**
157          * Subclass of BuilderBase should call this function to apply values.
158          * @param action GuidedAction to apply BuilderBase values.
159          */
applyValues(GuidedAction action)160         protected final void applyValues(GuidedAction action) {
161             // Base Action values
162             action.setId(mId);
163             action.setLabel1(mTitle);
164             action.setEditTitle(mEditTitle);
165             action.setLabel2(mDescription);
166             action.setEditDescription(mEditDescription);
167             action.setIcon(mIcon);
168 
169             // Subclass values
170             action.mIntent = mIntent;
171             action.mEditable = mEditable;
172             action.mInputType = mInputType;
173             action.mDescriptionInputType = mDescriptionInputType;
174             action.mEditInputType = mEditInputType;
175             action.mDescriptionEditInputType = mDescriptionEditInputType;
176             action.mActionFlags = mActionFlags;
177             action.mCheckSetId = mCheckSetId;
178             action.mSubActions = mSubActions;
179         }
180 
181         /**
182          * Construct a clickable action with associated id and auto assign pre-defined title for the
183          * action. If the id is not supported, the method simply does nothing.
184          * @param id One of {@link GuidedAction#ACTION_ID_OK} {@link GuidedAction#ACTION_ID_CANCEL}
185          * {@link GuidedAction#ACTION_ID_FINISH} {@link GuidedAction#ACTION_ID_CONTINUE}
186          * {@link GuidedAction#ACTION_ID_YES} {@link GuidedAction#ACTION_ID_NO}.
187          * @return The same BuilderBase object.
188          */
clickAction(long id)189         public B clickAction(long id) {
190             if (id == ACTION_ID_OK) {
191                 mId = ACTION_ID_OK;
192                 mTitle = mContext.getString(android.R.string.ok);
193             } else if (id == ACTION_ID_CANCEL) {
194                 mId = ACTION_ID_CANCEL;
195                 mTitle = mContext.getString(android.R.string.cancel);
196             } else if (id == ACTION_ID_FINISH) {
197                 mId = ACTION_ID_FINISH;
198                 mTitle = mContext.getString(R.string.lb_guidedaction_finish_title);
199             } else if (id == ACTION_ID_CONTINUE) {
200                 mId = ACTION_ID_CONTINUE;
201                 mTitle = mContext.getString(R.string.lb_guidedaction_continue_title);
202             } else if (id == ACTION_ID_YES) {
203                 mId = ACTION_ID_YES;
204                 mTitle = mContext.getString(android.R.string.yes);
205             } else if (id == ACTION_ID_NO) {
206                 mId = ACTION_ID_NO;
207                 mTitle = mContext.getString(android.R.string.no);
208             }
209             return (B) this;
210         }
211 
212         /**
213          * Sets the ID associated with this action.  The ID can be any value the client wishes;
214          * it is typically used to determine what to do when an action is clicked.
215          * @param id The ID to associate with this action.
216          */
id(long id)217         public B id(long id) {
218             mId = id;
219             return (B) this;
220         }
221 
222         /**
223          * Sets the title for this action.  The title is typically a short string indicating the
224          * action to be taken on click, e.g. "Continue" or "Cancel".
225          * @param title The title for this action.
226          */
title(CharSequence title)227         public B title(CharSequence title) {
228             mTitle = title;
229             return (B) this;
230         }
231 
232         /**
233          * Sets the title for this action.  The title is typically a short string indicating the
234          * action to be taken on click, e.g. "Continue" or "Cancel".
235          * @param titleResourceId The resource id of title for this action.
236          */
title(@tringRes int titleResourceId)237         public B title(@StringRes int titleResourceId) {
238             mTitle = getContext().getString(titleResourceId);
239             return (B) this;
240         }
241 
242         /**
243          * Sets the optional title text to edit.  When TextView is activated, the edit title
244          * replaces the string of title.
245          * @param editTitle The optional title text to edit when TextView is activated.
246          */
editTitle(CharSequence editTitle)247         public B editTitle(CharSequence editTitle) {
248             mEditTitle = editTitle;
249             return (B) this;
250         }
251 
252         /**
253          * Sets the optional title text to edit.  When TextView is activated, the edit title
254          * replaces the string of title.
255          * @param editTitleResourceId String resource id of the optional title text to edit when
256          * TextView is activated.
257          */
editTitle(@tringRes int editTitleResourceId)258         public B editTitle(@StringRes int editTitleResourceId) {
259             mEditTitle = getContext().getString(editTitleResourceId);
260             return (B) this;
261         }
262 
263         /**
264          * Sets the description for this action.  The description is typically a longer string
265          * providing extra information on what the action will do.
266          * @param description The description for this action.
267          */
description(CharSequence description)268         public B description(CharSequence description) {
269             mDescription = description;
270             return (B) this;
271         }
272 
273         /**
274          * Sets the description for this action.  The description is typically a longer string
275          * providing extra information on what the action will do.
276          * @param descriptionResourceId String resource id of the description for this action.
277          */
description(@tringRes int descriptionResourceId)278         public B description(@StringRes int descriptionResourceId) {
279             mDescription = getContext().getString(descriptionResourceId);
280             return (B) this;
281         }
282 
283         /**
284          * Sets the optional description text to edit.  When TextView is activated, the edit
285          * description replaces the string of description.
286          * @param description The description to edit for this action.
287          */
editDescription(CharSequence description)288         public B editDescription(CharSequence description) {
289             mEditDescription = description;
290             return (B) this;
291         }
292 
293         /**
294          * Sets the optional description text to edit.  When TextView is activated, the edit
295          * description replaces the string of description.
296          * @param descriptionResourceId String resource id of the description to edit for this
297          * action.
298          */
editDescription(@tringRes int descriptionResourceId)299         public B editDescription(@StringRes int descriptionResourceId) {
300             mEditDescription = getContext().getString(descriptionResourceId);
301             return (B) this;
302         }
303 
304         /**
305          * Sets the intent associated with this action.  Clients would typically fire this intent
306          * directly when the action is clicked.
307          * @param intent The intent associated with this action.
308          */
intent(Intent intent)309         public B intent(Intent intent) {
310             mIntent = intent;
311             return (B) this;
312         }
313 
314         /**
315          * Sets the action's icon drawable.
316          * @param icon The drawable for the icon associated with this action.
317          */
icon(Drawable icon)318         public B icon(Drawable icon) {
319             mIcon = icon;
320             return (B) this;
321         }
322 
323         /**
324          * Sets the action's icon drawable by retrieving it by resource ID from the specified
325          * context. This is a convenience function that simply looks up the drawable and calls
326          * {@link #icon(Drawable)}.
327          * @param iconResourceId The resource ID for the icon associated with this action.
328          * @param context The context whose resource ID should be retrieved.
329          * @deprecated Use {@link #icon(int)}.
330          */
331         @Deprecated
iconResourceId(@rawableRes int iconResourceId, Context context)332         public B iconResourceId(@DrawableRes int iconResourceId, Context context) {
333             return icon(ContextCompat.getDrawable(context, iconResourceId));
334         }
335 
336         /**
337          * Sets the action's icon drawable by retrieving it by resource ID from Builder's
338          * context. This is a convenience function that simply looks up the drawable and calls
339          * {@link #icon(Drawable)}.
340          * @param iconResourceId The resource ID for the icon associated with this action.
341          */
icon(@rawableRes int iconResourceId)342         public B icon(@DrawableRes int iconResourceId) {
343             return icon(ContextCompat.getDrawable(getContext(), iconResourceId));
344         }
345 
346         /**
347          * Indicates whether this action title is editable. Note: Editable actions cannot also be
348          * checked, or belong to a check set.
349          * @param editable Whether this action is editable.
350          */
editable(boolean editable)351         public B editable(boolean editable) {
352             if (!editable) {
353                 if (mEditable == EDITING_TITLE) {
354                     mEditable = EDITING_NONE;
355                 }
356                 return (B) this;
357             }
358             mEditable = EDITING_TITLE;
359             if (isChecked() || mCheckSetId != NO_CHECK_SET) {
360                 throw new IllegalArgumentException("Editable actions cannot also be checked");
361             }
362             return (B) this;
363         }
364 
365         /**
366          * Indicates whether this action's description is editable
367          * @param editable Whether this action description is editable.
368          */
descriptionEditable(boolean editable)369         public B descriptionEditable(boolean editable) {
370             if (!editable) {
371                 if (mEditable == EDITING_DESCRIPTION) {
372                     mEditable = EDITING_NONE;
373                 }
374                 return (B) this;
375             }
376             mEditable = EDITING_DESCRIPTION;
377             if (isChecked() || mCheckSetId != NO_CHECK_SET) {
378                 throw new IllegalArgumentException("Editable actions cannot also be checked");
379             }
380             return (B) this;
381         }
382 
383         /**
384          * Indicates whether this action has a view can be activated to edit, e.g. a DatePicker.
385          * @param editable Whether this action has view can be activated to edit.
386          */
hasEditableActivatorView(boolean editable)387         public B hasEditableActivatorView(boolean editable) {
388             if (!editable) {
389                 if (mEditable == EDITING_ACTIVATOR_VIEW) {
390                     mEditable = EDITING_NONE;
391                 }
392                 return (B) this;
393             }
394             mEditable = EDITING_ACTIVATOR_VIEW;
395             if (isChecked() || mCheckSetId != NO_CHECK_SET) {
396                 throw new IllegalArgumentException("Editable actions cannot also be checked");
397             }
398             return (B) this;
399         }
400 
401         /**
402          * Sets {@link InputType} of this action title not in editing.
403          *
404          * @param inputType InputType for the action title not in editing.
405          */
inputType(int inputType)406         public B inputType(int inputType) {
407             mInputType = inputType;
408             return (B) this;
409         }
410 
411         /**
412          * Sets {@link InputType} of this action description not in editing.
413          *
414          * @param inputType InputType for the action description not in editing.
415          */
descriptionInputType(int inputType)416         public B descriptionInputType(int inputType) {
417             mDescriptionInputType = inputType;
418             return (B) this;
419         }
420 
421 
422         /**
423          * Sets {@link InputType} of this action title in editing.
424          *
425          * @param inputType InputType for the action title in editing.
426          */
editInputType(int inputType)427         public B editInputType(int inputType) {
428             mEditInputType = inputType;
429             return (B) this;
430         }
431 
432         /**
433          * Sets {@link InputType} of this action description in editing.
434          *
435          * @param inputType InputType for the action description in editing.
436          */
descriptionEditInputType(int inputType)437         public B descriptionEditInputType(int inputType) {
438             mDescriptionEditInputType = inputType;
439             return (B) this;
440         }
441 
442 
isChecked()443         private boolean isChecked() {
444             return (mActionFlags & PF_CHECKED) == PF_CHECKED;
445         }
446         /**
447          * Indicates whether this action is initially checked.
448          * @param checked Whether this action is checked.
449          */
checked(boolean checked)450         public B checked(boolean checked) {
451             setFlags(checked ? PF_CHECKED : 0, PF_CHECKED);
452             if (mEditable != EDITING_NONE) {
453                 throw new IllegalArgumentException("Editable actions cannot also be checked");
454             }
455             return (B) this;
456         }
457 
458         /**
459          * Indicates whether this action is part of a single-select group similar to radio buttons
460          * or this action is a checkbox. When one item in a check set is checked, all others with
461          * the same check set ID will be checked automatically.
462          * @param checkSetId The check set ID, or {@link GuidedAction#NO_CHECK_SET} to indicate not
463          * radio or checkbox, or {@link GuidedAction#CHECKBOX_CHECK_SET_ID} to indicate a checkbox.
464          */
checkSetId(int checkSetId)465         public B checkSetId(int checkSetId) {
466             mCheckSetId = checkSetId;
467             if (mEditable != EDITING_NONE) {
468                 throw new IllegalArgumentException("Editable actions cannot also be in check sets");
469             }
470             return (B) this;
471         }
472 
473         /**
474          * Indicates whether the title and description are long, and should be displayed
475          * appropriately.
476          * @param multilineDescription Whether this action has a multiline description.
477          */
multilineDescription(boolean multilineDescription)478         public B multilineDescription(boolean multilineDescription) {
479             setFlags(multilineDescription ? PF_MULTI_lINE_DESCRIPTION : 0,
480                     PF_MULTI_lINE_DESCRIPTION);
481             return (B) this;
482         }
483 
484         /**
485          * Indicates whether this action has a next state and should display a chevron.
486          * @param hasNext Whether this action has a next state.
487          */
hasNext(boolean hasNext)488         public B hasNext(boolean hasNext) {
489             setFlags(hasNext ? PF_HAS_NEXT : 0, PF_HAS_NEXT);
490             return (B) this;
491         }
492 
493         /**
494          * Indicates whether this action is for information purposes only and cannot be clicked.
495          * @param infoOnly Whether this action has a next state.
496          */
infoOnly(boolean infoOnly)497         public B infoOnly(boolean infoOnly) {
498             setFlags(infoOnly ? PF_INFO_ONLY : 0, PF_INFO_ONLY);
499             return (B) this;
500         }
501 
502         /**
503          * Indicates whether this action is enabled.  If not enabled, an action cannot be clicked.
504          * @param enabled Whether the action is enabled.
505          */
enabled(boolean enabled)506         public B enabled(boolean enabled) {
507             setFlags(enabled ? PF_ENABLED : 0, PF_ENABLED);
508             return (B) this;
509         }
510 
511         /**
512          * Indicates whether this action can take focus.
513          * @param focusable
514          * @return The same BuilderBase object.
515          */
focusable(boolean focusable)516         public B focusable(boolean focusable) {
517             setFlags(focusable ? PF_FOCUSABLE : 0, PF_FOCUSABLE);
518             return (B) this;
519         }
520 
521         /**
522          * Sets sub actions list.
523          * @param subActions
524          * @return The same BuilderBase object.
525          */
subActions(List<GuidedAction> subActions)526         public B subActions(List<GuidedAction> subActions) {
527             mSubActions = subActions;
528             return (B) this;
529         }
530 
531         /**
532          * Explicitly sets auto restore feature on the GuidedAction.  It's by default true.
533          * @param autoSaveRestoreEnabled True if turn on auto save/restore of GuidedAction content,
534          *                                false otherwise.
535          * @return The same BuilderBase object.
536          * @see GuidedAction#isAutoSaveRestoreEnabled()
537          */
autoSaveRestoreEnabled(boolean autoSaveRestoreEnabled)538         public B autoSaveRestoreEnabled(boolean autoSaveRestoreEnabled) {
539             setFlags(autoSaveRestoreEnabled ? PF_AUTORESTORE : 0, PF_AUTORESTORE);
540             return (B) this;
541         }
542 
543     }
544 
545     /**
546      * Builds a {@link GuidedAction} object.
547      */
548     public static class Builder extends BuilderBase<Builder> {
549 
550         /**
551          * @deprecated Use {@link GuidedAction.Builder#GuidedAction.Builder(Context)}.
552          */
553         @Deprecated
Builder()554         public Builder() {
555             super(null);
556         }
557 
558         /**
559          * Creates a Builder for GuidedAction.
560          * @param context Context to build GuidedAction.
561          */
Builder(Context context)562         public Builder(Context context) {
563             super(context);
564         }
565 
566         /**
567          * Builds the GuidedAction corresponding to this Builder.
568          * @return The GuidedAction as configured through this Builder.
569          */
build()570         public GuidedAction build() {
571             GuidedAction action = new GuidedAction();
572             applyValues(action);
573             return action;
574         }
575 
576     }
577 
578     private static final int PF_CHECKED = 0x00000001;
579     private static final int PF_MULTI_lINE_DESCRIPTION = 0x00000002;
580     private static final int PF_HAS_NEXT = 0x00000004;
581     private static final int PF_INFO_ONLY = 0x00000008;
582     private static final int PF_ENABLED = 0x00000010;
583     private static final int PF_FOCUSABLE = 0x00000020;
584     private static final int PF_AUTORESTORE = 0x00000040;
585     private int mActionFlags;
586 
587     private CharSequence mEditTitle;
588     private CharSequence mEditDescription;
589     private int mEditable;
590     private int mInputType;
591     private int mDescriptionInputType;
592     private int mEditInputType;
593     private int mDescriptionEditInputType;
594 
595     private int mCheckSetId;
596 
597     private List<GuidedAction> mSubActions;
598 
599     private Intent mIntent;
600 
GuidedAction()601     protected GuidedAction() {
602         super(0);
603     }
604 
setFlags(int flag, int mask)605     private void setFlags(int flag, int mask) {
606         mActionFlags = (mActionFlags & ~mask) | (flag & mask);
607     }
608 
609     /**
610      * Returns the title of this action.
611      * @return The title set when this action was built.
612      */
getTitle()613     public CharSequence getTitle() {
614         return getLabel1();
615     }
616 
617     /**
618      * Sets the title of this action.
619      * @param title The title set when this action was built.
620      */
setTitle(CharSequence title)621     public void setTitle(CharSequence title) {
622         setLabel1(title);
623     }
624 
625     /**
626      * Returns the optional title text to edit.  When not null, it is being edited instead of
627      * {@link #getTitle()}.
628      * @return Optional title text to edit instead of {@link #getTitle()}.
629      */
getEditTitle()630     public CharSequence getEditTitle() {
631         return mEditTitle;
632     }
633 
634     /**
635      * Sets the optional title text to edit instead of {@link #setTitle(CharSequence)}.
636      * @param editTitle Optional title text to edit instead of {@link #setTitle(CharSequence)}.
637      */
setEditTitle(CharSequence editTitle)638     public void setEditTitle(CharSequence editTitle) {
639         mEditTitle = editTitle;
640     }
641 
642     /**
643      * Returns the optional description text to edit.  When not null, it is being edited instead of
644      * {@link #getDescription()}.
645      * @return Optional description text to edit instead of {@link #getDescription()}.
646      */
getEditDescription()647     public CharSequence getEditDescription() {
648         return mEditDescription;
649     }
650 
651     /**
652      * Sets the optional description text to edit instead of {@link #setDescription(CharSequence)}.
653      * @param editDescription Optional description text to edit instead of
654      * {@link #setDescription(CharSequence)}.
655      */
setEditDescription(CharSequence editDescription)656     public void setEditDescription(CharSequence editDescription) {
657         mEditDescription = editDescription;
658     }
659 
660     /**
661      * Returns true if {@link #getEditTitle()} is not null.  When true, the {@link #getEditTitle()}
662      * is being edited instead of {@link #getTitle()}.
663      * @return true if {@link #getEditTitle()} is not null.
664      */
isEditTitleUsed()665     public boolean isEditTitleUsed() {
666         return mEditTitle != null;
667     }
668 
669     /**
670      * Returns the description of this action.
671      * @return The description of this action.
672      */
getDescription()673     public CharSequence getDescription() {
674         return getLabel2();
675     }
676 
677     /**
678      * Sets the description of this action.
679      * @param description The description of the action.
680      */
setDescription(CharSequence description)681     public void setDescription(CharSequence description) {
682         setLabel2(description);
683     }
684 
685     /**
686      * Returns the intent associated with this action.
687      * @return The intent set when this action was built.
688      */
getIntent()689     public Intent getIntent() {
690         return mIntent;
691     }
692 
693     /**
694      * Sets the intent of this action.
695      * @param intent New intent to set on this action.
696      */
setIntent(Intent intent)697     public void setIntent(Intent intent) {
698         mIntent = intent;
699     }
700 
701     /**
702      * Returns whether this action title is editable.
703      * @return true if the action title is editable, false otherwise.
704      */
isEditable()705     public boolean isEditable() {
706         return mEditable == EDITING_TITLE;
707     }
708 
709     /**
710      * Returns whether this action description is editable.
711      * @return true if the action description is editable, false otherwise.
712      */
isDescriptionEditable()713     public boolean isDescriptionEditable() {
714         return mEditable == EDITING_DESCRIPTION;
715     }
716 
717     /**
718      * Returns if this action has editable title or editable description.
719      * @return True if this action has editable title or editable description, false otherwise.
720      */
hasTextEditable()721     public boolean hasTextEditable() {
722         return mEditable == EDITING_TITLE || mEditable == EDITING_DESCRIPTION;
723     }
724 
725     /**
726      * Returns whether this action can be activated to edit, e.g. a DatePicker.
727      * @return true if the action can be activated to edit.
728      */
hasEditableActivatorView()729     public boolean hasEditableActivatorView() {
730         return mEditable == EDITING_ACTIVATOR_VIEW;
731     }
732 
733     /**
734      * Returns InputType of action title in editing; only valid when {@link #isEditable()} is true.
735      * @return InputType of action title in editing.
736      */
getEditInputType()737     public int getEditInputType() {
738         return mEditInputType;
739     }
740 
741     /**
742      * Returns InputType of action description in editing; only valid when
743      * {@link #isDescriptionEditable()} is true.
744      * @return InputType of action description in editing.
745      */
getDescriptionEditInputType()746     public int getDescriptionEditInputType() {
747         return mDescriptionEditInputType;
748     }
749 
750     /**
751      * Returns InputType of action title not in editing.
752      * @return InputType of action title not in editing.
753      */
getInputType()754     public int getInputType() {
755         return mInputType;
756     }
757 
758     /**
759      * Returns InputType of action description not in editing.
760      * @return InputType of action description not in editing.
761      */
getDescriptionInputType()762     public int getDescriptionInputType() {
763         return mDescriptionInputType;
764     }
765 
766     /**
767      * Returns whether this action is checked.
768      * @return true if the action is currently checked, false otherwise.
769      */
isChecked()770     public boolean isChecked() {
771         return (mActionFlags & PF_CHECKED) == PF_CHECKED;
772     }
773 
774     /**
775      * Sets whether this action is checked.
776      * @param checked Whether this action should be checked.
777      */
setChecked(boolean checked)778     public void setChecked(boolean checked) {
779         setFlags(checked ? PF_CHECKED : 0, PF_CHECKED);
780     }
781 
782     /**
783      * Returns the check set id this action is a part of. All actions in the same list with the same
784      * check set id are considered linked. When one of the actions within that set is selected, that
785      * action becomes checked, while all the other actions become unchecked.
786      *
787      * @return an integer representing the check set this action is a part of, or
788      *         {@link #CHECKBOX_CHECK_SET_ID} if this is a checkbox, or {@link #NO_CHECK_SET} if
789      *         this action is not a checkbox or radiobutton.
790      */
getCheckSetId()791     public int getCheckSetId() {
792         return mCheckSetId;
793     }
794 
795     /**
796      * Returns whether this action is has a multiline description.
797      * @return true if the action was constructed as having a multiline description, false
798      * otherwise.
799      */
hasMultilineDescription()800     public boolean hasMultilineDescription() {
801         return (mActionFlags & PF_MULTI_lINE_DESCRIPTION) == PF_MULTI_lINE_DESCRIPTION;
802     }
803 
804     /**
805      * Returns whether this action is enabled.
806      * @return true if the action is currently enabled, false otherwise.
807      */
isEnabled()808     public boolean isEnabled() {
809         return (mActionFlags & PF_ENABLED) == PF_ENABLED;
810     }
811 
812     /**
813      * Sets whether this action is enabled.
814      * @param enabled Whether this action should be enabled.
815      */
setEnabled(boolean enabled)816     public void setEnabled(boolean enabled) {
817         setFlags(enabled ? PF_ENABLED : 0, PF_ENABLED);
818     }
819 
820     /**
821      * Returns whether this action is focusable.
822      * @return true if the action is currently focusable, false otherwise.
823      */
isFocusable()824     public boolean isFocusable() {
825         return (mActionFlags & PF_FOCUSABLE) == PF_FOCUSABLE;
826     }
827 
828     /**
829      * Sets whether this action is focusable.
830      * @param focusable Whether this action should be focusable.
831      */
setFocusable(boolean focusable)832     public void setFocusable(boolean focusable) {
833         setFlags(focusable ? PF_FOCUSABLE : 0, PF_FOCUSABLE);
834     }
835 
836     /**
837      * Returns whether this action will request further user input when selected, such as showing
838      * another GuidedStepFragment or launching a new activity. Configured during construction.
839      * @return true if the action will request further user input when selected, false otherwise.
840      */
hasNext()841     public boolean hasNext() {
842         return (mActionFlags & PF_HAS_NEXT) == PF_HAS_NEXT;
843     }
844 
845     /**
846      * Returns whether the action will only display information and is thus not clickable. If both
847      * this and {@link #hasNext()} are true, infoOnly takes precedence. The default is false. For
848      * example, this might represent e.g. the amount of storage a document uses, or the cost of an
849      * app.
850      * @return true if will only display information, false otherwise.
851      */
infoOnly()852     public boolean infoOnly() {
853         return (mActionFlags & PF_INFO_ONLY) == PF_INFO_ONLY;
854     }
855 
856     /**
857      * Change sub actions list.
858      * @param actions Sub actions list to set on this action.  Sets null to disable sub actions.
859      */
setSubActions(List<GuidedAction> actions)860     public void setSubActions(List<GuidedAction> actions) {
861         mSubActions = actions;
862     }
863 
864     /**
865      * @return List of sub actions or null if sub actions list is not enabled.
866      */
getSubActions()867     public List<GuidedAction> getSubActions() {
868         return mSubActions;
869     }
870 
871     /**
872      * @return True if has sub actions list, even it's currently empty.
873      */
hasSubActions()874     public boolean hasSubActions() {
875         return mSubActions != null;
876     }
877 
878     /**
879      * Returns true if Action will be saved to instanceState and restored later, false otherwise.
880      * The default value is true.  When isAutoSaveRestoreEnabled() is true and {@link #getId()} is
881      * not {@link #NO_ID}:
882      * <li>{@link #isEditable()} is true: save text of {@link #getTitle()}</li>
883      * <li>{@link #isDescriptionEditable()} is true: save text of {@link #getDescription()}</li>
884      * <li>{@link #getCheckSetId()} is not {@link #NO_CHECK_SET}: save {@link #isChecked()}}</li>
885      * <li>{@link GuidedDatePickerAction} will be saved</li>
886      * App may explicitly disable auto restore and handle by itself. App should override Fragment
887      * onSaveInstanceState() and onCreateActions()
888      * @return True if Action will be saved to instanceState and restored later, false otherwise.
889      */
isAutoSaveRestoreEnabled()890     public final boolean isAutoSaveRestoreEnabled() {
891         return (mActionFlags & PF_AUTORESTORE) == PF_AUTORESTORE;
892     }
893 
894     /**
895      * Save action into a bundle using a given key. When isAutoRestoreEna() is true:
896      * <li>{@link #isEditable()} is true: save text of {@link #getTitle()}</li>
897      * <li>{@link #isDescriptionEditable()} is true: save text of {@link #getDescription()}</li>
898      * <li>{@link #getCheckSetId()} is not {@link #NO_CHECK_SET}: save {@link #isChecked()}}</li>
899      * <li>{@link GuidedDatePickerAction} will be saved</li>
900      * Subclass may override this method.
901      * @param bundle  Bundle to save the Action.
902      * @param key Key used to save the Action.
903      */
onSaveInstanceState(Bundle bundle, String key)904     public void onSaveInstanceState(Bundle bundle, String key) {
905         if (needAutoSaveTitle() && getTitle() != null) {
906             bundle.putString(key, getTitle().toString());
907         } else if (needAutoSaveDescription() && getDescription() != null) {
908             bundle.putString(key, getDescription().toString());
909         } else if (getCheckSetId() != NO_CHECK_SET) {
910             bundle.putBoolean(key, isChecked());
911         }
912     }
913 
914     /**
915      * Restore action from a bundle using a given key. When isAutoRestore() is true:
916      * <li>{@link #isEditable()} is true: save text of {@link #getTitle()}</li>
917      * <li>{@link #isDescriptionEditable()} is true: save text of {@link #getDescription()}</li>
918      * <li>{@link #getCheckSetId()} is not {@link #NO_CHECK_SET}: save {@link #isChecked()}}</li>
919      * <li>{@link GuidedDatePickerAction} will be saved</li>
920      * Subclass may override this method.
921      * @param bundle  Bundle to restore the Action from.
922      * @param key Key used to restore the Action.
923      */
onRestoreInstanceState(Bundle bundle, String key)924     public void onRestoreInstanceState(Bundle bundle, String key) {
925         if (needAutoSaveTitle()) {
926             String title = bundle.getString(key);
927             if (title != null) {
928                 setTitle(title);
929             }
930         } else if (needAutoSaveDescription()) {
931             String description = bundle.getString(key);
932             if (description != null) {
933                 setDescription(description);
934             }
935         } else if (getCheckSetId() != NO_CHECK_SET) {
936             setChecked(bundle.getBoolean(key, isChecked()));
937         }
938     }
939 
isPasswordVariant(int inputType)940     final static boolean isPasswordVariant(int inputType) {
941         final int variation = inputType & InputType.TYPE_MASK_VARIATION;
942         return variation == InputType.TYPE_TEXT_VARIATION_PASSWORD
943                 || variation == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
944                 || variation == InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD;
945     }
946 
needAutoSaveTitle()947     final boolean needAutoSaveTitle() {
948         return isEditable() && !isPasswordVariant(getEditInputType());
949     }
950 
needAutoSaveDescription()951     final boolean needAutoSaveDescription() {
952         return isDescriptionEditable() && !isPasswordVariant(getDescriptionEditInputType());
953     }
954 
955 }
956