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
diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java
index abbd793..3a3646b 100644
--- a/core/java/android/text/style/TextAppearanceSpan.java
+++ b/core/java/android/text/style/TextAppearanceSpan.java
@@ -19,6 +19,7 @@
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 @@
* 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 TextAppearanceSpan(Context context, int appearance, int colorList) {
ColorStateList textColor;
-
+
TypedArray a =
context.obtainStyledAttributes(appearance,
com.android.internal.R.styleable.TextAppearance);
@@ -68,28 +70,33 @@
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 @@
textColor = a.getColorStateList(colorList);
a.recycle();
}
-
+
mTextColor = textColor;
}
@@ -112,15 +119,16 @@
*/
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 @@
} else {
mTextColorLink = null;
}
+ mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src);
}
-
+
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
@@ -143,7 +152,7 @@
public int getSpanTypeIdInternal() {
return TextUtils.TEXT_APPEARANCE_SPAN;
}
-
+
public int describeContents() {
return 0;
}
@@ -154,7 +163,7 @@
/** @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 @@
} else {
dest.writeInt(0);
}
+ LeakyTypefaceStorage.writeTypefaceToParcel(mTypeface, dest);
}
/**
@@ -176,7 +186,7 @@
* if it does not specify one.
*/
public String getFamily() {
- return mTypeface;
+ return mFamilyName;
}
/**
@@ -226,9 +236,14 @@
@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 @@
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 @@
ds.setTextSkewX(-0.25f);
}
- ds.setTypeface(tf);
+ ds.setTypeface(styledTypeface);
}
if (mTextSize > 0) {
diff --git a/graphics/java/android/graphics/LeakyTypefaceStorage.java b/graphics/java/android/graphics/LeakyTypefaceStorage.java
new file mode 100644
index 0000000..618e60d
--- /dev/null
+++ b/graphics/java/android/graphics/LeakyTypefaceStorage.java
@@ -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);
+ }
+ }
+}