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