/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package android.support.v17.leanback.widget; import android.os.Bundle; import android.support.annotation.DrawableRes; import android.support.annotation.StringRes; import android.support.v17.leanback.R; import android.support.v4.content.ContextCompat; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; import android.text.InputType; import java.util.List; /** * A data class which represents an action within a {@link * android.support.v17.leanback.app.GuidedStepFragment}. GuidedActions contain at minimum a title * and a description, and typically also an icon. *

* A GuidedAction typically represents a single action a user may take, but may also represent a * possible choice out of a group of mutually exclusive choices (similar to radio buttons), or an * information-only label (in which case the item cannot be clicked). *

* GuidedActions may optionally be checked. They may also indicate that they will request further * user input on selection, in which case they will be displayed with a chevron indicator. *

* GuidedAction recommends to use {@link Builder}. When application subclass GuidedAction, it * can subclass {@link BuilderBase}, implement its own builder() method where it should * call {@link BuilderBase#applyValues(GuidedAction)}. */ public class GuidedAction extends Action { private static final String TAG = "GuidedAction"; /** * Special check set Id that is neither checkbox nor radio. */ public static final int NO_CHECK_SET = 0; /** * Default checkset Id for radio. */ public static final int DEFAULT_CHECK_SET_ID = 1; /** * Checkset Id for checkbox. */ public static final int CHECKBOX_CHECK_SET_ID = -1; /** * When finishing editing, goes to next action. */ public static final long ACTION_ID_NEXT = -2; /** * When finishing editing, stay on current action. */ public static final long ACTION_ID_CURRENT = -3; /** * Id of standard OK action. */ public static final long ACTION_ID_OK = -4; /** * Id of standard Cancel action. */ public static final long ACTION_ID_CANCEL = -5; /** * Id of standard Finish action. */ public static final long ACTION_ID_FINISH = -6; /** * Id of standard Finish action. */ public static final long ACTION_ID_CONTINUE = -7; /** * Id of standard Yes action. */ public static final long ACTION_ID_YES = -8; /** * Id of standard No action. */ public static final long ACTION_ID_NO = -9; static final int EDITING_NONE = 0; static final int EDITING_TITLE = 1; static final int EDITING_DESCRIPTION = 2; static final int EDITING_ACTIVATOR_VIEW = 3; /** * Base builder class to build a {@link GuidedAction} object. When subclass GuidedAction, you * can override this BuilderBase class, implements your build() method which should call * {@link #applyValues(GuidedAction)}. When using GuidedAction directly, use {@link Builder}. */ public abstract static class BuilderBase { private Context mContext; private long mId; private CharSequence mTitle; private CharSequence mEditTitle; private CharSequence mDescription; private CharSequence mEditDescription; private Drawable mIcon; /** * The mActionFlags holds various action states such as whether title or description are * editable, or the action is focusable. * */ private int mActionFlags; private int mEditable = EDITING_NONE; private int mInputType = InputType.TYPE_CLASS_TEXT; private int mDescriptionInputType = InputType.TYPE_CLASS_TEXT; private int mEditInputType = InputType.TYPE_CLASS_TEXT; private int mDescriptionEditInputType = InputType.TYPE_CLASS_TEXT; private int mCheckSetId = NO_CHECK_SET; private List mSubActions; private Intent mIntent; /** * Creates a BuilderBase for GuidedAction or its subclass. * @param context Context object used to build the GuidedAction. */ public BuilderBase(Context context) { mContext = context; mActionFlags = PF_ENABLED | PF_FOCUSABLE | PF_AUTORESTORE; } /** * Returns Context of this Builder. * @return Context of this Builder. */ public Context getContext() { return mContext; } private void setFlags(int flag, int mask) { mActionFlags = (mActionFlags & ~mask) | (flag & mask); } /** * Subclass of BuilderBase should call this function to apply values. * @param action GuidedAction to apply BuilderBase values. */ protected final void applyValues(GuidedAction action) { // Base Action values action.setId(mId); action.setLabel1(mTitle); action.setEditTitle(mEditTitle); action.setLabel2(mDescription); action.setEditDescription(mEditDescription); action.setIcon(mIcon); // Subclass values action.mIntent = mIntent; action.mEditable = mEditable; action.mInputType = mInputType; action.mDescriptionInputType = mDescriptionInputType; action.mEditInputType = mEditInputType; action.mDescriptionEditInputType = mDescriptionEditInputType; action.mActionFlags = mActionFlags; action.mCheckSetId = mCheckSetId; action.mSubActions = mSubActions; } /** * Construct a clickable action with associated id and auto assign pre-defined title for the * action. If the id is not supported, the method simply does nothing. * @param id One of {@link GuidedAction#ACTION_ID_OK} {@link GuidedAction#ACTION_ID_CANCEL} * {@link GuidedAction#ACTION_ID_FINISH} {@link GuidedAction#ACTION_ID_CONTINUE} * {@link GuidedAction#ACTION_ID_YES} {@link GuidedAction#ACTION_ID_NO}. * @return The same BuilderBase object. */ public B clickAction(long id) { if (id == ACTION_ID_OK) { mId = ACTION_ID_OK; mTitle = mContext.getString(android.R.string.ok); } else if (id == ACTION_ID_CANCEL) { mId = ACTION_ID_CANCEL; mTitle = mContext.getString(android.R.string.cancel); } else if (id == ACTION_ID_FINISH) { mId = ACTION_ID_FINISH; mTitle = mContext.getString(R.string.lb_guidedaction_finish_title); } else if (id == ACTION_ID_CONTINUE) { mId = ACTION_ID_CONTINUE; mTitle = mContext.getString(R.string.lb_guidedaction_continue_title); } else if (id == ACTION_ID_YES) { mId = ACTION_ID_YES; mTitle = mContext.getString(android.R.string.yes); } else if (id == ACTION_ID_NO) { mId = ACTION_ID_NO; mTitle = mContext.getString(android.R.string.no); } return (B) this; } /** * Sets the ID associated with this action. The ID can be any value the client wishes; * it is typically used to determine what to do when an action is clicked. * @param id The ID to associate with this action. */ public B id(long id) { mId = id; return (B) this; } /** * Sets the title for this action. The title is typically a short string indicating the * action to be taken on click, e.g. "Continue" or "Cancel". * @param title The title for this action. */ public B title(CharSequence title) { mTitle = title; return (B) this; } /** * Sets the title for this action. The title is typically a short string indicating the * action to be taken on click, e.g. "Continue" or "Cancel". * @param titleResourceId The resource id of title for this action. */ public B title(@StringRes int titleResourceId) { mTitle = getContext().getString(titleResourceId); return (B) this; } /** * Sets the optional title text to edit. When TextView is activated, the edit title * replaces the string of title. * @param editTitle The optional title text to edit when TextView is activated. */ public B editTitle(CharSequence editTitle) { mEditTitle = editTitle; return (B) this; } /** * Sets the optional title text to edit. When TextView is activated, the edit title * replaces the string of title. * @param editTitleResourceId String resource id of the optional title text to edit when * TextView is activated. */ public B editTitle(@StringRes int editTitleResourceId) { mEditTitle = getContext().getString(editTitleResourceId); return (B) this; } /** * Sets the description for this action. The description is typically a longer string * providing extra information on what the action will do. * @param description The description for this action. */ public B description(CharSequence description) { mDescription = description; return (B) this; } /** * Sets the description for this action. The description is typically a longer string * providing extra information on what the action will do. * @param descriptionResourceId String resource id of the description for this action. */ public B description(@StringRes int descriptionResourceId) { mDescription = getContext().getString(descriptionResourceId); return (B) this; } /** * Sets the optional description text to edit. When TextView is activated, the edit * description replaces the string of description. * @param description The description to edit for this action. */ public B editDescription(CharSequence description) { mEditDescription = description; return (B) this; } /** * Sets the optional description text to edit. When TextView is activated, the edit * description replaces the string of description. * @param descriptionResourceId String resource id of the description to edit for this * action. */ public B editDescription(@StringRes int descriptionResourceId) { mEditDescription = getContext().getString(descriptionResourceId); return (B) this; } /** * Sets the intent associated with this action. Clients would typically fire this intent * directly when the action is clicked. * @param intent The intent associated with this action. */ public B intent(Intent intent) { mIntent = intent; return (B) this; } /** * Sets the action's icon drawable. * @param icon The drawable for the icon associated with this action. */ public B icon(Drawable icon) { mIcon = icon; return (B) this; } /** * Sets the action's icon drawable by retrieving it by resource ID from the specified * context. This is a convenience function that simply looks up the drawable and calls * {@link #icon(Drawable)}. * @param iconResourceId The resource ID for the icon associated with this action. * @param context The context whose resource ID should be retrieved. * @deprecated Use {@link #icon(int)}. */ @Deprecated public B iconResourceId(@DrawableRes int iconResourceId, Context context) { return icon(ContextCompat.getDrawable(context, iconResourceId)); } /** * Sets the action's icon drawable by retrieving it by resource ID from Builder's * context. This is a convenience function that simply looks up the drawable and calls * {@link #icon(Drawable)}. * @param iconResourceId The resource ID for the icon associated with this action. */ public B icon(@DrawableRes int iconResourceId) { return icon(ContextCompat.getDrawable(getContext(), iconResourceId)); } /** * Indicates whether this action title is editable. Note: Editable actions cannot also be * checked, or belong to a check set. * @param editable Whether this action is editable. */ public B editable(boolean editable) { if (!editable) { if (mEditable == EDITING_TITLE) { mEditable = EDITING_NONE; } return (B) this; } mEditable = EDITING_TITLE; if (isChecked() || mCheckSetId != NO_CHECK_SET) { throw new IllegalArgumentException("Editable actions cannot also be checked"); } return (B) this; } /** * Indicates whether this action's description is editable * @param editable Whether this action description is editable. */ public B descriptionEditable(boolean editable) { if (!editable) { if (mEditable == EDITING_DESCRIPTION) { mEditable = EDITING_NONE; } return (B) this; } mEditable = EDITING_DESCRIPTION; if (isChecked() || mCheckSetId != NO_CHECK_SET) { throw new IllegalArgumentException("Editable actions cannot also be checked"); } return (B) this; } /** * Indicates whether this action has a view can be activated to edit, e.g. a DatePicker. * @param editable Whether this action has view can be activated to edit. */ public B hasEditableActivatorView(boolean editable) { if (!editable) { if (mEditable == EDITING_ACTIVATOR_VIEW) { mEditable = EDITING_NONE; } return (B) this; } mEditable = EDITING_ACTIVATOR_VIEW; if (isChecked() || mCheckSetId != NO_CHECK_SET) { throw new IllegalArgumentException("Editable actions cannot also be checked"); } return (B) this; } /** * Sets {@link InputType} of this action title not in editing. * * @param inputType InputType for the action title not in editing. */ public B inputType(int inputType) { mInputType = inputType; return (B) this; } /** * Sets {@link InputType} of this action description not in editing. * * @param inputType InputType for the action description not in editing. */ public B descriptionInputType(int inputType) { mDescriptionInputType = inputType; return (B) this; } /** * Sets {@link InputType} of this action title in editing. * * @param inputType InputType for the action title in editing. */ public B editInputType(int inputType) { mEditInputType = inputType; return (B) this; } /** * Sets {@link InputType} of this action description in editing. * * @param inputType InputType for the action description in editing. */ public B descriptionEditInputType(int inputType) { mDescriptionEditInputType = inputType; return (B) this; } private boolean isChecked() { return (mActionFlags & PF_CHECKED) == PF_CHECKED; } /** * Indicates whether this action is initially checked. * @param checked Whether this action is checked. */ public B checked(boolean checked) { setFlags(checked ? PF_CHECKED : 0, PF_CHECKED); if (mEditable != EDITING_NONE) { throw new IllegalArgumentException("Editable actions cannot also be checked"); } return (B) this; } /** * Indicates whether this action is part of a single-select group similar to radio buttons * or this action is a checkbox. When one item in a check set is checked, all others with * the same check set ID will be checked automatically. * @param checkSetId The check set ID, or {@link GuidedAction#NO_CHECK_SET} to indicate not * radio or checkbox, or {@link GuidedAction#CHECKBOX_CHECK_SET_ID} to indicate a checkbox. */ public B checkSetId(int checkSetId) { mCheckSetId = checkSetId; if (mEditable != EDITING_NONE) { throw new IllegalArgumentException("Editable actions cannot also be in check sets"); } return (B) this; } /** * Indicates whether the title and description are long, and should be displayed * appropriately. * @param multilineDescription Whether this action has a multiline description. */ public B multilineDescription(boolean multilineDescription) { setFlags(multilineDescription ? PF_MULTI_lINE_DESCRIPTION : 0, PF_MULTI_lINE_DESCRIPTION); return (B) this; } /** * Indicates whether this action has a next state and should display a chevron. * @param hasNext Whether this action has a next state. */ public B hasNext(boolean hasNext) { setFlags(hasNext ? PF_HAS_NEXT : 0, PF_HAS_NEXT); return (B) this; } /** * Indicates whether this action is for information purposes only and cannot be clicked. * @param infoOnly Whether this action has a next state. */ public B infoOnly(boolean infoOnly) { setFlags(infoOnly ? PF_INFO_ONLY : 0, PF_INFO_ONLY); return (B) this; } /** * Indicates whether this action is enabled. If not enabled, an action cannot be clicked. * @param enabled Whether the action is enabled. */ public B enabled(boolean enabled) { setFlags(enabled ? PF_ENABLED : 0, PF_ENABLED); return (B) this; } /** * Indicates whether this action can take focus. * @param focusable * @return The same BuilderBase object. */ public B focusable(boolean focusable) { setFlags(focusable ? PF_FOCUSABLE : 0, PF_FOCUSABLE); return (B) this; } /** * Sets sub actions list. * @param subActions * @return The same BuilderBase object. */ public B subActions(List subActions) { mSubActions = subActions; return (B) this; } /** * Explicitly sets auto restore feature on the GuidedAction. It's by default true. * @param autoSaveRestoreEnabled True if turn on auto save/restore of GuidedAction content, * false otherwise. * @return The same BuilderBase object. * @see GuidedAction#isAutoSaveRestoreEnabled() */ public B autoSaveRestoreEnabled(boolean autoSaveRestoreEnabled) { setFlags(autoSaveRestoreEnabled ? PF_AUTORESTORE : 0, PF_AUTORESTORE); return (B) this; } } /** * Builds a {@link GuidedAction} object. */ public static class Builder extends BuilderBase { /** * @deprecated Use {@link GuidedAction.Builder#GuidedAction.Builder(Context)}. */ @Deprecated public Builder() { super(null); } /** * Creates a Builder for GuidedAction. * @param context Context to build GuidedAction. */ public Builder(Context context) { super(context); } /** * Builds the GuidedAction corresponding to this Builder. * @return The GuidedAction as configured through this Builder. */ public GuidedAction build() { GuidedAction action = new GuidedAction(); applyValues(action); return action; } } static final int PF_CHECKED = 0x00000001; static final int PF_MULTI_lINE_DESCRIPTION = 0x00000002; static final int PF_HAS_NEXT = 0x00000004; static final int PF_INFO_ONLY = 0x00000008; static final int PF_ENABLED = 0x00000010; static final int PF_FOCUSABLE = 0x00000020; static final int PF_AUTORESTORE = 0x00000040; int mActionFlags; private CharSequence mEditTitle; private CharSequence mEditDescription; int mEditable; int mInputType; int mDescriptionInputType; int mEditInputType; int mDescriptionEditInputType; int mCheckSetId; List mSubActions; Intent mIntent; protected GuidedAction() { super(0); } private void setFlags(int flag, int mask) { mActionFlags = (mActionFlags & ~mask) | (flag & mask); } /** * Returns the title of this action. * @return The title set when this action was built. */ public CharSequence getTitle() { return getLabel1(); } /** * Sets the title of this action. * @param title The title set when this action was built. */ public void setTitle(CharSequence title) { setLabel1(title); } /** * Returns the optional title text to edit. When not null, it is being edited instead of * {@link #getTitle()}. * @return Optional title text to edit instead of {@link #getTitle()}. */ public CharSequence getEditTitle() { return mEditTitle; } /** * Sets the optional title text to edit instead of {@link #setTitle(CharSequence)}. * @param editTitle Optional title text to edit instead of {@link #setTitle(CharSequence)}. */ public void setEditTitle(CharSequence editTitle) { mEditTitle = editTitle; } /** * Returns the optional description text to edit. When not null, it is being edited instead of * {@link #getDescription()}. * @return Optional description text to edit instead of {@link #getDescription()}. */ public CharSequence getEditDescription() { return mEditDescription; } /** * Sets the optional description text to edit instead of {@link #setDescription(CharSequence)}. * @param editDescription Optional description text to edit instead of * {@link #setDescription(CharSequence)}. */ public void setEditDescription(CharSequence editDescription) { mEditDescription = editDescription; } /** * Returns true if {@link #getEditTitle()} is not null. When true, the {@link #getEditTitle()} * is being edited instead of {@link #getTitle()}. * @return true if {@link #getEditTitle()} is not null. */ public boolean isEditTitleUsed() { return mEditTitle != null; } /** * Returns the description of this action. * @return The description of this action. */ public CharSequence getDescription() { return getLabel2(); } /** * Sets the description of this action. * @param description The description of the action. */ public void setDescription(CharSequence description) { setLabel2(description); } /** * Returns the intent associated with this action. * @return The intent set when this action was built. */ public Intent getIntent() { return mIntent; } /** * Sets the intent of this action. * @param intent New intent to set on this action. */ public void setIntent(Intent intent) { mIntent = intent; } /** * Returns whether this action title is editable. * @return true if the action title is editable, false otherwise. */ public boolean isEditable() { return mEditable == EDITING_TITLE; } /** * Returns whether this action description is editable. * @return true if the action description is editable, false otherwise. */ public boolean isDescriptionEditable() { return mEditable == EDITING_DESCRIPTION; } /** * Returns if this action has editable title or editable description. * @return True if this action has editable title or editable description, false otherwise. */ public boolean hasTextEditable() { return mEditable == EDITING_TITLE || mEditable == EDITING_DESCRIPTION; } /** * Returns whether this action can be activated to edit, e.g. a DatePicker. * @return true if the action can be activated to edit. */ public boolean hasEditableActivatorView() { return mEditable == EDITING_ACTIVATOR_VIEW; } /** * Returns InputType of action title in editing; only valid when {@link #isEditable()} is true. * @return InputType of action title in editing. */ public int getEditInputType() { return mEditInputType; } /** * Returns InputType of action description in editing; only valid when * {@link #isDescriptionEditable()} is true. * @return InputType of action description in editing. */ public int getDescriptionEditInputType() { return mDescriptionEditInputType; } /** * Returns InputType of action title not in editing. * @return InputType of action title not in editing. */ public int getInputType() { return mInputType; } /** * Returns InputType of action description not in editing. * @return InputType of action description not in editing. */ public int getDescriptionInputType() { return mDescriptionInputType; } /** * Returns whether this action is checked. * @return true if the action is currently checked, false otherwise. */ public boolean isChecked() { return (mActionFlags & PF_CHECKED) == PF_CHECKED; } /** * Sets whether this action is checked. * @param checked Whether this action should be checked. */ public void setChecked(boolean checked) { setFlags(checked ? PF_CHECKED : 0, PF_CHECKED); } /** * Returns the check set id this action is a part of. All actions in the same list with the same * check set id are considered linked. When one of the actions within that set is selected, that * action becomes checked, while all the other actions become unchecked. * * @return an integer representing the check set this action is a part of, or * {@link #CHECKBOX_CHECK_SET_ID} if this is a checkbox, or {@link #NO_CHECK_SET} if * this action is not a checkbox or radiobutton. */ public int getCheckSetId() { return mCheckSetId; } /** * Returns whether this action is has a multiline description. * @return true if the action was constructed as having a multiline description, false * otherwise. */ public boolean hasMultilineDescription() { return (mActionFlags & PF_MULTI_lINE_DESCRIPTION) == PF_MULTI_lINE_DESCRIPTION; } /** * Returns whether this action is enabled. * @return true if the action is currently enabled, false otherwise. */ public boolean isEnabled() { return (mActionFlags & PF_ENABLED) == PF_ENABLED; } /** * Sets whether this action is enabled. * @param enabled Whether this action should be enabled. */ public void setEnabled(boolean enabled) { setFlags(enabled ? PF_ENABLED : 0, PF_ENABLED); } /** * Returns whether this action is focusable. * @return true if the action is currently focusable, false otherwise. */ public boolean isFocusable() { return (mActionFlags & PF_FOCUSABLE) == PF_FOCUSABLE; } /** * Sets whether this action is focusable. * @param focusable Whether this action should be focusable. */ public void setFocusable(boolean focusable) { setFlags(focusable ? PF_FOCUSABLE : 0, PF_FOCUSABLE); } /** * Returns whether this action will request further user input when selected, such as showing * another GuidedStepFragment or launching a new activity. Configured during construction. * @return true if the action will request further user input when selected, false otherwise. */ public boolean hasNext() { return (mActionFlags & PF_HAS_NEXT) == PF_HAS_NEXT; } /** * Returns whether the action will only display information and is thus not clickable. If both * this and {@link #hasNext()} are true, infoOnly takes precedence. The default is false. For * example, this might represent e.g. the amount of storage a document uses, or the cost of an * app. * @return true if will only display information, false otherwise. */ public boolean infoOnly() { return (mActionFlags & PF_INFO_ONLY) == PF_INFO_ONLY; } /** * Change sub actions list. * @param actions Sub actions list to set on this action. Sets null to disable sub actions. */ public void setSubActions(List actions) { mSubActions = actions; } /** * @return List of sub actions or null if sub actions list is not enabled. */ public List getSubActions() { return mSubActions; } /** * @return True if has sub actions list, even it's currently empty. */ public boolean hasSubActions() { return mSubActions != null; } /** * Returns true if Action will be saved to instanceState and restored later, false otherwise. * The default value is true. When isAutoSaveRestoreEnabled() is true and {@link #getId()} is * not {@link #NO_ID}: *

  • {@link #isEditable()} is true: save text of {@link #getTitle()}
  • *
  • {@link #isDescriptionEditable()} is true: save text of {@link #getDescription()}
  • *
  • {@link #getCheckSetId()} is not {@link #NO_CHECK_SET}: save {@link #isChecked()}}
  • *
  • {@link GuidedDatePickerAction} will be saved
  • * App may explicitly disable auto restore and handle by itself. App should override Fragment * onSaveInstanceState() and onCreateActions() * @return True if Action will be saved to instanceState and restored later, false otherwise. */ public final boolean isAutoSaveRestoreEnabled() { return (mActionFlags & PF_AUTORESTORE) == PF_AUTORESTORE; } /** * Save action into a bundle using a given key. When isAutoRestoreEna() is true: *
  • {@link #isEditable()} is true: save text of {@link #getTitle()}
  • *
  • {@link #isDescriptionEditable()} is true: save text of {@link #getDescription()}
  • *
  • {@link #getCheckSetId()} is not {@link #NO_CHECK_SET}: save {@link #isChecked()}}
  • *
  • {@link GuidedDatePickerAction} will be saved
  • * Subclass may override this method. * @param bundle Bundle to save the Action. * @param key Key used to save the Action. */ public void onSaveInstanceState(Bundle bundle, String key) { if (needAutoSaveTitle() && getTitle() != null) { bundle.putString(key, getTitle().toString()); } else if (needAutoSaveDescription() && getDescription() != null) { bundle.putString(key, getDescription().toString()); } else if (getCheckSetId() != NO_CHECK_SET) { bundle.putBoolean(key, isChecked()); } } /** * Restore action from a bundle using a given key. When isAutoRestore() is true: *
  • {@link #isEditable()} is true: save text of {@link #getTitle()}
  • *
  • {@link #isDescriptionEditable()} is true: save text of {@link #getDescription()}
  • *
  • {@link #getCheckSetId()} is not {@link #NO_CHECK_SET}: save {@link #isChecked()}}
  • *
  • {@link GuidedDatePickerAction} will be saved
  • * Subclass may override this method. * @param bundle Bundle to restore the Action from. * @param key Key used to restore the Action. */ public void onRestoreInstanceState(Bundle bundle, String key) { if (needAutoSaveTitle()) { String title = bundle.getString(key); if (title != null) { setTitle(title); } } else if (needAutoSaveDescription()) { String description = bundle.getString(key); if (description != null) { setDescription(description); } } else if (getCheckSetId() != NO_CHECK_SET) { setChecked(bundle.getBoolean(key, isChecked())); } } final static boolean isPasswordVariant(int inputType) { final int variation = inputType & InputType.TYPE_MASK_VARIATION; return variation == InputType.TYPE_TEXT_VARIATION_PASSWORD || variation == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD || variation == InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD; } final boolean needAutoSaveTitle() { return isEditable() && !isPasswordVariant(getEditInputType()); } final boolean needAutoSaveDescription() { return isDescriptionEditable() && !isPasswordVariant(getDescriptionEditInputType()); } }