1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.support.v7.widget;
18
19import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
20
21import android.content.Context;
22import android.content.res.ColorStateList;
23import android.graphics.PorterDuff;
24import android.graphics.drawable.Drawable;
25import android.support.annotation.DrawableRes;
26import android.support.annotation.NonNull;
27import android.support.annotation.Nullable;
28import android.support.annotation.RestrictTo;
29import android.support.v4.os.BuildCompat;
30import android.support.v4.view.TintableBackgroundView;
31import android.support.v4.widget.AutoSizeableTextView;
32import android.support.v4.widget.TextViewCompat;
33import android.support.v7.appcompat.R;
34import android.util.AttributeSet;
35import android.widget.TextView;
36
37/**
38 * A {@link TextView} which supports compatible features on older version of the platform,
39 * including:
40 * <ul>
41 *     <li>Supports {@link R.attr#textAllCaps} style attribute which works back to
42 *     {@link android.os.Build.VERSION_CODES#GINGERBREAD Gingerbread}.</li>
43 *     <li>Allows dynamic tint of its background via the background tint methods in
44 *     {@link android.support.v4.view.ViewCompat}.</li>
45 *     <li>Allows setting of the background tint using {@link R.attr#backgroundTint} and
46 *     {@link R.attr#backgroundTintMode}.</li>
47 *     <li>Supports auto-sizing via {@link android.support.v4.widget.TextViewCompat} by allowing
48 *     to instruct a {@link TextView} to let the size of the text expand or contract automatically
49 *     to fill its layout based on the TextView's characteristics and boundaries. The
50 *     style attributes associated with auto-sizing are {@link R.attr#autoSizeTextType},
51 *     {@link R.attr#autoSizeMinTextSize}, {@link R.attr#autoSizeMaxTextSize},
52 *     {@link R.attr#autoSizeStepGranularity} and {@link R.attr#autoSizePresetSizes}, all of
53 *     which work back to
54 *     {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH Ice Cream Sandwich}.</li>
55 * </ul>
56 *
57 * <p>This will automatically be used when you use {@link TextView} in your layouts.
58 * You should only need to manually use this class when writing custom views.</p>
59 */
60public class AppCompatTextView extends TextView implements TintableBackgroundView,
61        AutoSizeableTextView {
62
63    private final AppCompatBackgroundHelper mBackgroundTintHelper;
64    private final AppCompatTextHelper mTextHelper;
65
66    public AppCompatTextView(Context context) {
67        this(context, null);
68    }
69
70    public AppCompatTextView(Context context, AttributeSet attrs) {
71        this(context, attrs, android.R.attr.textViewStyle);
72    }
73
74    public AppCompatTextView(Context context, AttributeSet attrs, int defStyleAttr) {
75        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
76
77        mBackgroundTintHelper = new AppCompatBackgroundHelper(this);
78        mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr);
79
80        mTextHelper = AppCompatTextHelper.create(this);
81        mTextHelper.loadFromAttributes(attrs, defStyleAttr);
82        mTextHelper.applyCompoundDrawablesTints();
83    }
84
85    @Override
86    public void setBackgroundResource(@DrawableRes int resId) {
87        super.setBackgroundResource(resId);
88        if (mBackgroundTintHelper != null) {
89            mBackgroundTintHelper.onSetBackgroundResource(resId);
90        }
91    }
92
93    @Override
94    public void setBackgroundDrawable(Drawable background) {
95        super.setBackgroundDrawable(background);
96        if (mBackgroundTintHelper != null) {
97            mBackgroundTintHelper.onSetBackgroundDrawable(background);
98        }
99    }
100
101    /**
102     * This should be accessed via
103     * {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View, ColorStateList)}
104     *
105     * @hide
106     */
107    @RestrictTo(LIBRARY_GROUP)
108    @Override
109    public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
110        if (mBackgroundTintHelper != null) {
111            mBackgroundTintHelper.setSupportBackgroundTintList(tint);
112        }
113    }
114
115    /**
116     * This should be accessed via
117     * {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
118     *
119     * @hide
120     */
121    @RestrictTo(LIBRARY_GROUP)
122    @Override
123    @Nullable
124    public ColorStateList getSupportBackgroundTintList() {
125        return mBackgroundTintHelper != null
126                ? mBackgroundTintHelper.getSupportBackgroundTintList() : null;
127    }
128
129    /**
130     * This should be accessed via
131     * {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, PorterDuff.Mode)}
132     *
133     * @hide
134     */
135    @RestrictTo(LIBRARY_GROUP)
136    @Override
137    public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
138        if (mBackgroundTintHelper != null) {
139            mBackgroundTintHelper.setSupportBackgroundTintMode(tintMode);
140        }
141    }
142
143    /**
144     * This should be accessed via
145     * {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
146     *
147     * @hide
148     */
149    @RestrictTo(LIBRARY_GROUP)
150    @Override
151    @Nullable
152    public PorterDuff.Mode getSupportBackgroundTintMode() {
153        return mBackgroundTintHelper != null
154                ? mBackgroundTintHelper.getSupportBackgroundTintMode() : null;
155    }
156
157    @Override
158    public void setTextAppearance(Context context, int resId) {
159        super.setTextAppearance(context, resId);
160        if (mTextHelper != null) {
161            mTextHelper.onSetTextAppearance(context, resId);
162        }
163    }
164
165    @Override
166    protected void drawableStateChanged() {
167        super.drawableStateChanged();
168        if (mBackgroundTintHelper != null) {
169            mBackgroundTintHelper.applySupportBackgroundTint();
170        }
171        if (mTextHelper != null) {
172            mTextHelper.applyCompoundDrawablesTints();
173        }
174    }
175
176    @Override
177    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
178        super.onLayout(changed, left, top, right, bottom);
179        if (mTextHelper != null) {
180            mTextHelper.onLayout(changed, left, top, right, bottom);
181        }
182    }
183
184    @Override
185    public void setTextSize(int unit, float size) {
186        if (BuildCompat.isAtLeastO()) {
187            super.setTextSize(unit, size);
188        } else {
189            if (mTextHelper != null) {
190                mTextHelper.setTextSize(unit, size);
191            }
192        }
193    }
194
195    /**
196     * This should be accessed via
197     * {@link android.support.v4.widget.TextViewCompat#setAutoSizeTextTypeWithDefaults(
198     *        TextView, int)}
199     *
200     * @hide
201     */
202    @RestrictTo(LIBRARY_GROUP)
203    @Override
204    public void setAutoSizeTextTypeWithDefaults(
205            @TextViewCompat.AutoSizeTextType int autoSizeTextType) {
206        if (BuildCompat.isAtLeastO()) {
207            super.setAutoSizeTextTypeWithDefaults(autoSizeTextType);
208        } else {
209            if (mTextHelper != null) {
210                mTextHelper.setAutoSizeTextTypeWithDefaults(autoSizeTextType);
211            }
212        }
213    }
214
215    /**
216     * This should be accessed via
217     * {@link android.support.v4.widget.TextViewCompat#setAutoSizeTextTypeUniformWithConfiguration(
218     *        TextView, int, int, int, int)}
219     *
220     * @hide
221     */
222    @RestrictTo(LIBRARY_GROUP)
223    @Override
224    public void setAutoSizeTextTypeUniformWithConfiguration(
225            int autoSizeMinTextSize,
226            int autoSizeMaxTextSize,
227            int autoSizeStepGranularity,
228            int unit) throws IllegalArgumentException {
229        if (BuildCompat.isAtLeastO()) {
230            super.setAutoSizeTextTypeUniformWithConfiguration(
231                    autoSizeMinTextSize, autoSizeMaxTextSize, autoSizeStepGranularity, unit);
232        } else {
233            if (mTextHelper != null) {
234                mTextHelper.setAutoSizeTextTypeUniformWithConfiguration(
235                        autoSizeMinTextSize, autoSizeMaxTextSize, autoSizeStepGranularity, unit);
236            }
237        }
238    }
239
240    /**
241     * This should be accessed via
242     * {@link android.support.v4.widget.TextViewCompat#setAutoSizeTextTypeUniformWithPresetSizes(
243     *        TextView, int[], int)}
244     *
245     * @hide
246     */
247    @RestrictTo(LIBRARY_GROUP)
248    @Override
249    public void setAutoSizeTextTypeUniformWithPresetSizes(@NonNull int[] presetSizes, int unit)
250            throws IllegalArgumentException {
251        if (BuildCompat.isAtLeastO()) {
252            super.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
253        } else {
254            if (mTextHelper != null) {
255                mTextHelper.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
256            }
257        }
258    }
259
260    /**
261     * This should be accessed via
262     * {@link android.support.v4.widget.TextViewCompat#getAutoSizeTextType(TextView)}
263     *
264     * @hide
265     */
266    @RestrictTo(LIBRARY_GROUP)
267    @Override
268    @TextViewCompat.AutoSizeTextType
269    public int getAutoSizeTextType() {
270        if (BuildCompat.isAtLeastO()) {
271            return super.getAutoSizeTextType() == TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM
272                    ? TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM
273                    : TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;
274        } else {
275            if (mTextHelper != null) {
276                return mTextHelper.getAutoSizeTextType();
277            }
278        }
279        return TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;
280    }
281
282    /**
283     * This should be accessed via
284     * {@link android.support.v4.widget.TextViewCompat#getAutoSizeStepGranularity(TextView)}
285     *
286     * @hide
287     */
288    @RestrictTo(LIBRARY_GROUP)
289    @Override
290    public int getAutoSizeStepGranularity() {
291        if (BuildCompat.isAtLeastO()) {
292            return super.getAutoSizeStepGranularity();
293        } else {
294            if (mTextHelper != null) {
295                return mTextHelper.getAutoSizeStepGranularity();
296            }
297        }
298        return -1;
299    }
300
301    /**
302     * This should be accessed via
303     * {@link android.support.v4.widget.TextViewCompat#getAutoSizeMinTextSize(TextView)}
304     *
305     * @hide
306     */
307    @RestrictTo(LIBRARY_GROUP)
308    @Override
309    public int getAutoSizeMinTextSize() {
310        if (BuildCompat.isAtLeastO()) {
311            return super.getAutoSizeMinTextSize();
312        } else {
313            if (mTextHelper != null) {
314                return mTextHelper.getAutoSizeMinTextSize();
315            }
316        }
317        return -1;
318    }
319
320    /**
321     * This should be accessed via
322     * {@link android.support.v4.widget.TextViewCompat#getAutoSizeMaxTextSize(TextView)}
323     *
324     * @hide
325     */
326    @RestrictTo(LIBRARY_GROUP)
327    @Override
328    public int getAutoSizeMaxTextSize() {
329        if (BuildCompat.isAtLeastO()) {
330            return super.getAutoSizeMaxTextSize();
331        } else {
332            if (mTextHelper != null) {
333                return mTextHelper.getAutoSizeMaxTextSize();
334            }
335        }
336        return -1;
337    }
338
339    /**
340     * This should be accessed via
341     * {@link android.support.v4.widget.TextViewCompat#getAutoSizeTextAvailableSizes(TextView)}
342     *
343     * @hide
344     */
345    @RestrictTo(LIBRARY_GROUP)
346    @Override
347    public int[] getAutoSizeTextAvailableSizes() {
348        if (BuildCompat.isAtLeastO()) {
349            return super.getAutoSizeTextAvailableSizes();
350        } else {
351            if (mTextHelper != null) {
352                return mTextHelper.getAutoSizeTextAvailableSizes();
353            }
354        }
355        return new int[0];
356    }
357}
358