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 
17 package android.support.v7.widget;
18 
19 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
20 
21 import android.content.Context;
22 import android.content.res.ColorStateList;
23 import android.graphics.PorterDuff;
24 import android.graphics.drawable.Drawable;
25 import android.support.annotation.DrawableRes;
26 import android.support.annotation.NonNull;
27 import android.support.annotation.Nullable;
28 import android.support.annotation.RestrictTo;
29 import android.support.v4.os.BuildCompat;
30 import android.support.v4.view.TintableBackgroundView;
31 import android.support.v4.widget.AutoSizeableTextView;
32 import android.support.v4.widget.TextViewCompat;
33 import android.support.v7.appcompat.R;
34 import android.util.AttributeSet;
35 import 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  */
60 public class AppCompatTextView extends TextView implements TintableBackgroundView,
61         AutoSizeableTextView {
62 
63     private final AppCompatBackgroundHelper mBackgroundTintHelper;
64     private final AppCompatTextHelper mTextHelper;
65 
AppCompatTextView(Context context)66     public AppCompatTextView(Context context) {
67         this(context, null);
68     }
69 
AppCompatTextView(Context context, AttributeSet attrs)70     public AppCompatTextView(Context context, AttributeSet attrs) {
71         this(context, attrs, android.R.attr.textViewStyle);
72     }
73 
AppCompatTextView(Context context, AttributeSet attrs, int defStyleAttr)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
setBackgroundResource(@rawableRes int resId)86     public void setBackgroundResource(@DrawableRes int resId) {
87         super.setBackgroundResource(resId);
88         if (mBackgroundTintHelper != null) {
89             mBackgroundTintHelper.onSetBackgroundResource(resId);
90         }
91     }
92 
93     @Override
setBackgroundDrawable(Drawable background)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
setSupportBackgroundTintList(@ullable ColorStateList tint)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
getSupportBackgroundTintList()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
setSupportBackgroundTintMode(@ullable PorterDuff.Mode tintMode)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
getSupportBackgroundTintMode()152     public PorterDuff.Mode getSupportBackgroundTintMode() {
153         return mBackgroundTintHelper != null
154                 ? mBackgroundTintHelper.getSupportBackgroundTintMode() : null;
155     }
156 
157     @Override
setTextAppearance(Context context, int resId)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
drawableStateChanged()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
onLayout(boolean changed, int left, int top, int right, int bottom)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
setTextSize(int unit, float size)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
setAutoSizeTextTypeWithDefaults( @extViewCompat.AutoSizeTextType int autoSizeTextType)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
setAutoSizeTextTypeUniformWithConfiguration( int autoSizeMinTextSize, int autoSizeMaxTextSize, int autoSizeStepGranularity, int unit)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
setAutoSizeTextTypeUniformWithPresetSizes(@onNull int[] presetSizes, int unit)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
getAutoSizeTextType()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
getAutoSizeStepGranularity()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
getAutoSizeMinTextSize()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
getAutoSizeMaxTextSize()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
getAutoSizeTextAvailableSizes()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