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