Support font resource in TextAppearanceSpan

To be able to parcel/unparcel Typeface, keep it in static context.

Bug: 62850669
Test: Manually done
Test: bit CtsTextTestCases:android.text.style.cts.TextAppearanceSpanTest
Change-Id: I408cd33b98d8bb13776560231d1eeaac0a7c6bf8
This commit is contained in:
Seigo Nonaka
2017-07-13 13:34:00 -07:00
parent a73f5e6bff
commit c49ee3bde9
2 changed files with 139 additions and 34 deletions

View File

@@ -19,6 +19,7 @@ package android.text.style;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.LeakyTypefaceStorage;
import android.graphics.Typeface;
import android.os.Parcel;
import android.text.ParcelableSpan;
@@ -30,11 +31,12 @@ import android.text.TextUtils;
* resource.
*/
public class TextAppearanceSpan extends MetricAffectingSpan implements ParcelableSpan {
private final String mTypeface;
private final String mFamilyName;
private final int mStyle;
private final int mTextSize;
private final ColorStateList mTextColor;
private final ColorStateList mTextColorLink;
private final Typeface mTypeface;
/**
* Uses the specified TextAppearance resource to determine the
@@ -55,7 +57,7 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl
*/
public TextAppearanceSpan(Context context, int appearance, int colorList) {
ColorStateList textColor;
TypedArray a =
context.obtainStyledAttributes(appearance,
com.android.internal.R.styleable.TextAppearance);
@@ -68,28 +70,33 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl
TextAppearance_textSize, -1);
mStyle = a.getInt(com.android.internal.R.styleable.TextAppearance_textStyle, 0);
String family = a.getString(com.android.internal.R.styleable.TextAppearance_fontFamily);
if (family != null) {
mTypeface = family;
mTypeface = a.getFont(com.android.internal.R.styleable.TextAppearance_fontFamily);
if (mTypeface != null) {
mFamilyName = null;
} else {
int tf = a.getInt(com.android.internal.R.styleable.TextAppearance_typeface, 0);
String family = a.getString(com.android.internal.R.styleable.TextAppearance_fontFamily);
if (family != null) {
mFamilyName = family;
} else {
int tf = a.getInt(com.android.internal.R.styleable.TextAppearance_typeface, 0);
switch (tf) {
case 1:
mTypeface = "sans";
break;
switch (tf) {
case 1:
mFamilyName = "sans";
break;
case 2:
mTypeface = "serif";
break;
case 2:
mFamilyName = "serif";
break;
case 3:
mTypeface = "monospace";
break;
case 3:
mFamilyName = "monospace";
break;
default:
mTypeface = null;
break;
default:
mFamilyName = null;
break;
}
}
}
@@ -102,7 +109,7 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl
textColor = a.getColorStateList(colorList);
a.recycle();
}
mTextColor = textColor;
}
@@ -112,15 +119,16 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl
*/
public TextAppearanceSpan(String family, int style, int size,
ColorStateList color, ColorStateList linkColor) {
mTypeface = family;
mFamilyName = family;
mStyle = style;
mTextSize = size;
mTextColor = color;
mTextColorLink = linkColor;
mTypeface = null;
}
public TextAppearanceSpan(Parcel src) {
mTypeface = src.readString();
mFamilyName = src.readString();
mStyle = src.readInt();
mTextSize = src.readInt();
if (src.readInt() != 0) {
@@ -133,8 +141,9 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl
} else {
mTextColorLink = null;
}
mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src);
}
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
@@ -143,7 +152,7 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl
public int getSpanTypeIdInternal() {
return TextUtils.TEXT_APPEARANCE_SPAN;
}
public int describeContents() {
return 0;
}
@@ -154,7 +163,7 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl
/** @hide */
public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeString(mTypeface);
dest.writeString(mFamilyName);
dest.writeInt(mStyle);
dest.writeInt(mTextSize);
if (mTextColor != null) {
@@ -169,6 +178,7 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl
} else {
dest.writeInt(0);
}
LeakyTypefaceStorage.writeTypefaceToParcel(mTypeface, dest);
}
/**
@@ -176,7 +186,7 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl
* if it does not specify one.
*/
public String getFamily() {
return mTypeface;
return mFamilyName;
}
/**
@@ -226,9 +236,14 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl
@Override
public void updateMeasureState(TextPaint ds) {
if (mTypeface != null || mStyle != 0) {
final Typeface styledTypeface;
int style = 0;
if (mTypeface != null) {
style = mStyle;
styledTypeface = Typeface.create(mTypeface, style);
} else if (mFamilyName != null || mStyle != 0) {
Typeface tf = ds.getTypeface();
int style = 0;
if (tf != null) {
style = tf.getStyle();
@@ -236,15 +251,19 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl
style |= mStyle;
if (mTypeface != null) {
tf = Typeface.create(mTypeface, style);
if (mFamilyName != null) {
styledTypeface = Typeface.create(mFamilyName, style);
} else if (tf == null) {
tf = Typeface.defaultFromStyle(style);
styledTypeface = Typeface.defaultFromStyle(style);
} else {
tf = Typeface.create(tf, style);
styledTypeface = Typeface.create(tf, style);
}
} else {
styledTypeface = null;
}
int fake = style & ~tf.getStyle();
if (styledTypeface != null) {
int fake = style & ~styledTypeface.getStyle();
if ((fake & Typeface.BOLD) != 0) {
ds.setFakeBoldText(true);
@@ -254,7 +273,7 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl
ds.setTextSkewX(-0.25f);
}
ds.setTypeface(tf);
ds.setTypeface(styledTypeface);
}
if (mTextSize > 0) {

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.graphics;
import com.android.internal.annotations.GuardedBy;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Process;
import android.util.ArrayMap;
import java.util.ArrayList;
/**
* This class is used for Parceling Typeface object.
* Note: Typeface object can not be passed over the process boundary.
*
* @hide
*/
public class LeakyTypefaceStorage {
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static final ArrayList<Typeface> sStorage = new ArrayList<>();
@GuardedBy("sLock")
private static final ArrayMap<Typeface, Integer> sTypefaceMap = new ArrayMap<>();
/**
* Write typeface to parcel.
*
* You can't transfer Typeface to a different process. {@link readTypefaceFromParcel} will
* return {@code null} if the {@link readTypefaceFromParcel} is called in a different process.
*
* @param typeface A {@link Typeface} to be written.
* @param parcel A {@link Parcel} object.
*/
public static void writeTypefaceToParcel(@Nullable Typeface typeface, @NonNull Parcel parcel) {
parcel.writeInt(Process.myPid());
synchronized (sLock) {
final int id;
final Integer i = sTypefaceMap.get(typeface);
if (i != null) {
id = i.intValue();
} else {
id = sStorage.size();
sStorage.add(typeface);
sTypefaceMap.put(typeface, id);
}
parcel.writeInt(id);
}
}
/**
* Read typeface from parcel.
*
* If the {@link Typeface} was created in another process, this method returns null.
*
* @param parcel A {@link Parcel} object
* @return A {@link Typeface} object.
*/
public static @Nullable Typeface readTypefaceFromParcel(@NonNull Parcel parcel) {
final int pid = parcel.readInt();
final int typefaceId = parcel.readInt();
if (pid != Process.myPid()) {
return null; // The Typeface was created and written in another process.
}
synchronized (sLock) {
return sStorage.get(typefaceId);
}
}
}