TextInputLayout.java revision 66698bb15ba0f873aa1c2290cc50d6bb839a474a
1c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes/*
2c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * Copyright (C) 2015 The Android Open Source Project
3c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes *
4c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * Licensed under the Apache License, Version 2.0 (the "License");
5c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * you may not use this file except in compliance with the License.
6c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * You may obtain a copy of the License at
7c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes *
8c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes *      http://www.apache.org/licenses/LICENSE-2.0
9c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes *
10c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * Unless required by applicable law or agreed to in writing, software
11c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * distributed under the License is distributed on an "AS IS" BASIS,
12c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * See the License for the specific language governing permissions and
14c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * limitations under the License.
15c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes */
16c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
17c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banespackage android.support.design.widget;
18c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
19c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.content.Context;
202aa57abd4531068424642e1a5dfff47c17b84c2eChris Banesimport android.content.res.ColorStateList;
21c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.content.res.TypedArray;
22c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.graphics.Canvas;
23c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.graphics.Color;
24c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.graphics.Paint;
25388841ca2f07d0da9bbdc66c51abe789e05cf17bChris Banesimport android.graphics.Typeface;
26d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banesimport android.support.annotation.NonNull;
272aa57abd4531068424642e1a5dfff47c17b84c2eChris Banesimport android.support.annotation.Nullable;
282aa57abd4531068424642e1a5dfff47c17b84c2eChris Banesimport android.support.annotation.StyleRes;
29c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.support.design.R;
30c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.support.v4.view.AccessibilityDelegateCompat;
31d5f3a08bb9e1c4b09237f16263aff953dca63904Chris Banesimport android.support.v4.view.GravityCompat;
32c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.support.v4.view.ViewCompat;
33c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
34c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
354452ce91a49bbf820282a90601becfe61d64fcfbChris Banesimport android.support.v4.widget.Space;
3666698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banesimport android.support.v7.widget.AppCompatDrawableManager;
37c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.text.Editable;
38c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.text.TextUtils;
39c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.text.TextWatcher;
40c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.util.AttributeSet;
41c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.util.TypedValue;
42c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.view.Gravity;
43c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.view.View;
44c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.view.ViewGroup;
45c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.view.accessibility.AccessibilityEvent;
46c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.view.animation.AccelerateInterpolator;
47c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.widget.EditText;
48c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.widget.LinearLayout;
49c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.widget.TextView;
50c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
51c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes/**
529fb154338a62edc2c57dc036895199d6f1769400Chris Banes * Layout which wraps an {@link android.widget.EditText} (or descendant) to show a floating label
539fb154338a62edc2c57dc036895199d6f1769400Chris Banes * when the hint is hidden due to the user inputting text.
549fb154338a62edc2c57dc036895199d6f1769400Chris Banes *
559fb154338a62edc2c57dc036895199d6f1769400Chris Banes * Also supports showing an error via {@link #setErrorEnabled(boolean)} and
569fb154338a62edc2c57dc036895199d6f1769400Chris Banes * {@link #setError(CharSequence)}.
57c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes */
58c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banespublic class TextInputLayout extends LinearLayout {
59c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
60f44e88b81821e61a65886cc7027617ca8995070bChris Banes    private static final int ANIMATION_DURATION = 200;
61249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    private static final int INVALID_MAX_LENGTH = -1;
62c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
63c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    private EditText mEditText;
64c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    private CharSequence mHint;
65c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
662aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    private Paint mTmpPaint;
672aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes
68249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    private LinearLayout mIndicatorArea;
69249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki
70c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    private boolean mErrorEnabled;
71c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    private TextView mErrorView;
72c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    private int mErrorTextAppearance;
73249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    private boolean mErrorShown;
74249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki
75249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    private boolean mCounterEnabled;
76249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    private TextView mCounterView;
77249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    private int mCounterMaxLength;
78249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    private int mCounterTextAppearance;
79249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    private int mCounterOverflowTextAppearance;
80249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    private boolean mCounterOverflowed;
81c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
822aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    private ColorStateList mDefaultTextColor;
832aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    private ColorStateList mFocusedTextColor;
84c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
85295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes    private final CollapsingTextHelper mCollapsingTextHelper = new CollapsingTextHelper(this);
86c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
87b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes    private boolean mHintAnimationEnabled;
88f44e88b81821e61a65886cc7027617ca8995070bChris Banes    private ValueAnimatorCompat mAnimator;
89f44e88b81821e61a65886cc7027617ca8995070bChris Banes
90c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    public TextInputLayout(Context context) {
91c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        this(context, null);
92c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    }
93c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
94c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    public TextInputLayout(Context context, AttributeSet attrs) {
952aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        this(context, attrs, 0);
962aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    }
972aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes
982aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    public TextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) {
992aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        // Can't call through to super(Context, AttributeSet, int) since it doesn't exist on API 10
10044dc3d86d8fa6544d49b2e14f89c9b1f49639dbdChris Banes        super(context, attrs);
101c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
102809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes        ThemeUtils.checkAppCompatTheme(context);
103809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes
104c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        setOrientation(VERTICAL);
105c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        setWillNotDraw(false);
106295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes        setAddStatesFromChildren(true);
107c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
108c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        mCollapsingTextHelper.setTextSizeInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
109c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        mCollapsingTextHelper.setPositionInterpolator(new AccelerateInterpolator());
110d5f3a08bb9e1c4b09237f16263aff953dca63904Chris Banes        mCollapsingTextHelper.setCollapsedTextGravity(Gravity.TOP | GravityCompat.START);
111c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
112c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        final TypedArray a = context.obtainStyledAttributes(attrs,
1132aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes                R.styleable.TextInputLayout, defStyleAttr, R.style.Widget_Design_TextInputLayout);
1144369f8b91f58ab2fa81b617d1d15ed0fa3185092Chris Banes        setHint(a.getText(R.styleable.TextInputLayout_android_hint));
115b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes        mHintAnimationEnabled = a.getBoolean(
116b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes                R.styleable.TextInputLayout_hintAnimationEnabled, true);
117c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
1182aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        if (a.hasValue(R.styleable.TextInputLayout_android_textColorHint)) {
1192aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes            mDefaultTextColor = mFocusedTextColor =
1202aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes                    a.getColorStateList(R.styleable.TextInputLayout_android_textColorHint);
1212aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        }
1222aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes
123c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        final int hintAppearance = a.getResourceId(
124c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                R.styleable.TextInputLayout_hintTextAppearance, -1);
125c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        if (hintAppearance != -1) {
1262aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes            setHintTextAppearance(
1272aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes                    a.getResourceId(R.styleable.TextInputLayout_hintTextAppearance, 0));
128c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        }
129c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
130c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        mErrorTextAppearance = a.getResourceId(R.styleable.TextInputLayout_errorTextAppearance, 0);
131c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        final boolean errorEnabled = a.getBoolean(R.styleable.TextInputLayout_errorEnabled, false);
132249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki
133249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        final boolean counterEnabled = a.getBoolean(
134249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                R.styleable.TextInputLayout_counterEnabled, false);
135249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        setCounterMaxLength(
136249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                a.getInt(R.styleable.TextInputLayout_counterMaxLength, INVALID_MAX_LENGTH));
137249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        mCounterTextAppearance = a.getResourceId(
138249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                R.styleable.TextInputLayout_counterTextAppearance, 0);
139249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        mCounterOverflowTextAppearance = a.getResourceId(
140249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                R.styleable.TextInputLayout_counterOverflowTextAppearance, 0);
141c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        a.recycle();
142c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
1432aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        setErrorEnabled(errorEnabled);
144249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        setCounterEnabled(counterEnabled);
145c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
146c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        if (ViewCompat.getImportantForAccessibility(this)
147c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
148c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            // Make sure we're important for accessibility if we haven't been explicitly not
149c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            ViewCompat.setImportantForAccessibility(this,
150c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
151c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        }
152c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
153c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        ViewCompat.setAccessibilityDelegate(this, new TextInputAccessibilityDelegate());
154c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    }
155c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
156c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    @Override
157c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    public void addView(View child, int index, ViewGroup.LayoutParams params) {
158c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        if (child instanceof EditText) {
1592aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes            setEditText((EditText) child);
1602aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes            super.addView(child, 0, updateEditTextMargin(params));
161c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        } else {
162c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            // Carry on adding the View...
163c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            super.addView(child, index, params);
164c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        }
165c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    }
166c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
167388841ca2f07d0da9bbdc66c51abe789e05cf17bChris Banes    /**
168d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes     * Set the typeface to use for both the expanded and floating hint.
169388841ca2f07d0da9bbdc66c51abe789e05cf17bChris Banes     *
170388841ca2f07d0da9bbdc66c51abe789e05cf17bChris Banes     * @param typeface typeface to use, or {@code null} to use the default.
171388841ca2f07d0da9bbdc66c51abe789e05cf17bChris Banes     */
1722aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    public void setTypeface(@Nullable Typeface typeface) {
173d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes        mCollapsingTextHelper.setTypefaces(typeface);
174d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes    }
175d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes
176d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes    /**
177d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes     * Returns the typeface used for both the expanded and floating hint.
178d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes     */
179d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes    @NonNull
180d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes    public Typeface getTypeface() {
181d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes        // This could be either the collapsed or expanded
182d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes        return mCollapsingTextHelper.getCollapsedTypeface();
183388841ca2f07d0da9bbdc66c51abe789e05cf17bChris Banes    }
184388841ca2f07d0da9bbdc66c51abe789e05cf17bChris Banes
1852aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    private void setEditText(EditText editText) {
186c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        // If we already have an EditText, throw an exception
187c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        if (mEditText != null) {
188c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            throw new IllegalArgumentException("We already have an EditText, can only have one");
189c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        }
190c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        mEditText = editText;
191c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
192388841ca2f07d0da9bbdc66c51abe789e05cf17bChris Banes        // Use the EditText's typeface, and it's text size for our expanded text
193d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes        mCollapsingTextHelper.setTypefaces(mEditText.getTypeface());
194c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        mCollapsingTextHelper.setExpandedTextSize(mEditText.getTextSize());
195b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes        mCollapsingTextHelper.setExpandedTextGravity(mEditText.getGravity());
196c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
197c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        // Add a TextWatcher so that we know when the text input has changed
198c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        mEditText.addTextChangedListener(new TextWatcher() {
199c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            @Override
200c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            public void afterTextChanged(Editable s) {
201295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes                updateLabelVisibility(true);
202249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                if (mCounterEnabled) {
203249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                    updateCounter(s.length());
204249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                }
205c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            }
206c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
207c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            @Override
2082aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
209c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
210c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            @Override
2112aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes            public void onTextChanged(CharSequence s, int start, int before, int count) {}
212c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        });
213c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
2142aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        // Use the EditText's hint colors if we don't have one set
2152aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        if (mDefaultTextColor == null) {
2162aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes            mDefaultTextColor = mEditText.getHintTextColors();
2172aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        }
2185c42e6145cc5a917f3e97d241d55f76e2790504aChris Banes
219c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        // If we do not have a valid hint, try and retrieve it from the EditText
220c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        if (TextUtils.isEmpty(mHint)) {
221c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            setHint(mEditText.getHint());
222c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            // Clear the EditText's hint as we will display it ourselves
223c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            mEditText.setHint(null);
224c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        }
225c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
226249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        if (mCounterView != null) {
227249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            updateCounter(mEditText.getText().length());
228249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        }
229249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki
230249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        if (mIndicatorArea != null) {
231249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            adjustIndicatorPadding();
232c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        }
233c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
234c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        // Update the label visibility with no animation
235c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        updateLabelVisibility(false);
2362aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    }
237c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
2382aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    private LayoutParams updateEditTextMargin(ViewGroup.LayoutParams lp) {
2392aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        // Create/update the LayoutParams so that we can add enough top margin
240c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        // to the EditText so make room for the label
2412aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        LayoutParams llp = lp instanceof LayoutParams ? (LayoutParams) lp : new LayoutParams(lp);
2422aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes
2432aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        if (mTmpPaint == null) {
2442aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes            mTmpPaint = new Paint();
2452aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        }
246d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes        mTmpPaint.setTypeface(mCollapsingTextHelper.getCollapsedTypeface());
2472aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        mTmpPaint.setTextSize(mCollapsingTextHelper.getCollapsedTextSize());
2482aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        llp.topMargin = (int) -mTmpPaint.ascent();
249c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
2502aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        return llp;
251c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    }
252c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
253c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    private void updateLabelVisibility(boolean animate) {
2544452ce91a49bbf820282a90601becfe61d64fcfbChris Banes        final boolean hasText = mEditText != null && !TextUtils.isEmpty(mEditText.getText());
2554452ce91a49bbf820282a90601becfe61d64fcfbChris Banes        final boolean isFocused = arrayContains(getDrawableState(), android.R.attr.state_focused);
2564452ce91a49bbf820282a90601becfe61d64fcfbChris Banes        final boolean isErrorShowing = !TextUtils.isEmpty(getError());
257f85bf1e127ea645ca45637288ccfbab11ef2b415Chris Banes
2584452ce91a49bbf820282a90601becfe61d64fcfbChris Banes        if (mDefaultTextColor != null) {
2592aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes            mCollapsingTextHelper.setExpandedTextColor(mDefaultTextColor.getDefaultColor());
2604452ce91a49bbf820282a90601becfe61d64fcfbChris Banes        }
2614452ce91a49bbf820282a90601becfe61d64fcfbChris Banes
2624452ce91a49bbf820282a90601becfe61d64fcfbChris Banes        if (mCounterOverflowed && mCounterView != null) {
2634452ce91a49bbf820282a90601becfe61d64fcfbChris Banes            mCollapsingTextHelper.setCollapsedTextColor(mCounterView.getCurrentTextColor());
2644452ce91a49bbf820282a90601becfe61d64fcfbChris Banes        } else if (isErrorShowing && mErrorView != null) {
2654452ce91a49bbf820282a90601becfe61d64fcfbChris Banes            mCollapsingTextHelper.setCollapsedTextColor(mErrorView.getCurrentTextColor());
2664452ce91a49bbf820282a90601becfe61d64fcfbChris Banes        } else if (isFocused && mFocusedTextColor != null) {
2674452ce91a49bbf820282a90601becfe61d64fcfbChris Banes            mCollapsingTextHelper.setCollapsedTextColor(mFocusedTextColor.getDefaultColor());
2684452ce91a49bbf820282a90601becfe61d64fcfbChris Banes        } else if (mDefaultTextColor != null) {
2694452ce91a49bbf820282a90601becfe61d64fcfbChris Banes            mCollapsingTextHelper.setCollapsedTextColor(mDefaultTextColor.getDefaultColor());
2702aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        }
271c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
272f85bf1e127ea645ca45637288ccfbab11ef2b415Chris Banes        if (hasText || isFocused || isErrorShowing) {
273c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            // We should be showing the label so do so if it isn't already
274c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            collapseHint(animate);
275c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        } else {
276c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            // We should not be showing the label so hide it
277c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            expandHint(animate);
278c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        }
279c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    }
280c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
281c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    /**
2822aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     * Returns the {@link android.widget.EditText} used for text input.
283c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes     */
2842aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    @Nullable
285c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    public EditText getEditText() {
286c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        return mEditText;
287c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    }
288c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
289c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    /**
290c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes     * Set the hint to be displayed in the floating label
2912aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     *
2922aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     * @attr ref android.support.design.R.styleable#TextInputLayout_android_hint
293c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes     */
2942aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    public void setHint(@Nullable CharSequence hint) {
295c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        mHint = hint;
296c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        mCollapsingTextHelper.setText(hint);
297c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
298c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
299c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    }
300c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
301c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    /**
3022aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     * Returns the hint which is displayed in the floating label.
3032aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     *
3042aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     * @attr ref android.support.design.R.styleable#TextInputLayout_android_hint
3052aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     */
3062aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    @Nullable
3072aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    public CharSequence getHint() {
3082aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        return mHint;
3092aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    }
3102aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes
3112aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    /**
3122aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     * Sets the hint text color, size, style from the specified TextAppearance resource.
3132aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     *
3142aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     * @attr ref android.support.design.R.styleable#TextInputLayout_hintTextAppearance
3152aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     */
3162aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    public void setHintTextAppearance(@StyleRes int resId) {
3172aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        mCollapsingTextHelper.setCollapsedTextAppearance(resId);
3182aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        mFocusedTextColor = ColorStateList.valueOf(mCollapsingTextHelper.getCollapsedTextColor());
3192aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes
3202aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        if (mEditText != null) {
3212aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes            updateLabelVisibility(false);
3222aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes
3232aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes            // Text size might have changed so update the top margin
3242aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes            LayoutParams lp = updateEditTextMargin(mEditText.getLayoutParams());
3252aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes            mEditText.setLayoutParams(lp);
3262aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes            mEditText.requestLayout();
3272aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        }
3282aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    }
3292aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes
3304452ce91a49bbf820282a90601becfe61d64fcfbChris Banes    private void addIndicator(TextView indicator, int index) {
331249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        if (mIndicatorArea == null) {
332249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            mIndicatorArea = new LinearLayout(getContext());
333249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            mIndicatorArea.setOrientation(LinearLayout.HORIZONTAL);
3344452ce91a49bbf820282a90601becfe61d64fcfbChris Banes            addView(mIndicatorArea, LinearLayout.LayoutParams.MATCH_PARENT,
3354452ce91a49bbf820282a90601becfe61d64fcfbChris Banes                    LinearLayout.LayoutParams.WRAP_CONTENT);
3364452ce91a49bbf820282a90601becfe61d64fcfbChris Banes
3374452ce91a49bbf820282a90601becfe61d64fcfbChris Banes            // Add a flexible spacer in the middle so that the left/right views stay pinned
3384452ce91a49bbf820282a90601becfe61d64fcfbChris Banes            final Space spacer = new Space(getContext());
3394452ce91a49bbf820282a90601becfe61d64fcfbChris Banes            final LinearLayout.LayoutParams spacerLp = new LinearLayout.LayoutParams(0, 0, 1f);
3404452ce91a49bbf820282a90601becfe61d64fcfbChris Banes            mIndicatorArea.addView(spacer, spacerLp);
3414452ce91a49bbf820282a90601becfe61d64fcfbChris Banes
342249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            if (mEditText != null) {
343249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                adjustIndicatorPadding();
344249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            }
345249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        }
3464452ce91a49bbf820282a90601becfe61d64fcfbChris Banes        mIndicatorArea.setVisibility(View.VISIBLE);
3474452ce91a49bbf820282a90601becfe61d64fcfbChris Banes        mIndicatorArea.addView(indicator, index);
348249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    }
349249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki
350249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    private void adjustIndicatorPadding() {
351249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        // Add padding to the error and character counter so that they match the EditText
352249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        ViewCompat.setPaddingRelative(mIndicatorArea, ViewCompat.getPaddingStart(mEditText),
353249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                0, ViewCompat.getPaddingEnd(mEditText), mEditText.getPaddingBottom());
354249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    }
355249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki
356249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    private void removeIndicator(TextView indicator) {
3574452ce91a49bbf820282a90601becfe61d64fcfbChris Banes        if (mIndicatorArea != null) {
3584452ce91a49bbf820282a90601becfe61d64fcfbChris Banes            mIndicatorArea.removeView(indicator);
3594452ce91a49bbf820282a90601becfe61d64fcfbChris Banes            if (mIndicatorArea.getChildCount() == 0) {
3604452ce91a49bbf820282a90601becfe61d64fcfbChris Banes                mIndicatorArea.setVisibility(View.GONE);
3614452ce91a49bbf820282a90601becfe61d64fcfbChris Banes            }
362249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        }
363249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    }
364249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki
3652aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    /**
366c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes     * Whether the error functionality is enabled or not in this layout. Enabling this
367c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes     * functionality before setting an error message via {@link #setError(CharSequence)}, will mean
368c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes     * that this layout will not change size when an error is displayed.
3692aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     *
3702aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     * @attr ref android.support.design.R.styleable#TextInputLayout_errorEnabled
371c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes     */
372c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    public void setErrorEnabled(boolean enabled) {
373c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        if (mErrorEnabled != enabled) {
3741832f6e483ae37e8b92914819c7e786d6f22f84bChris Banes            if (mErrorView != null) {
3751832f6e483ae37e8b92914819c7e786d6f22f84bChris Banes                ViewCompat.animate(mErrorView).cancel();
3761832f6e483ae37e8b92914819c7e786d6f22f84bChris Banes            }
3771832f6e483ae37e8b92914819c7e786d6f22f84bChris Banes
378c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            if (enabled) {
379c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                mErrorView = new TextView(getContext());
380c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                mErrorView.setTextAppearance(getContext(), mErrorTextAppearance);
381c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                mErrorView.setVisibility(INVISIBLE);
3823224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes                ViewCompat.setAccessibilityLiveRegion(mErrorView,
3833224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes                        ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE);
3844452ce91a49bbf820282a90601becfe61d64fcfbChris Banes                addIndicator(mErrorView, 0);
385c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            } else {
386249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                mErrorShown = false;
387249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                updateEditTextBackground();
388249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                removeIndicator(mErrorView);
389c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                mErrorView = null;
390c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            }
391c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            mErrorEnabled = enabled;
392c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        }
393c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    }
394c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
395c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    /**
3962aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     * Returns whether the error functionality is enabled or not in this layout.
3972aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     *
3982aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     * @attr ref android.support.design.R.styleable#TextInputLayout_errorEnabled
3992aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     *
4002aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     * @see #setErrorEnabled(boolean)
4012aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     */
4022aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    public boolean isErrorEnabled() {
4032aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        return mErrorEnabled;
4042aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    }
4052aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes
4062aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    /**
407c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes     * Sets an error message that will be displayed below our {@link EditText}. If the
408c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes     * {@code error} is {@code null}, the error message will be cleared.
409c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes     * <p>
410c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes     * If the error functionality has not been enabled via {@link #setErrorEnabled(boolean)}, then
411c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes     * it will be automatically enabled if {@code error} is not empty.
412c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes     *
413c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes     * @param error Error message to display, or null to clear
4142aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     *
4152aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     * @see #getError()
416c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes     */
4172aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    public void setError(@Nullable CharSequence error) {
418c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        if (!mErrorEnabled) {
419c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            if (TextUtils.isEmpty(error)) {
420c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                // If error isn't enabled, and the error is empty, just return
421c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                return;
422c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            }
423c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            // Else, we'll assume that they want to enable the error functionality
424c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            setErrorEnabled(true);
425c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        }
426c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
427c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        if (!TextUtils.isEmpty(error)) {
428c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            ViewCompat.setAlpha(mErrorView, 0f);
4293104067991a7d66e9e10e7a6ba49d809f83bc2caChris Banes            mErrorView.setText(error);
430c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            ViewCompat.animate(mErrorView)
431c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                    .alpha(1f)
432c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                    .setDuration(ANIMATION_DURATION)
4333a30d0eaba58dd961cf2ae9be3a7d0b9754a031eChris Banes                    .setInterpolator(AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR)
4343104067991a7d66e9e10e7a6ba49d809f83bc2caChris Banes                    .setListener(new ViewPropertyAnimatorListenerAdapter() {
4353104067991a7d66e9e10e7a6ba49d809f83bc2caChris Banes                        @Override
4363104067991a7d66e9e10e7a6ba49d809f83bc2caChris Banes                        public void onAnimationStart(View view) {
4373104067991a7d66e9e10e7a6ba49d809f83bc2caChris Banes                            view.setVisibility(VISIBLE);
4383104067991a7d66e9e10e7a6ba49d809f83bc2caChris Banes                        }
4393104067991a7d66e9e10e7a6ba49d809f83bc2caChris Banes                    })
440c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                    .start();
4413104067991a7d66e9e10e7a6ba49d809f83bc2caChris Banes
4423104067991a7d66e9e10e7a6ba49d809f83bc2caChris Banes            // Set the EditText's background tint to the error color
443249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            mErrorShown = true;
444249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            updateEditTextBackground();
445f85bf1e127ea645ca45637288ccfbab11ef2b415Chris Banes            updateLabelVisibility(true);
446c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        } else {
447c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            if (mErrorView.getVisibility() == VISIBLE) {
448c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                ViewCompat.animate(mErrorView)
449c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                        .alpha(0f)
450c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                        .setDuration(ANIMATION_DURATION)
4513a30d0eaba58dd961cf2ae9be3a7d0b9754a031eChris Banes                        .setInterpolator(AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR)
452c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                        .setListener(new ViewPropertyAnimatorListenerAdapter() {
453c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                            @Override
454c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                            public void onAnimationEnd(View view) {
4551832f6e483ae37e8b92914819c7e786d6f22f84bChris Banes                                view.setVisibility(INVISIBLE);
456f85bf1e127ea645ca45637288ccfbab11ef2b415Chris Banes
457f85bf1e127ea645ca45637288ccfbab11ef2b415Chris Banes                                updateLabelVisibility(true);
458c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                            }
459c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                        }).start();
4603104067991a7d66e9e10e7a6ba49d809f83bc2caChris Banes
4613104067991a7d66e9e10e7a6ba49d809f83bc2caChris Banes                // Restore the 'original' tint, using colorControlNormal and colorControlActivated
462249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                mErrorShown = false;
463249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                updateEditTextBackground();
464c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            }
465c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        }
466c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    }
467c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
468249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    /**
469249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki     * Whether the character counter functionality is enabled or not in this layout.
470249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki     *
471249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki     * @attr ref android.support.design.R.styleable#TextInputLayout_counterEnabled
472249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki     */
473249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    public void setCounterEnabled(boolean enabled) {
474249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        if (mCounterEnabled != enabled) {
475249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            if (enabled) {
476249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                mCounterView = new TextView(getContext());
477249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                mCounterView.setMaxLines(1);
478249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                mCounterView.setTextAppearance(getContext(), mCounterTextAppearance);
4793224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes                ViewCompat.setAccessibilityLiveRegion(mCounterView,
4803224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes                        ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE);
4814452ce91a49bbf820282a90601becfe61d64fcfbChris Banes                addIndicator(mCounterView, -1);
482249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                if (mEditText == null) {
483249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                    updateCounter(0);
484249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                } else {
485249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                    updateCounter(mEditText.getText().length());
486249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                }
487249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            } else {
488249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                removeIndicator(mCounterView);
489249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                mCounterView = null;
490249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            }
491249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            mCounterEnabled = enabled;
492249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        }
493249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    }
494249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki
495249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    /**
496e948c13b87d93495ffe88f2ac36a820bf1bcb256Yuichi Araki     * Returns whether the character counter functionality is enabled or not in this layout.
497e948c13b87d93495ffe88f2ac36a820bf1bcb256Yuichi Araki     *
498e948c13b87d93495ffe88f2ac36a820bf1bcb256Yuichi Araki     * @attr ref android.support.design.R.styleable#TextInputLayout_counterEnabled
499e948c13b87d93495ffe88f2ac36a820bf1bcb256Yuichi Araki     *
500e948c13b87d93495ffe88f2ac36a820bf1bcb256Yuichi Araki     * @see #setCounterEnabled(boolean)
501e948c13b87d93495ffe88f2ac36a820bf1bcb256Yuichi Araki     */
502e948c13b87d93495ffe88f2ac36a820bf1bcb256Yuichi Araki    public boolean isCounterEnabled() {
503e948c13b87d93495ffe88f2ac36a820bf1bcb256Yuichi Araki        return mCounterEnabled;
504e948c13b87d93495ffe88f2ac36a820bf1bcb256Yuichi Araki    }
505e948c13b87d93495ffe88f2ac36a820bf1bcb256Yuichi Araki
506e948c13b87d93495ffe88f2ac36a820bf1bcb256Yuichi Araki    /**
507249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki     * Sets the max length to display at the character counter.
508249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki     *
509249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki     * @param maxLength maxLength to display. Any value less than or equal to 0 will not be shown.
51017b65202f5e30392c5a525f62347b56027157620Chris Banes     *
51117b65202f5e30392c5a525f62347b56027157620Chris Banes     * @attr ref android.support.design.R.styleable#TextInputLayout_counterMaxLength
512249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki     */
513249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    public void setCounterMaxLength(int maxLength) {
514249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        if (mCounterMaxLength != maxLength) {
515249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            if (maxLength > 0) {
516249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                mCounterMaxLength = maxLength;
517249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            } else {
518249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                mCounterMaxLength = INVALID_MAX_LENGTH;
519249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            }
520249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            if (mCounterEnabled) {
521249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                updateCounter(mEditText == null ? 0 : mEditText.getText().length());
522249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            }
523249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        }
524249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    }
525249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki
526249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    /**
527249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki     * Returns the max length shown at the character counter.
52817b65202f5e30392c5a525f62347b56027157620Chris Banes     *
52917b65202f5e30392c5a525f62347b56027157620Chris Banes     * @attr ref android.support.design.R.styleable#TextInputLayout_counterMaxLength
530249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki     */
531249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    public int getCounterMaxLength() {
532249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        return mCounterMaxLength;
533249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    }
534249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki
535249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    private void updateCounter(int length) {
536249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        boolean wasCounterOverflowed = mCounterOverflowed;
537249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        if (mCounterMaxLength == INVALID_MAX_LENGTH) {
538249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            mCounterView.setText(String.valueOf(length));
539249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            mCounterOverflowed = false;
540249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        } else {
541249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            mCounterOverflowed = length > mCounterMaxLength;
542249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            if (wasCounterOverflowed != mCounterOverflowed) {
543249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                mCounterView.setTextAppearance(getContext(), mCounterOverflowed ?
544249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                        mCounterOverflowTextAppearance : mCounterTextAppearance);
545249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            }
546249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            mCounterView.setText(getContext().getString(R.string.character_counter_pattern,
547249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                    length, mCounterMaxLength));
548249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        }
549249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        if (mEditText != null && wasCounterOverflowed != mCounterOverflowed) {
5504452ce91a49bbf820282a90601becfe61d64fcfbChris Banes            updateLabelVisibility(false);
551249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            updateEditTextBackground();
552249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        }
553249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    }
554249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki
555249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki    private void updateEditTextBackground() {
556249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        if (mErrorShown && mErrorView != null) {
55796cb4d8853e79310aa9a52b8baab8f54873ce428Chris Banes            // Set the EditText's background tint to the error color
55896cb4d8853e79310aa9a52b8baab8f54873ce428Chris Banes            ViewCompat.setBackgroundTintList(mEditText,
55996cb4d8853e79310aa9a52b8baab8f54873ce428Chris Banes                    ColorStateList.valueOf(mErrorView.getCurrentTextColor()));
560249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki        } else if (mCounterOverflowed && mCounterView != null) {
561249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki            ViewCompat.setBackgroundTintList(mEditText,
562249ad6faac840b73bb55d47cdbecccdee2c8dd61Yuichi Araki                    ColorStateList.valueOf(mCounterView.getCurrentTextColor()));
56396cb4d8853e79310aa9a52b8baab8f54873ce428Chris Banes        } else {
56496cb4d8853e79310aa9a52b8baab8f54873ce428Chris Banes            ViewCompat.setBackgroundTintList(mEditText,
5657e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes                    AppCompatDrawableManager.get()
5667e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes                            .getTintList(getContext(), R.drawable.abc_edit_text_material));
56796cb4d8853e79310aa9a52b8baab8f54873ce428Chris Banes        }
56896cb4d8853e79310aa9a52b8baab8f54873ce428Chris Banes    }
56996cb4d8853e79310aa9a52b8baab8f54873ce428Chris Banes
5702aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    /**
5712aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     * Returns the error message that was set to be displayed with
5722aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     * {@link #setError(CharSequence)}, or <code>null</code> if no error was set
5732aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     * or if error displaying is not enabled.
5742aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     *
5752aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     * @see #setError(CharSequence)
5762aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes     */
577b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes    @Nullable
5782aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    public CharSequence getError() {
5792aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        if (mErrorEnabled && mErrorView != null && mErrorView.getVisibility() == VISIBLE) {
5802aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes            return mErrorView.getText();
5812aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        }
5822aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        return null;
5832aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes    }
5842aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes
585b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes    /**
586b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes     * Returns whether any hint state changes, due to being focused or non-empty text, are
587b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes     * animated.
588b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes     *
589b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes     * @see #setHintAnimationEnabled(boolean)
590b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes     *
591b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes     * @attr ref android.support.design.R.styleable#TextInputLayout_hintAnimationEnabled
592b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes     */
593b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes    public boolean isHintAnimationEnabled() {
594b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes        return mHintAnimationEnabled;
595b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes    }
596b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes
597b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes    /**
598b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes     * Set whether any hint state changes, due to being focused or non-empty text, are
599b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes     * animated.
600b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes     *
601b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes     * @see #isHintAnimationEnabled()
602b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes     *
603b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes     * @attr ref android.support.design.R.styleable#TextInputLayout_hintAnimationEnabled
604b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes     */
605b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes    public void setHintAnimationEnabled(boolean enabled) {
606b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes        mHintAnimationEnabled = enabled;
607b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes    }
608b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes
609c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    @Override
610c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    public void draw(Canvas canvas) {
611c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        super.draw(canvas);
612c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        mCollapsingTextHelper.draw(canvas);
613c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    }
614c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
615c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    @Override
616c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
617c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        super.onLayout(changed, left, top, right, bottom);
618c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
619c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        if (mEditText != null) {
620a611711d69eaf8ba65af12118a96810fc17b4163Chris Banes            final int l = mEditText.getLeft() + mEditText.getCompoundPaddingLeft();
621a611711d69eaf8ba65af12118a96810fc17b4163Chris Banes            final int r = mEditText.getRight() - mEditText.getCompoundPaddingRight();
622c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
623c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            mCollapsingTextHelper.setExpandedBounds(l,
624a611711d69eaf8ba65af12118a96810fc17b4163Chris Banes                    mEditText.getTop() + mEditText.getCompoundPaddingTop(),
625a611711d69eaf8ba65af12118a96810fc17b4163Chris Banes                    r, mEditText.getBottom() - mEditText.getCompoundPaddingBottom());
626c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
627c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            // Set the collapsed bounds to be the the full height (minus padding) to match the
628c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            // EditText's editable area
629c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            mCollapsingTextHelper.setCollapsedBounds(l, getPaddingTop(),
630c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                    r, bottom - top - getPaddingBottom());
6311bf6093c6dab532bc57c75767470afa3cbbe0384Chris Banes
6321bf6093c6dab532bc57c75767470afa3cbbe0384Chris Banes            mCollapsingTextHelper.recalculate();
633c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        }
634c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    }
635c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
636295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes    @Override
637295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes    public void refreshDrawableState() {
638295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes        super.refreshDrawableState();
639295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes        // Drawable state has changed so see if we need to update the label
6402aa57abd4531068424642e1a5dfff47c17b84c2eChris Banes        updateLabelVisibility(ViewCompat.isLaidOut(this));
641295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes    }
642295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes
643c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    private void collapseHint(boolean animate) {
644295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes        if (mAnimator != null && mAnimator.isRunning()) {
645295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes            mAnimator.cancel();
646295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes        }
647b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes        if (animate && mHintAnimationEnabled) {
648c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            animateToExpansionFraction(1f);
649c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        } else {
650c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            mCollapsingTextHelper.setExpansionFraction(1f);
651c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        }
652c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    }
653c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
654c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    private void expandHint(boolean animate) {
655295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes        if (mAnimator != null && mAnimator.isRunning()) {
656295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes            mAnimator.cancel();
657295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes        }
658b8a3b8ee61756f9f9a856eb2428bf2389fd92926Chris Banes        if (animate && mHintAnimationEnabled) {
659c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            animateToExpansionFraction(0f);
660c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        } else {
661c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            mCollapsingTextHelper.setExpansionFraction(0f);
662c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        }
663c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    }
664c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
665c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    private void animateToExpansionFraction(final float target) {
666295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes        if (mCollapsingTextHelper.getExpansionFraction() == target) {
667295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes            return;
668295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes        }
669f44e88b81821e61a65886cc7027617ca8995070bChris Banes        if (mAnimator == null) {
670f44e88b81821e61a65886cc7027617ca8995070bChris Banes            mAnimator = ViewUtils.createAnimator();
671f44e88b81821e61a65886cc7027617ca8995070bChris Banes            mAnimator.setInterpolator(AnimationUtils.LINEAR_INTERPOLATOR);
672f44e88b81821e61a65886cc7027617ca8995070bChris Banes            mAnimator.setDuration(ANIMATION_DURATION);
673f44e88b81821e61a65886cc7027617ca8995070bChris Banes            mAnimator.setUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
674f44e88b81821e61a65886cc7027617ca8995070bChris Banes                @Override
675f44e88b81821e61a65886cc7027617ca8995070bChris Banes                public void onAnimationUpdate(ValueAnimatorCompat animator) {
676f44e88b81821e61a65886cc7027617ca8995070bChris Banes                    mCollapsingTextHelper.setExpansionFraction(animator.getAnimatedFloatValue());
677f44e88b81821e61a65886cc7027617ca8995070bChris Banes                }
678f44e88b81821e61a65886cc7027617ca8995070bChris Banes            });
679f44e88b81821e61a65886cc7027617ca8995070bChris Banes        }
680f44e88b81821e61a65886cc7027617ca8995070bChris Banes        mAnimator.setFloatValues(mCollapsingTextHelper.getExpansionFraction(), target);
681f44e88b81821e61a65886cc7027617ca8995070bChris Banes        mAnimator.start();
682c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    }
683c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
684c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    private int getThemeAttrColor(int attr) {
685c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        TypedValue tv = new TypedValue();
686c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        if (getContext().getTheme().resolveAttribute(attr, tv, true)) {
687c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            return tv.data;
688c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        } else {
689c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            return Color.MAGENTA;
690c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        }
691c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    }
692c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
693c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    private class TextInputAccessibilityDelegate extends AccessibilityDelegateCompat {
694c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        @Override
695c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
696c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            super.onInitializeAccessibilityEvent(host, event);
697c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            event.setClassName(TextInputLayout.class.getSimpleName());
698c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        }
699c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
700c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        @Override
701c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
702c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            super.onPopulateAccessibilityEvent(host, event);
703c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
704c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            final CharSequence text = mCollapsingTextHelper.getText();
705c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            if (!TextUtils.isEmpty(text)) {
706c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                event.getText().add(text);
707c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            }
708c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        }
709c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
710c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        @Override
711c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
712c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            super.onInitializeAccessibilityNodeInfo(host, info);
713c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            info.setClassName(TextInputLayout.class.getSimpleName());
714c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes
715c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            final CharSequence text = mCollapsingTextHelper.getText();
716c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            if (!TextUtils.isEmpty(text)) {
717c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                info.setText(text);
718c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            }
719c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            if (mEditText != null) {
720c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                info.setLabelFor(mEditText);
721c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            }
722c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            final CharSequence error = mErrorView != null ? mErrorView.getText() : null;
723c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            if (!TextUtils.isEmpty(error)) {
724c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                info.setContentInvalid(true);
725c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes                info.setError(error);
726c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes            }
727c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes        }
728c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes    }
729295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes
730295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes    private static boolean arrayContains(int[] array, int value) {
731295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes        for (int v : array) {
732295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes            if (v == value) {
733295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes                return true;
734295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes            }
735295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes        }
736295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes        return false;
737295311874d0807bab84fb2091e608da9e9ef3cd9Chris Banes    }
738c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes}