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