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