1 // Copyright 2016 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chrome.browser.autofill.prefeditor; 6 7 import android.text.TextUtils; 8 import android.text.TextWatcher; 9 import android.util.Pair; 10 11 import androidx.annotation.Nullable; 12 13 import org.chromium.base.Callback; 14 import org.chromium.chrome.browser.autofill.settings.AutofillProfileBridge.DropdownKeyValue; 15 import org.chromium.chrome.browser.preferences.SharedPreferencesManager; 16 17 import java.util.ArrayList; 18 import java.util.HashMap; 19 import java.util.HashSet; 20 import java.util.List; 21 import java.util.Set; 22 23 /** 24 * Representation of a single input text field in an editor. Can be used, for example, for a phone 25 * input field. 26 */ 27 public class EditorFieldModel { 28 /** 29 * The interface to be implemented by the field validator. 30 */ 31 public interface EditorFieldValidator { 32 /** 33 * Called to check the validity of the field value. 34 * 35 * @param value The value of the field to check. 36 * @return True if the value is valid. 37 */ isValid(@ullable CharSequence value)38 boolean isValid(@Nullable CharSequence value); 39 40 /** 41 * Called to check whehter the length of the field value is maximum. 42 * 43 * @param value The value of the field to check. 44 * @return True if the field value length is maximum among all the possible valid values in 45 * this field. 46 */ isLengthMaximum(@ullable CharSequence value)47 boolean isLengthMaximum(@Nullable CharSequence value); 48 } 49 50 /** 51 * The interface to be implemented by the field value icon generator. 52 */ 53 public interface EditorValueIconGenerator { 54 /** 55 * Called to get the field value icon resource Id. 56 * @param value The value of the field. 57 * @return The resouce Id of the value icon, 0 indicates no icon. 58 */ getIconResourceId(@ullable CharSequence value)59 int getIconResourceId(@Nullable CharSequence value); 60 } 61 62 private static final int INPUT_TYPE_HINT_MIN_INCLUSIVE = 0; 63 64 /** Text input with no special formatting rules, e.g., a city, a suburb, or a company name. */ 65 private static final int INPUT_TYPE_HINT_NONE = 0; 66 67 /** Indicates a phone field. */ 68 public static final int INPUT_TYPE_HINT_PHONE = 1; 69 70 /** Indicates an email field. */ 71 public static final int INPUT_TYPE_HINT_EMAIL = 2; 72 73 /** Indicates a multi-line address field that may include numbers. */ 74 public static final int INPUT_TYPE_HINT_STREET_LINES = 3; 75 76 /** Indicates a person's name. */ 77 public static final int INPUT_TYPE_HINT_PERSON_NAME = 4; 78 79 /** Indicates a region or an administrative area, e.g., a state or a province. */ 80 public static final int INPUT_TYPE_HINT_REGION = 5; 81 82 /** Indicates an alpha-numeric value, e.g., postal code or sorting code. */ 83 public static final int INPUT_TYPE_HINT_ALPHA_NUMERIC = 6; 84 85 /** Indicates a credit card input. */ 86 public static final int INPUT_TYPE_HINT_CREDIT_CARD = 7; 87 88 private static final int INPUT_TYPE_HINT_MAX_TEXT_INPUT_EXCLUSIVE = 8; 89 90 /** Indicates a dropdown. */ 91 public static final int INPUT_TYPE_HINT_DROPDOWN = 9; 92 93 /** Indicates a list of icons. */ 94 public static final int INPUT_TYPE_HINT_ICONS = 10; 95 96 /** Indicates a checkbox. */ 97 public static final int INPUT_TYPE_HINT_CHECKBOX = 11; 98 99 /** 100 * Indicates a label, e.g., for a server credit card. 101 * 102 * TOP_LABEL 103 * MID_LABEL [ICON] 104 * BOTTOM_LABEL 105 * 106 * Example: 107 * 108 * Visa***1234 109 * First Last [VISA] 110 * Exp: 03/2021 111 */ 112 public static final int INPUT_TYPE_HINT_LABEL = 12; 113 114 private static final int INPUT_TYPE_HINT_MAX_EXCLUSIVE = 13; 115 116 private final int mInputTypeHint; 117 118 @Nullable 119 private List<Integer> mIconResourceIds; 120 @Nullable 121 private List<Integer> mIconDescriptionsForAccessibility; 122 @Nullable 123 private List<DropdownKeyValue> mDropdownKeyValues; 124 @Nullable 125 private HashMap<String, CharSequence> mDropdownKeyToValueMap; 126 @Nullable 127 private HashMap<CharSequence, String> mDropdownValueToKeyMap; 128 @Nullable 129 private Set<String> mDropdownKeys; 130 @Nullable 131 private List<CharSequence> mSuggestions; 132 @Nullable 133 private TextWatcher mFormatter; 134 @Nullable 135 private EditorFieldValidator mValidator; 136 @Nullable 137 private EditorValueIconGenerator mValueIconGenerator; 138 @Nullable 139 private CharSequence mRequiredErrorMessage; 140 @Nullable 141 private CharSequence mInvalidErrorMessage; 142 @Nullable 143 private CharSequence mCustomErrorMessage; 144 @Nullable 145 private CharSequence mErrorMessage; 146 @Nullable 147 private CharSequence mLabel; 148 @Nullable 149 private CharSequence mMidLabel; 150 @Nullable 151 private CharSequence mBottomLabel; 152 @Nullable 153 private CharSequence mValue; 154 @Nullable 155 private CharSequence mHint; 156 @Nullable 157 private Callback<Pair<String, Runnable>> mDropdownCallback; 158 @Nullable 159 private Runnable mActionIconAction; 160 private int mLabelIconResourceId; 161 private int mActionIconResourceId; 162 private int mActionIconDescriptionForAccessibility; 163 private boolean mIsFullLine = true; 164 private boolean mPlusIconIsDisplayed; 165 166 /** 167 * Constructs a label to show in the editor. This can be, for example, description of a server 168 * credit card and its icon. Layout: 169 * 170 * topLabel 171 * midLabel iconId 172 * bottomLabel 173 * 174 * @param topLabel Top label. 175 * @param midLabel Middle label. 176 * @param bottomLabel Bottom label. 177 * @param iconId Icon. 178 */ createLabel( CharSequence topLabel, CharSequence midLabel, CharSequence bottomLabel, int iconId)179 public static EditorFieldModel createLabel( 180 CharSequence topLabel, CharSequence midLabel, CharSequence bottomLabel, int iconId) { 181 assert topLabel != null; 182 assert midLabel != null; 183 assert bottomLabel != null; 184 EditorFieldModel result = new EditorFieldModel(INPUT_TYPE_HINT_LABEL); 185 result.mLabel = topLabel; 186 result.mMidLabel = midLabel; 187 result.mBottomLabel = bottomLabel; 188 result.mLabelIconResourceId = iconId; 189 return result; 190 } 191 192 /** 193 * Constructs a checkbox to show in the editor. It's checked by default. 194 * 195 * @param checkboxLabel The label for the checkbox. 196 * @param checkboxPreference The shared preference key for the checkbox status. It must be in 197 * ChromePreferenceKeys. 198 */ createCheckbox( CharSequence checkboxLabel, CharSequence checkboxPreference)199 public static EditorFieldModel createCheckbox( 200 CharSequence checkboxLabel, CharSequence checkboxPreference) { 201 assert checkboxLabel != null; 202 assert checkboxPreference != null; 203 EditorFieldModel result = new EditorFieldModel(INPUT_TYPE_HINT_CHECKBOX); 204 result.mLabel = checkboxLabel; 205 result.mValue = checkboxPreference; 206 return result; 207 } 208 209 /** 210 * Constructs a list of icons to show in the editor. This can be, for example, the list of 211 * accepted credit cards. 212 * 213 * @param label The label for the icons. 214 * @param iconIds The list of drawable resources to display, in this order. 215 * @param descIds The list of string identifiers for descriptions of the icons. This is for 216 * accessibility. 217 */ createIconList( CharSequence label, List<Integer> iconIds, List<Integer> descIds)218 public static EditorFieldModel createIconList( 219 CharSequence label, List<Integer> iconIds, List<Integer> descIds) { 220 assert label != null; 221 assert iconIds != null; 222 assert descIds != null; 223 EditorFieldModel result = new EditorFieldModel(INPUT_TYPE_HINT_ICONS); 224 result.mLabel = label; 225 result.mIconResourceIds = iconIds; 226 result.mIconDescriptionsForAccessibility = descIds; 227 return result; 228 } 229 230 /** 231 * Constructs a dropdown field model. 232 * 233 * @param label The human-readable label for user to understand the type of data 234 * that should be entered into this field. 235 * @param dropdownKeyValues The keyed values to display in the dropdown. 236 * @param hint The optional hint to be displayed when no value is selected. 237 */ createDropdown(@ullable CharSequence label, List<DropdownKeyValue> dropdownKeyValues, @Nullable CharSequence hint)238 public static EditorFieldModel createDropdown(@Nullable CharSequence label, 239 List<DropdownKeyValue> dropdownKeyValues, @Nullable CharSequence hint) { 240 assert dropdownKeyValues != null; 241 EditorFieldModel result = new EditorFieldModel(INPUT_TYPE_HINT_DROPDOWN); 242 result.mLabel = label; 243 result.mHint = hint; 244 result.setDropdownKeyValues(dropdownKeyValues); 245 return result; 246 } 247 248 /** 249 * Constructs a dropdown field model with a validator. 250 * 251 * @param label The human-readable label for user to understand the type of data 252 * that should be entered into this field. 253 * @param dropdownKeyValues The keyed values to display in the dropdown. 254 * @param validator The validator for the values in this field. 255 * @param requiredErrorMessage The error message that indicates to the user that they 256 * cannot leave this field empty. 257 */ createDropdown(@ullable CharSequence label, List<DropdownKeyValue> dropdownKeyValues, EditorFieldValidator validator, CharSequence invalidErrorMessage)258 public static EditorFieldModel createDropdown(@Nullable CharSequence label, 259 List<DropdownKeyValue> dropdownKeyValues, EditorFieldValidator validator, 260 CharSequence invalidErrorMessage) { 261 assert dropdownKeyValues != null; 262 assert validator != null; 263 assert invalidErrorMessage != null; 264 EditorFieldModel result = createDropdown(label, dropdownKeyValues, null /* hint */); 265 result.mValidator = validator; 266 result.mInvalidErrorMessage = invalidErrorMessage; 267 return result; 268 } 269 270 /** Constructs a text input field model without any special text formatting hints. */ createTextInput()271 public static EditorFieldModel createTextInput() { 272 return new EditorFieldModel(INPUT_TYPE_HINT_NONE); 273 } 274 275 /** 276 * Constructs a text input field model. 277 * 278 * @param inputTypeHint The type of input. For example, INPUT_TYPE_HINT_PHONE. 279 */ createTextInput(int inputTypeHint)280 public static EditorFieldModel createTextInput(int inputTypeHint) { 281 EditorFieldModel result = new EditorFieldModel(inputTypeHint); 282 assert result.isTextField(); 283 return result; 284 } 285 286 /** 287 * Constructs a text input field model. 288 * 289 * @param inputTypeHint The type of input. For example, INPUT_TYPE_HINT_PHONE. 290 * @param label The human-readable label for user to understand the type of data 291 * that should be entered into this field. 292 * @param suggestions Optional set of values to suggest to the user. 293 * @param formatter Optional formatter for the values in this field. 294 * @param validator Optional validator for the values in this field. 295 * @param valueIconGenerator Optional icon generator for the values in this field. 296 * @param requiredErrorMessage The optional error message that indicates to the user that they 297 * cannot leave this field empty. 298 * @param invalidErrorMessage The optional error message that indicates to the user that the 299 * value they have entered is not valid. 300 * @param value Optional initial value of this field. 301 */ createTextInput(int inputTypeHint, CharSequence label, @Nullable Set<CharSequence> suggestions, @Nullable TextWatcher formatter, @Nullable EditorFieldValidator validator, @Nullable EditorValueIconGenerator valueIconGenerator, @Nullable CharSequence requiredErrorMessage, @Nullable CharSequence invalidErrorMessage, @Nullable CharSequence value)302 public static EditorFieldModel createTextInput(int inputTypeHint, CharSequence label, 303 @Nullable Set<CharSequence> suggestions, @Nullable TextWatcher formatter, 304 @Nullable EditorFieldValidator validator, 305 @Nullable EditorValueIconGenerator valueIconGenerator, 306 @Nullable CharSequence requiredErrorMessage, @Nullable CharSequence invalidErrorMessage, 307 @Nullable CharSequence value) { 308 assert label != null; 309 EditorFieldModel result = new EditorFieldModel(inputTypeHint); 310 assert result.isTextField(); 311 result.mSuggestions = suggestions == null ? null : new ArrayList<CharSequence>(suggestions); 312 result.mFormatter = formatter; 313 result.mValidator = validator; 314 result.mValueIconGenerator = valueIconGenerator; 315 result.mInvalidErrorMessage = invalidErrorMessage; 316 result.mRequiredErrorMessage = requiredErrorMessage; 317 result.mLabel = label; 318 result.mValue = value; 319 return result; 320 } 321 322 /** 323 * Adds an icon to a text input field. The icon can be tapped to perform an action, e.g., launch 324 * a credit card scanner. 325 * 326 * @param icon The drawable resource for the icon. 327 * @param description The string resource for the human readable description of the action. 328 * @param action The callback to invoke when the icon is tapped. 329 */ addActionIcon(int icon, int description, Runnable action)330 public void addActionIcon(int icon, int description, Runnable action) { 331 assert isTextField(); 332 mActionIconResourceId = icon; 333 mActionIconDescriptionForAccessibility = description; 334 mActionIconAction = action; 335 } 336 EditorFieldModel(int inputTypeHint)337 private EditorFieldModel(int inputTypeHint) { 338 assert isTextField(); 339 mInputTypeHint = inputTypeHint; 340 } 341 342 /** @return The action icon resource identifier, for example, R.drawable.ocr_card. */ getActionIconResourceId()343 public int getActionIconResourceId() { 344 assert isTextField(); 345 return mActionIconResourceId; 346 } 347 348 /** @return The string resource for the human readable description of the action icon. */ getActionIconDescriptionForAccessibility()349 public int getActionIconDescriptionForAccessibility() { 350 assert isTextField(); 351 return mActionIconDescriptionForAccessibility; 352 } 353 354 /** @return The action to invoke when the action icon has been tapped. */ getActionIconAction()355 public Runnable getActionIconAction() { 356 assert isTextField(); 357 return mActionIconAction; 358 } 359 360 /** @return The value formatter or null if not exist. */ 361 @Nullable getFormatter()362 public TextWatcher getFormatter() { 363 assert isTextField(); 364 return mFormatter; 365 } 366 367 /** @return The value icon generator or null if not exist. */ getValueIconGenerator()368 public EditorValueIconGenerator getValueIconGenerator() { 369 assert isTextField(); 370 return mValueIconGenerator; 371 } 372 373 /** @return Whether the input is a text field. */ isTextField()374 public boolean isTextField() { 375 return mInputTypeHint >= INPUT_TYPE_HINT_MIN_INCLUSIVE 376 && mInputTypeHint < INPUT_TYPE_HINT_MAX_TEXT_INPUT_EXCLUSIVE; 377 } 378 379 /** @return Whether the input is a dropdown field. */ isDropdownField()380 public boolean isDropdownField() { 381 return mInputTypeHint == INPUT_TYPE_HINT_DROPDOWN; 382 } 383 384 /** @return The type of input, for example, INPUT_TYPE_HINT_PHONE. */ getInputTypeHint()385 public int getInputTypeHint() { 386 return mInputTypeHint; 387 } 388 389 /** @return Whether the checkbox is checked. */ isChecked()390 public boolean isChecked() { 391 assert mInputTypeHint == INPUT_TYPE_HINT_CHECKBOX; 392 return SharedPreferencesManager.getInstance().readBoolean(mValue.toString(), true); 393 } 394 395 /** Sets the checkbox state. */ setIsChecked(boolean isChecked)396 public void setIsChecked(boolean isChecked) { 397 assert mInputTypeHint == INPUT_TYPE_HINT_CHECKBOX; 398 SharedPreferencesManager.getInstance().writeBoolean(mValue.toString(), isChecked); 399 } 400 401 /** @return The list of icons resource identifiers to display. */ getIconResourceIds()402 public List<Integer> getIconResourceIds() { 403 assert mInputTypeHint == INPUT_TYPE_HINT_ICONS; 404 return mIconResourceIds; 405 } 406 407 /** 408 * @return The list of string identifiers of the descriptions of the displayed icons. This is 409 * for the screen reader. 410 */ getIconDescriptionsForAccessibility()411 public List<Integer> getIconDescriptionsForAccessibility() { 412 assert mInputTypeHint == INPUT_TYPE_HINT_ICONS; 413 return mIconDescriptionsForAccessibility; 414 } 415 416 /** @return The dropdown key-value pairs. */ getDropdownKeyValues()417 public List<DropdownKeyValue> getDropdownKeyValues() { 418 assert mInputTypeHint == INPUT_TYPE_HINT_DROPDOWN; 419 return mDropdownKeyValues; 420 } 421 422 /** @return The dropdown keys. */ getDropdownKeys()423 public Set<String> getDropdownKeys() { 424 assert mInputTypeHint == INPUT_TYPE_HINT_DROPDOWN; 425 return mDropdownKeys; 426 } 427 428 /** 429 * In the dropdown list, finds the key that corresponds to the value. 430 * If the value is not in the list, returns null. 431 * This assumes a one-to-one relation between keys and values. 432 * 433 * @param value The value to lookup. 434 * @return The key that corresponds to the value. 435 */ 436 @Nullable getDropdownKeyByValue(@ullable CharSequence value)437 public String getDropdownKeyByValue(@Nullable CharSequence value) { 438 assert mInputTypeHint == INPUT_TYPE_HINT_DROPDOWN; 439 return mDropdownValueToKeyMap.get(value); 440 } 441 442 /** 443 * In the dropdown list, finds the value that corresponds to the key. 444 * If the key is not in the list, returns null. 445 * This assumes a one-to-one relation between keys and values. 446 * 447 * @param key The key to lookup. 448 * @return The value that corresponds to the key. 449 */ 450 @Nullable getDropdownValueByKey(@ullable String key)451 public CharSequence getDropdownValueByKey(@Nullable String key) { 452 assert mInputTypeHint == INPUT_TYPE_HINT_DROPDOWN; 453 return mDropdownKeyToValueMap.get(key); 454 } 455 456 /** Updates the dropdown key values. */ setDropdownKeyValues(List<DropdownKeyValue> dropdownKeyValues)457 public void setDropdownKeyValues(List<DropdownKeyValue> dropdownKeyValues) { 458 assert mInputTypeHint == INPUT_TYPE_HINT_DROPDOWN; 459 mDropdownKeyValues = dropdownKeyValues; 460 mDropdownKeys = new HashSet<>(); 461 mDropdownKeyToValueMap = new HashMap<>(); 462 mDropdownValueToKeyMap = new HashMap<>(); 463 for (int i = 0; i < mDropdownKeyValues.size(); i++) { 464 mDropdownKeys.add(mDropdownKeyValues.get(i).getKey()); 465 mDropdownValueToKeyMap.put( 466 mDropdownKeyValues.get(i).getValue(), mDropdownKeyValues.get(i).getKey()); 467 mDropdownKeyToValueMap.put( 468 mDropdownKeyValues.get(i).getKey(), mDropdownKeyValues.get(i).getValue()); 469 } 470 assert mDropdownKeyValues.size() == mDropdownKeys.size(); 471 } 472 473 /** 474 * @return True if the last visible item on the dropdown list, needs a '+' icon on the right. 475 */ isDisplayPlusIcon()476 public boolean isDisplayPlusIcon() { 477 assert mInputTypeHint == INPUT_TYPE_HINT_DROPDOWN; 478 return mPlusIconIsDisplayed; 479 } 480 481 /** 482 * Sets whether the last item of this dropdown list needs a '+' icon on the right. By 483 * default, the item doesn't need that. 484 * 485 * @param plusIconIsDisplayed Whether the last item of the this dropdown list needs a '+' icon. 486 */ setDisplayPlusIcon(boolean plusIconIsDisplayed)487 public void setDisplayPlusIcon(boolean plusIconIsDisplayed) { 488 assert mInputTypeHint == INPUT_TYPE_HINT_DROPDOWN; 489 mPlusIconIsDisplayed = plusIconIsDisplayed; 490 } 491 492 /** @return The human-readable label for this field. */ getLabel()493 public CharSequence getLabel() { 494 return mLabel; 495 } 496 497 /** @return The human-readable hint for this dropdown field. */ getHint()498 public CharSequence getHint() { 499 assert mInputTypeHint == INPUT_TYPE_HINT_DROPDOWN; 500 return mHint; 501 } 502 503 /** @return The human-readable mid-level label for this field. */ getMidLabel()504 public CharSequence getMidLabel() { 505 assert mInputTypeHint == INPUT_TYPE_HINT_LABEL; 506 return mMidLabel; 507 } 508 509 /** @return The human-readable lower-level label for this field. */ getBottomLabel()510 public CharSequence getBottomLabel() { 511 assert mInputTypeHint == INPUT_TYPE_HINT_LABEL; 512 return mBottomLabel; 513 } 514 515 /** @return The icon to show next to the label. */ getLabelIconResourceId()516 public int getLabelIconResourceId() { 517 assert mInputTypeHint == INPUT_TYPE_HINT_LABEL; 518 return mLabelIconResourceId; 519 } 520 521 /** 522 * Updates the label. 523 * 524 * @param label The new label to use. 525 */ setLabel(CharSequence label)526 public void setLabel(CharSequence label) { 527 mLabel = label; 528 } 529 530 /** @return Suggested values for this field. Can be null. */ 531 @Nullable getSuggestions()532 public List<CharSequence> getSuggestions() { 533 return mSuggestions; 534 } 535 536 /** @return The error message for the last validation. Can be null if no error was reported. */ 537 @Nullable getErrorMessage()538 public CharSequence getErrorMessage() { 539 return mErrorMessage; 540 } 541 542 /** Updates the custom error message */ setCustomErrorMessage(@ullable CharSequence errorMessage)543 public void setCustomErrorMessage(@Nullable CharSequence errorMessage) { 544 mCustomErrorMessage = errorMessage; 545 } 546 547 /** 548 * @return The value that the user has typed into the field or the key of the value that the 549 * user has selected in the dropdown. Can be null. 550 */ 551 @Nullable getValue()552 public CharSequence getValue() { 553 return mValue; 554 } 555 556 /** 557 * Updates the value of this field. Does not trigger validation or update the last error 558 * message. Can be called on a dropdown to initialize it, but will not fire the dropdown 559 * callback. 560 * 561 * @param value The new value that the user has typed in or the initial key for the dropdown. 562 */ setValue(@ullable CharSequence userTypedValueOrInitialDropdownKey)563 public void setValue(@Nullable CharSequence userTypedValueOrInitialDropdownKey) { 564 mValue = userTypedValueOrInitialDropdownKey; 565 } 566 567 /** 568 * Updates the dropdown selection and fires the dropdown callback. 569 * 570 * @param key The new dropdown key. 571 * @param callback The callback to invoke when the change has been processed. 572 */ setDropdownKey(@ullable String key, Runnable callback)573 public void setDropdownKey(@Nullable String key, Runnable callback) { 574 assert mInputTypeHint == INPUT_TYPE_HINT_DROPDOWN; 575 // The mValue can only be set to null if there is a hint. 576 if (key == null && mHint == null) { 577 return; 578 } 579 mValue = key; 580 if (mDropdownCallback != null) { 581 mDropdownCallback.onResult(new Pair<String, Runnable>(key, callback)); 582 } 583 } 584 585 /** @return Whether or not the field is required. */ isRequired()586 public boolean isRequired() { 587 return !TextUtils.isEmpty(mRequiredErrorMessage); 588 } 589 590 /** 591 * Updates the required error message. 592 * 593 * @param message The error message to use if this field is required, but empty. If null, then 594 * this field is optional. 595 */ setRequiredErrorMessage(@ullable CharSequence message)596 public void setRequiredErrorMessage(@Nullable CharSequence message) { 597 mRequiredErrorMessage = message; 598 } 599 600 /** 601 * Returns true if the field value is valid. Also updates the error message. 602 * 603 * @return Whether the field value is valid. 604 */ isValid()605 public boolean isValid() { 606 if (!TextUtils.isEmpty(mCustomErrorMessage)) { 607 mErrorMessage = mCustomErrorMessage; 608 return false; 609 } 610 611 if (isRequired() 612 && (TextUtils.isEmpty(mValue) || TextUtils.getTrimmedLength(mValue) == 0)) { 613 mErrorMessage = mRequiredErrorMessage; 614 return false; 615 } 616 617 if (mValidator != null && !mValidator.isValid(mValue)) { 618 mErrorMessage = mInvalidErrorMessage; 619 return false; 620 } 621 622 mErrorMessage = null; 623 return true; 624 } 625 626 /** 627 * Returns true if the field value length is maximum among all the possible valid values in this 628 * field. 629 * 630 * @Return Whether the field value length is maximum. 631 */ isLengthMaximum()632 public boolean isLengthMaximum() { 633 return mValidator == null ? false : mValidator.isLengthMaximum(mValue); 634 } 635 636 /** 637 * Sets the dropdown callback. 638 * 639 * @param callback The callback to invoke when the dropdown selection has changed. The first 640 * element in the callback pair is the dropdown key. The second element is the 641 * callback to invoke after the dropdown change has been processed. 642 */ setDropdownCallback(Callback<Pair<String, Runnable>> callback)643 public void setDropdownCallback(Callback<Pair<String, Runnable>> callback) { 644 assert mInputTypeHint == INPUT_TYPE_HINT_DROPDOWN; 645 mDropdownCallback = callback; 646 } 647 648 /** 649 * @return True if the input field should take up the full line, instead of sharing with other 650 * input fields. This is the default. 651 */ isFullLine()652 public boolean isFullLine() { 653 return mIsFullLine; 654 } 655 656 /** 657 * Sets whether this input field should take up the full line. All fields take up the full line 658 * by default. 659 * 660 * @param isFullLine Whether the input field should take up the full line. 661 */ setIsFullLine(boolean isFullLine)662 public void setIsFullLine(boolean isFullLine) { 663 mIsFullLine = isFullLine; 664 } 665 } 666