1 /*
2  * Copyright (C) 2008 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.text.style;
18 
19 import android.content.Context;
20 import android.content.res.ColorStateList;
21 import android.content.res.TypedArray;
22 import android.graphics.LeakyTypefaceStorage;
23 import android.graphics.Typeface;
24 import android.os.Parcel;
25 import android.text.ParcelableSpan;
26 import android.text.TextPaint;
27 import android.text.TextUtils;
28 
29 /**
30  * Sets the text color, size, style, and typeface to match a TextAppearance
31  * resource.
32  */
33 public class TextAppearanceSpan extends MetricAffectingSpan implements ParcelableSpan {
34     private final String mFamilyName;
35     private final int mStyle;
36     private final int mTextSize;
37     private final ColorStateList mTextColor;
38     private final ColorStateList mTextColorLink;
39     private final Typeface mTypeface;
40 
41     /**
42      * Uses the specified TextAppearance resource to determine the
43      * text appearance.  The <code>appearance</code> should be, for example,
44      * <code>android.R.style.TextAppearance_Small</code>.
45      */
TextAppearanceSpan(Context context, int appearance)46     public TextAppearanceSpan(Context context, int appearance) {
47         this(context, appearance, -1);
48     }
49 
50     /**
51      * Uses the specified TextAppearance resource to determine the
52      * text appearance, and the specified text color resource
53      * to determine the color.  The <code>appearance</code> should be,
54      * for example, <code>android.R.style.TextAppearance_Small</code>,
55      * and the <code>colorList</code> should be, for example,
56      * <code>android.R.styleable.Theme_textColorPrimary</code>.
57      */
TextAppearanceSpan(Context context, int appearance, int colorList)58     public TextAppearanceSpan(Context context, int appearance, int colorList) {
59         ColorStateList textColor;
60 
61         TypedArray a =
62             context.obtainStyledAttributes(appearance,
63                                            com.android.internal.R.styleable.TextAppearance);
64 
65         textColor = a.getColorStateList(com.android.internal.R.styleable.
66                                         TextAppearance_textColor);
67         mTextColorLink = a.getColorStateList(com.android.internal.R.styleable.
68                                         TextAppearance_textColorLink);
69         mTextSize = a.getDimensionPixelSize(com.android.internal.R.styleable.
70                                         TextAppearance_textSize, -1);
71 
72         mStyle = a.getInt(com.android.internal.R.styleable.TextAppearance_textStyle, 0);
73         if (!context.isRestricted() && context.canLoadUnsafeResources()) {
74             mTypeface = a.getFont(com.android.internal.R.styleable.TextAppearance_fontFamily);
75         } else {
76             mTypeface = null;
77         }
78         if (mTypeface != null) {
79             mFamilyName = null;
80         } else {
81             String family = a.getString(com.android.internal.R.styleable.TextAppearance_fontFamily);
82             if (family != null) {
83                 mFamilyName = family;
84             } else {
85                 int tf = a.getInt(com.android.internal.R.styleable.TextAppearance_typeface, 0);
86 
87                 switch (tf) {
88                     case 1:
89                         mFamilyName = "sans";
90                         break;
91 
92                     case 2:
93                         mFamilyName = "serif";
94                         break;
95 
96                     case 3:
97                         mFamilyName = "monospace";
98                         break;
99 
100                     default:
101                         mFamilyName = null;
102                         break;
103                 }
104             }
105         }
106 
107         a.recycle();
108 
109         if (colorList >= 0) {
110             a = context.obtainStyledAttributes(com.android.internal.R.style.Theme,
111                                             com.android.internal.R.styleable.Theme);
112 
113             textColor = a.getColorStateList(colorList);
114             a.recycle();
115         }
116 
117         mTextColor = textColor;
118     }
119 
120     /**
121      * Makes text be drawn with the specified typeface, size, style,
122      * and colors.
123      */
TextAppearanceSpan(String family, int style, int size, ColorStateList color, ColorStateList linkColor)124     public TextAppearanceSpan(String family, int style, int size,
125                               ColorStateList color, ColorStateList linkColor) {
126         mFamilyName = family;
127         mStyle = style;
128         mTextSize = size;
129         mTextColor = color;
130         mTextColorLink = linkColor;
131         mTypeface = null;
132     }
133 
TextAppearanceSpan(Parcel src)134     public TextAppearanceSpan(Parcel src) {
135         mFamilyName = src.readString();
136         mStyle = src.readInt();
137         mTextSize = src.readInt();
138         if (src.readInt() != 0) {
139             mTextColor = ColorStateList.CREATOR.createFromParcel(src);
140         } else {
141             mTextColor = null;
142         }
143         if (src.readInt() != 0) {
144             mTextColorLink = ColorStateList.CREATOR.createFromParcel(src);
145         } else {
146             mTextColorLink = null;
147         }
148         mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src);
149     }
150 
getSpanTypeId()151     public int getSpanTypeId() {
152         return getSpanTypeIdInternal();
153     }
154 
155     /** @hide */
getSpanTypeIdInternal()156     public int getSpanTypeIdInternal() {
157         return TextUtils.TEXT_APPEARANCE_SPAN;
158     }
159 
describeContents()160     public int describeContents() {
161         return 0;
162     }
163 
writeToParcel(Parcel dest, int flags)164     public void writeToParcel(Parcel dest, int flags) {
165         writeToParcelInternal(dest, flags);
166     }
167 
168     /** @hide */
writeToParcelInternal(Parcel dest, int flags)169     public void writeToParcelInternal(Parcel dest, int flags) {
170         dest.writeString(mFamilyName);
171         dest.writeInt(mStyle);
172         dest.writeInt(mTextSize);
173         if (mTextColor != null) {
174             dest.writeInt(1);
175             mTextColor.writeToParcel(dest, flags);
176         } else {
177             dest.writeInt(0);
178         }
179         if (mTextColorLink != null) {
180             dest.writeInt(1);
181             mTextColorLink.writeToParcel(dest, flags);
182         } else {
183             dest.writeInt(0);
184         }
185         LeakyTypefaceStorage.writeTypefaceToParcel(mTypeface, dest);
186     }
187 
188     /**
189      * Returns the typeface family specified by this span, or <code>null</code>
190      * if it does not specify one.
191      */
getFamily()192     public String getFamily() {
193         return mFamilyName;
194     }
195 
196     /**
197      * Returns the text color specified by this span, or <code>null</code>
198      * if it does not specify one.
199      */
getTextColor()200     public ColorStateList getTextColor() {
201         return mTextColor;
202     }
203 
204     /**
205      * Returns the link color specified by this span, or <code>null</code>
206      * if it does not specify one.
207      */
getLinkTextColor()208     public ColorStateList getLinkTextColor() {
209         return mTextColorLink;
210     }
211 
212     /**
213      * Returns the text size specified by this span, or <code>-1</code>
214      * if it does not specify one.
215      */
getTextSize()216     public int getTextSize() {
217         return mTextSize;
218     }
219 
220     /**
221      * Returns the text style specified by this span, or <code>0</code>
222      * if it does not specify one.
223      */
getTextStyle()224     public int getTextStyle() {
225         return mStyle;
226     }
227 
228     @Override
updateDrawState(TextPaint ds)229     public void updateDrawState(TextPaint ds) {
230         updateMeasureState(ds);
231 
232         if (mTextColor != null) {
233             ds.setColor(mTextColor.getColorForState(ds.drawableState, 0));
234         }
235 
236         if (mTextColorLink != null) {
237             ds.linkColor = mTextColorLink.getColorForState(ds.drawableState, 0);
238         }
239     }
240 
241     @Override
updateMeasureState(TextPaint ds)242     public void updateMeasureState(TextPaint ds) {
243         final Typeface styledTypeface;
244         int style = 0;
245 
246         if (mTypeface != null) {
247             style = mStyle;
248             styledTypeface = Typeface.create(mTypeface, style);
249         } else if (mFamilyName != null || mStyle != 0) {
250             Typeface tf = ds.getTypeface();
251 
252             if (tf != null) {
253                 style = tf.getStyle();
254             }
255 
256             style |= mStyle;
257 
258             if (mFamilyName != null) {
259                 styledTypeface = Typeface.create(mFamilyName, style);
260             } else if (tf == null) {
261                 styledTypeface = Typeface.defaultFromStyle(style);
262             } else {
263                 styledTypeface = Typeface.create(tf, style);
264             }
265         } else {
266             styledTypeface = null;
267         }
268 
269         if (styledTypeface != null) {
270             int fake = style & ~styledTypeface.getStyle();
271 
272             if ((fake & Typeface.BOLD) != 0) {
273                 ds.setFakeBoldText(true);
274             }
275 
276             if ((fake & Typeface.ITALIC) != 0) {
277                 ds.setTextSkewX(-0.25f);
278             }
279 
280             ds.setTypeface(styledTypeface);
281         }
282 
283         if (mTextSize > 0) {
284             ds.setTextSize(mTextSize);
285         }
286     }
287 }
288