GuidedAction.java revision a7f9f2928b60143e09f216a10ea50f2dd4581170
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 */ 14package android.support.v17.leanback.widget; 15 16import android.support.v17.leanback.R; 17 18import android.content.Context; 19import android.content.Intent; 20import android.graphics.drawable.Drawable; 21import android.text.InputType; 22import android.util.Log; 23 24import java.util.List; 25 26/** 27 * A data class which represents an action within a {@link 28 * android.support.v17.leanback.app.GuidedStepFragment}. GuidedActions contain at minimum a title 29 * and a description, and typically also an icon. 30 * <p> 31 * A GuidedAction typically represents a single action a user may take, but may also represent a 32 * possible choice out of a group of mutually exclusive choices (similar to radio buttons), or an 33 * information-only label (in which case the item cannot be clicked). 34 * <p> 35 * GuidedActions may optionally be checked. They may also indicate that they will request further 36 * user input on selection, in which case they will be displayed with a chevron indicator. 37 * <p> 38 * GuidedAction recommends to use {@link Builder}. When application subclass GuidedAction, it 39 * can subclass {@link BuilderBase}, implement {@link BuilderBase#build()} where it should 40 * call {@link BuilderBase#applyValues(GuidedAction)}. 41 */ 42public class GuidedAction extends Action { 43 44 private static final String TAG = "GuidedAction"; 45 46 /** 47 * Special check set Id that is neither checkbox nor radio. 48 */ 49 public static final int NO_CHECK_SET = 0; 50 /** 51 * Default checkset Id for radio. 52 */ 53 public static final int DEFAULT_CHECK_SET_ID = 1; 54 /** 55 * Checkset Id for checkbox. 56 */ 57 public static final int CHECKBOX_CHECK_SET_ID = -1; 58 59 /** 60 * When finishing editing, goes to next action. 61 */ 62 public static final long ACTION_ID_NEXT = -2; 63 /** 64 * When finishing editing, stay on current action. 65 */ 66 public static final long ACTION_ID_CURRENT = -3; 67 68 /** 69 * Id of standard OK action. 70 */ 71 public static final long ACTION_ID_OK = -4; 72 73 /** 74 * Id of standard Cancel action. 75 */ 76 public static final long ACTION_ID_CANCEL = -5; 77 78 /** 79 * Id of standard Finish action. 80 */ 81 public static final long ACTION_ID_FINISH = -6; 82 83 /** 84 * Id of standard Finish action. 85 */ 86 public static final long ACTION_ID_CONTINUE = -7; 87 88 /** 89 * Id of standard Yes action. 90 */ 91 public static final long ACTION_ID_YES = -8; 92 93 /** 94 * Id of standard No action. 95 */ 96 public static final long ACTION_ID_NO = -9; 97 98 /** 99 * Base builder class to build a {@link GuidedAction} object. When subclass GuidedAction, you 100 * can override this BuilderBase class, implements {@link #build()} and call 101 * {@link #applyValues(GuidedAction)}. When using GuidedAction directly, use {@link Builder}. 102 */ 103 public abstract static class BuilderBase<T extends GuidedAction> { 104 private long mId; 105 private CharSequence mTitle; 106 private CharSequence mEditTitle; 107 private CharSequence mDescription; 108 private CharSequence mEditDescription; 109 private Drawable mIcon; 110 private boolean mChecked; 111 private boolean mMultilineDescription; 112 private boolean mHasNext; 113 private boolean mInfoOnly; 114 private boolean mEditable = false; 115 private boolean mDescriptionEditable = false; 116 private int mInputType = InputType.TYPE_CLASS_TEXT; 117 private int mDescriptionInputType = InputType.TYPE_CLASS_TEXT; 118 private int mEditInputType = InputType.TYPE_CLASS_TEXT; 119 private int mDescriptionEditInputType = InputType.TYPE_CLASS_TEXT; 120 private int mCheckSetId = NO_CHECK_SET; 121 private boolean mEnabled = true; 122 private boolean mFocusable = true; 123 private List<GuidedAction> mSubActions; 124 private Intent mIntent; 125 126 /** 127 * Builds the GuidedAction corresponding to this Builder. 128 * @return the GuidedAction as configured through this BuilderBase. 129 */ 130 public abstract T build(); 131 132 /** 133 * Subclass of BuilderBase should call this function to apply values. 134 * @param action GuidedAction to apply BuilderBase values. 135 */ 136 protected final void applyValues(GuidedAction action) { 137 // Base Action values 138 action.setId(mId); 139 action.setLabel1(mTitle); 140 action.setEditTitle(mEditTitle); 141 action.setLabel2(mDescription); 142 action.setEditDescription(mEditDescription); 143 action.setIcon(mIcon); 144 145 // Subclass values 146 action.mIntent = mIntent; 147 action.mEditable = mEditable; 148 action.mDescriptionEditable = mDescriptionEditable; 149 action.mInputType = mInputType; 150 action.mDescriptionInputType = mDescriptionInputType; 151 action.mEditInputType = mEditInputType; 152 action.mDescriptionEditInputType = mDescriptionEditInputType; 153 action.mChecked = mChecked; 154 action.mCheckSetId = mCheckSetId; 155 action.mMultilineDescription = mMultilineDescription; 156 action.mHasNext = mHasNext; 157 action.mInfoOnly = mInfoOnly; 158 action.mEnabled = mEnabled; 159 action.mFocusable = mFocusable; 160 action.mSubActions = mSubActions; 161 } 162 163 /** 164 * Construct a standard "OK" action with {@link GuidedAction#ACTION_ID_OK}. 165 * @param context Context for loading action title. 166 * @return The same BuilderBase object. 167 */ 168 public BuilderBase<T> constructOK(Context context) { 169 mId = ACTION_ID_OK; 170 mTitle = context.getString(android.R.string.ok); 171 return this; 172 } 173 174 /** 175 * Construct a standard "Cancel" action with {@link GuidedAction#ACTION_ID_CANCEL}. 176 * @param context Context for loading action title. 177 * @return The same BuilderBase object. 178 */ 179 public BuilderBase<T> constructCancel(Context context) { 180 mId = ACTION_ID_CANCEL; 181 mTitle = context.getString(android.R.string.cancel); 182 return this; 183 } 184 185 /** 186 * Construct a standard "Finish" action with {@link GuidedAction#ACTION_ID_FINISH}. 187 * @param context Context for loading action title. 188 * @return The same BuilderBase object. 189 */ 190 public BuilderBase<T> constructFinish(Context context) { 191 mId = ACTION_ID_FINISH; 192 mTitle = context.getString(R.string.lb_guidedaction_finish_title); 193 return this; 194 } 195 196 /** 197 * Construct a standard "Continue" action with {@link GuidedAction#ACTION_ID_CONTINUE}. 198 * @param context Context for loading action title. 199 * @return The same BuilderBase object. 200 */ 201 public BuilderBase<T> constructContinue(Context context) { 202 mId = ACTION_ID_CONTINUE; 203 mHasNext = true; 204 mTitle = context.getString(R.string.lb_guidedaction_continue_title); 205 return this; 206 } 207 208 /** 209 * Construct a standard "Yes" action with {@link GuidedAction#ACTION_ID_YES}. 210 * @param context Context for loading action title. 211 * @return The same BuilderBase object. 212 */ 213 public BuilderBase<T> constructYes(Context context) { 214 mId = ACTION_ID_YES; 215 mTitle = context.getString(android.R.string.yes); 216 return this; 217 } 218 219 /** 220 * Construct a standard "No" action with {@link GuidedAction#ACTION_ID_NO}. 221 * @param context Context for loading action title. 222 * @return The same BuilderBase object. 223 */ 224 public BuilderBase<T> constructNo(Context context) { 225 mId = ACTION_ID_NO; 226 mTitle = context.getString(android.R.string.no); 227 return this; 228 } 229 230 /** 231 * Sets the ID associated with this action. The ID can be any value the client wishes; 232 * it is typically used to determine what to do when an action is clicked. 233 * @param id The ID to associate with this action. 234 */ 235 public BuilderBase<T> id(long id) { 236 mId = id; 237 return this; 238 } 239 240 /** 241 * Sets the title for this action. The title is typically a short string indicating the 242 * action to be taken on click, e.g. "Continue" or "Cancel". 243 * @param title The title for this action. 244 */ 245 public BuilderBase<T> title(CharSequence title) { 246 mTitle = title; 247 return this; 248 } 249 250 /** 251 * Sets the optional title text to edit. When TextView is activated, the edit title 252 * replaces the string of title. 253 */ 254 public BuilderBase<T> editTitle(CharSequence editTitle) { 255 mEditTitle = editTitle; 256 return this; 257 } 258 259 /** 260 * Sets the description for this action. The description is typically a longer string 261 * providing extra information on what the action will do. 262 * @param description The description for this action. 263 */ 264 public BuilderBase<T> description(CharSequence description) { 265 mDescription = description; 266 return this; 267 } 268 269 /** 270 * Sets the optional description text to edit. When TextView is activated, the edit 271 * description replaces the string of description. 272 * @param description The description to edit for this action. 273 */ 274 public BuilderBase<T> editDescription(CharSequence description) { 275 mEditDescription = description; 276 return this; 277 } 278 279 /** 280 * Sets the intent associated with this action. Clients would typically fire this intent 281 * directly when the action is clicked. 282 * @param intent The intent associated with this action. 283 */ 284 public BuilderBase<T> intent(Intent intent) { 285 mIntent = intent; 286 return this; 287 } 288 289 /** 290 * Sets the action's icon drawable. 291 * @param icon The drawable for the icon associated with this action. 292 */ 293 public BuilderBase<T> icon(Drawable icon) { 294 mIcon = icon; 295 return this; 296 } 297 298 /** 299 * Sets the action's icon drawable by retrieving it by resource ID from the specified 300 * context. This is a convenience function that simply looks up the drawable and calls 301 * {@link #icon}. 302 * @param iconResourceId The resource ID for the icon associated with this action. 303 * @param context The context whose resource ID should be retrieved. 304 */ 305 public BuilderBase<T> iconResourceId(int iconResourceId, Context context) { 306 return icon(context.getResources().getDrawable(iconResourceId)); 307 } 308 309 /** 310 * Indicates whether this action title is editable. Note: Editable actions cannot also be 311 * checked, or belong to a check set. 312 * @param editable Whether this action is editable. 313 */ 314 public BuilderBase<T> editable(boolean editable) { 315 mEditable = editable; 316 if (mChecked || mCheckSetId != NO_CHECK_SET) { 317 throw new IllegalArgumentException("Editable actions cannot also be checked"); 318 } 319 return this; 320 } 321 322 /** 323 * Indicates whether this action's description is editable 324 * @param editable Whether this action description is editable. 325 */ 326 public BuilderBase<T> descriptionEditable(boolean editable) { 327 mDescriptionEditable = editable; 328 if (mChecked || mCheckSetId != NO_CHECK_SET) { 329 throw new IllegalArgumentException("Editable actions cannot also be checked"); 330 } 331 return this; 332 } 333 334 /** 335 * Sets {@link InputType} of this action title not in editing. 336 * 337 * @param inputType InputType for the action title not in editing. 338 */ 339 public BuilderBase<T> inputType(int inputType) { 340 mInputType = inputType; 341 return this; 342 } 343 344 /** 345 * Sets {@link InputType} of this action description not in editing. 346 * 347 * @param inputType InputType for the action description not in editing. 348 */ 349 public BuilderBase<T> descriptionInputType(int inputType) { 350 mDescriptionInputType = inputType; 351 return this; 352 } 353 354 355 /** 356 * Sets {@link InputType} of this action title in editing. 357 * 358 * @param inputType InputType for the action title in editing. 359 */ 360 public BuilderBase<T> editInputType(int inputType) { 361 mEditInputType = inputType; 362 return this; 363 } 364 365 /** 366 * Sets {@link InputType} of this action description in editing. 367 * 368 * @param inputType InputType for the action description in editing. 369 */ 370 public BuilderBase<T> descriptionEditInputType(int inputType) { 371 mDescriptionEditInputType = inputType; 372 return this; 373 } 374 375 376 /** 377 * Indicates whether this action is initially checked. 378 * @param checked Whether this action is checked. 379 */ 380 public BuilderBase<T> checked(boolean checked) { 381 mChecked = checked; 382 if (mEditable || mDescriptionEditable) { 383 throw new IllegalArgumentException("Editable actions cannot also be checked"); 384 } 385 return this; 386 } 387 388 /** 389 * Indicates whether this action is part of a single-select group similar to radio buttons 390 * or this action is a checkbox. When one item in a check set is checked, all others with 391 * the same check set ID will be nchecked automatically. 392 * @param checkSetId The check set ID, or {@link GuidedAction#NO_CHECK_SET} to indicate not 393 * radio or checkbox, or {@link GuidedAction#CHECKBOX_CHECK_SET_ID} to indicate a checkbox. 394 */ 395 public BuilderBase<T> checkSetId(int checkSetId) { 396 mCheckSetId = checkSetId; 397 if (mEditable || mDescriptionEditable) { 398 throw new IllegalArgumentException("Editable actions cannot also be in check sets"); 399 } 400 return this; 401 } 402 403 /** 404 * Indicates whether the title and description are long, and should be displayed 405 * appropriately. 406 * @param multilineDescription Whether this action has a multiline description. 407 */ 408 public BuilderBase<T> multilineDescription(boolean multilineDescription) { 409 mMultilineDescription = multilineDescription; 410 return this; 411 } 412 413 /** 414 * Indicates whether this action has a next state and should display a chevron. 415 * @param hasNext Whether this action has a next state. 416 */ 417 public BuilderBase<T> hasNext(boolean hasNext) { 418 mHasNext = hasNext; 419 return this; 420 } 421 422 /** 423 * Indicates whether this action is for information purposes only and cannot be clicked. 424 * @param infoOnly Whether this action has a next state. 425 */ 426 public BuilderBase<T> infoOnly(boolean infoOnly) { 427 mInfoOnly = infoOnly; 428 return this; 429 } 430 431 /** 432 * Indicates whether this action is enabled. If not enabled, an action cannot be clicked. 433 * @param enabled Whether the action is enabled. 434 */ 435 public BuilderBase<T> enabled(boolean enabled) { 436 mEnabled = enabled; 437 return this; 438 } 439 440 /** 441 * Indicates whether this action can take focus. 442 * @param focusable 443 * @return The same BuilderBase object. 444 */ 445 public BuilderBase<T> focusable(boolean focusable) { 446 mFocusable = focusable; 447 return this; 448 } 449 450 /** 451 * Sets sub actions list. 452 * @param subActions 453 * @return The same BuilderBase object. 454 */ 455 public BuilderBase<T> subActions(List<GuidedAction> subActions) { 456 mSubActions = subActions; 457 return this; 458 } 459 } 460 461 /** 462 * Builds a {@link GuidedAction} object. 463 */ 464 public static class Builder extends BuilderBase<GuidedAction> { 465 466 @Override 467 public GuidedAction build() { 468 GuidedAction action = new GuidedAction(); 469 applyValues(action); 470 return action; 471 } 472 473 } 474 475 private CharSequence mEditTitle; 476 private CharSequence mEditDescription; 477 private boolean mEditable; 478 private boolean mDescriptionEditable; 479 private int mInputType; 480 private int mDescriptionInputType; 481 private int mEditInputType; 482 private int mDescriptionEditInputType; 483 private boolean mMultilineDescription; 484 private boolean mHasNext; 485 private boolean mChecked; 486 private boolean mInfoOnly; 487 private int mCheckSetId; 488 private boolean mEnabled; 489 private boolean mFocusable; 490 private List<GuidedAction> mSubActions; 491 492 private Intent mIntent; 493 494 protected GuidedAction() { 495 super(0); 496 } 497 498 /** 499 * Returns the title of this action. 500 * @return The title set when this action was built. 501 */ 502 public CharSequence getTitle() { 503 return getLabel1(); 504 } 505 506 /** 507 * Sets the title of this action. 508 * @param title The title set when this action was built. 509 */ 510 public void setTitle(CharSequence title) { 511 setLabel1(title); 512 } 513 514 /** 515 * Returns the optional title text to edit. When not null, it is being edited instead of 516 * {@link #getTitle()}. 517 * @return Optional title text to edit instead of {@link #getTitle()}. 518 */ 519 public CharSequence getEditTitle() { 520 return mEditTitle; 521 } 522 523 /** 524 * Sets the optional title text to edit instead of {@link #setTitle(CharSequence)}. 525 * @param editTitle Optional title text to edit instead of {@link #setTitle(CharSequence)}. 526 */ 527 public void setEditTitle(CharSequence editTitle) { 528 mEditTitle = editTitle; 529 } 530 531 /** 532 * Returns the optional description text to edit. When not null, it is being edited instead of 533 * {@link #getDescription()}. 534 * @return Optional description text to edit instead of {@link #getDescription()}. 535 */ 536 public CharSequence getEditDescription() { 537 return mEditDescription; 538 } 539 540 /** 541 * Sets the optional description text to edit instead of {@link #setDescription(CharSequence)}. 542 * @param editDescription Optional description text to edit instead of 543 * {@link #setDescription(CharSequence)}. 544 */ 545 public void setEditDescription(CharSequence editDescription) { 546 mEditDescription = editDescription; 547 } 548 549 /** 550 * Returns true if {@link #getEditTitle()} is not null. When true, the {@link #getEditTitle()} 551 * is being edited instead of {@link #getTitle()}. 552 * @return true if {@link #getEditTitle()} is not null. 553 */ 554 public boolean isEditTitleUsed() { 555 return mEditTitle != null; 556 } 557 558 /** 559 * Returns the description of this action. 560 * @return The description of this action. 561 */ 562 public CharSequence getDescription() { 563 return getLabel2(); 564 } 565 566 /** 567 * Sets the description of this action. 568 * @param description The description of the action. 569 */ 570 public void setDescription(CharSequence description) { 571 setLabel2(description); 572 } 573 574 /** 575 * Returns the intent associated with this action. 576 * @return The intent set when this action was built. 577 */ 578 public Intent getIntent() { 579 return mIntent; 580 } 581 582 /** 583 * Returns whether this action title is editable. 584 * @return true if the action title is editable, false otherwise. 585 */ 586 public boolean isEditable() { 587 return mEditable; 588 } 589 590 /** 591 * Returns whether this action description is editable. 592 * @return true if the action description is editable, false otherwise. 593 */ 594 public boolean isDescriptionEditable() { 595 return mDescriptionEditable; 596 } 597 598 /** 599 * Returns InputType of action title in editing; only valid when {@link #isEditable()} is true. 600 * @return InputType of action title in editing. 601 */ 602 public int getEditInputType() { 603 return mEditInputType; 604 } 605 606 /** 607 * Returns InputType of action description in editing; only valid when 608 * {@link #isDescriptionEditable()} is true. 609 * @return InputType of action description in editing. 610 */ 611 public int getDescriptionEditInputType() { 612 return mDescriptionEditInputType; 613 } 614 615 /** 616 * Returns InputType of action title not in editing. 617 * @return InputType of action title not in editing. 618 */ 619 public int getInputType() { 620 return mInputType; 621 } 622 623 /** 624 * Returns InputType of action description not in editing. 625 * @return InputType of action description not in editing. 626 */ 627 public int getDescriptionInputType() { 628 return mDescriptionInputType; 629 } 630 631 /** 632 * Returns whether this action is checked. 633 * @return true if the action is currently checked, false otherwise. 634 */ 635 public boolean isChecked() { 636 return mChecked; 637 } 638 639 /** 640 * Sets whether this action is checked. 641 * @param checked Whether this action should be checked. 642 */ 643 public void setChecked(boolean checked) { 644 mChecked = checked; 645 } 646 647 /** 648 * Returns the check set id this action is a part of. All actions in the same list with the same 649 * check set id are considered linked. When one of the actions within that set is selected, that 650 * action becomes checked, while all the other actions become unchecked. 651 * 652 * @return an integer representing the check set this action is a part of, or 653 * {@link #CHECKBOX_CHECK_SET_ID} if this is a checkbox, or {@link #NO_CHECK_SET} if 654 * this action is not a checkbox or radiobutton. 655 */ 656 public int getCheckSetId() { 657 return mCheckSetId; 658 } 659 660 /** 661 * Returns whether this action is has a multiline description. 662 * @return true if the action was constructed as having a multiline description, false 663 * otherwise. 664 */ 665 public boolean hasMultilineDescription() { 666 return mMultilineDescription; 667 } 668 669 /** 670 * Returns whether this action is enabled. 671 * @return true if the action is currently enabled, false otherwise. 672 */ 673 public boolean isEnabled() { 674 return mEnabled; 675 } 676 677 /** 678 * Sets whether this action is enabled. 679 * @param enabled Whether this action should be enabled. 680 */ 681 public void setEnabled(boolean enabled) { 682 mEnabled = enabled; 683 } 684 685 /** 686 * Returns whether this action is focusable. 687 * @return true if the action is currently focusable, false otherwise. 688 */ 689 public boolean isFocusable() { 690 return mFocusable; 691 } 692 693 /** 694 * Sets whether this action is focusable. 695 * @param focusable Whether this action should be focusable. 696 */ 697 public void setFocusable(boolean focusable) { 698 mFocusable = focusable; 699 } 700 701 /** 702 * Returns whether this action will request further user input when selected, such as showing 703 * another GuidedStepFragment or launching a new activity. Configured during construction. 704 * @return true if the action will request further user input when selected, false otherwise. 705 */ 706 public boolean hasNext() { 707 return mHasNext; 708 } 709 710 /** 711 * Returns whether the action will only display information and is thus not clickable. If both 712 * this and {@link #hasNext()} are true, infoOnly takes precedence. The default is false. For 713 * example, this might represent e.g. the amount of storage a document uses, or the cost of an 714 * app. 715 * @return true if will only display information, false otherwise. 716 */ 717 public boolean infoOnly() { 718 return mInfoOnly; 719 } 720 721 /** 722 * Change sub actions list. 723 * @param actions Sub actions list to set on this action. Sets null to disable sub actions. 724 */ 725 public void setSubActions(List<GuidedAction> actions) { 726 mSubActions = actions; 727 } 728 729 /** 730 * @return List of sub actions or null if sub actions list is not enabled. 731 */ 732 public List<GuidedAction> getSubActions() { 733 return mSubActions; 734 } 735 736 /** 737 * @return True if has sub actions list, even it's currently empty. 738 */ 739 public boolean hasSubActions() { 740 return mSubActions != null; 741 } 742 743} 744