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 */ BuilderBase(Context context)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 */ getContext()151 public Context getContext() { 152 return mContext; 153 } 154 setFlags(int flag, int mask)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 */ applyValues(GuidedAction action)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 */ clickAction(long id)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 */ id(long id)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 */ title(CharSequence title)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 */ title(@tringRes int titleResourceId)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 */ editTitle(CharSequence editTitle)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 */ editTitle(@tringRes int editTitleResourceId)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 */ description(CharSequence description)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 */ description(@tringRes int descriptionResourceId)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 */ editDescription(CharSequence description)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 */ editDescription(@tringRes int descriptionResourceId)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 */ intent(Intent intent)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 */ icon(Drawable icon)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 iconResourceId(@rawableRes int iconResourceId, Context context)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 */ icon(@rawableRes int iconResourceId)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 */ editable(boolean editable)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 */ descriptionEditable(boolean editable)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 */ hasEditableActivatorView(boolean editable)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 */ inputType(int inputType)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 */ descriptionInputType(int inputType)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 */ editInputType(int inputType)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 */ descriptionEditInputType(int inputType)441 public B descriptionEditInputType(int inputType) { 442 mDescriptionEditInputType = inputType; 443 return (B) this; 444 } 445 446 isChecked()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 */ checked(boolean checked)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 */ checkSetId(int checkSetId)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 */ multilineDescription(boolean multilineDescription)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 */ hasNext(boolean hasNext)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 */ infoOnly(boolean infoOnly)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 */ enabled(boolean enabled)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 */ focusable(boolean focusable)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 */ subActions(List<GuidedAction> subActions)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 */ autoSaveRestoreEnabled(boolean autoSaveRestoreEnabled)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 */ autofillHints(String... hints)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 Builder()567 public Builder() { 568 super(null); 569 } 570 571 /** 572 * Creates a Builder for GuidedAction. 573 * @param context Context to build GuidedAction. 574 */ Builder(Context context)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 */ build()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 GuidedAction()615 protected GuidedAction() { 616 super(0); 617 } 618 setFlags(int flag, int mask)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 */ getTitle()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 */ setTitle(CharSequence title)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 */ getEditTitle()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 */ setEditTitle(CharSequence editTitle)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 */ getEditDescription()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 */ setEditDescription(CharSequence editDescription)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 */ isEditTitleUsed()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 */ getDescription()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 */ setDescription(CharSequence description)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 */ getIntent()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 */ setIntent(Intent intent)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 */ isEditable()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 */ isDescriptionEditable()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 */ hasTextEditable()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 */ hasEditableActivatorView()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 */ getEditInputType()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 */ getDescriptionEditInputType()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 */ getInputType()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 */ getDescriptionInputType()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 */ isChecked()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 */ setChecked(boolean checked)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 */ getCheckSetId()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 */ hasMultilineDescription()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 */ isEnabled()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 */ setEnabled(boolean enabled)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 */ isFocusable()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 */ setFocusable(boolean focusable)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 */ getAutofillHints()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 */ hasNext()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 */ infoOnly()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 */ setSubActions(List<GuidedAction> actions)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 */ getSubActions()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 */ hasSubActions()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 */ isAutoSaveRestoreEnabled()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 */ onSaveInstanceState(Bundle bundle, String key)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 */ onRestoreInstanceState(Bundle bundle, String key)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 isPasswordVariant(int inputType)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 needAutoSaveTitle()968 final boolean needAutoSaveTitle() { 969 return isEditable() && !isPasswordVariant(getEditInputType()); 970 } 971 needAutoSaveDescription()972 final boolean needAutoSaveDescription() { 973 return isDescriptionEditable() && !isPasswordVariant(getDescriptionEditInputType()); 974 } 975 976 } 977