Merge "Declarative downloadable fonts"

This commit is contained in:
TreeHugger Robot
2017-02-14 15:39:00 +00:00
committed by Android (Google) Code Review
14 changed files with 261 additions and 36 deletions

View File

@@ -601,6 +601,8 @@ package android {
field public static final int font = 16844082; // 0x1010532
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int fontProviderAuthority = 16844114; // 0x1010552
field public static final int fontProviderQuery = 16844115; // 0x1010553
field public static final int fontStyle = 16844081; // 0x1010531
field public static final int fontWeight = 16844083; // 0x1010533
field public static final int footerDividersEnabled = 16843311; // 0x101022f

View File

@@ -713,6 +713,8 @@ package android {
field public static final int font = 16844082; // 0x1010532
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int fontProviderAuthority = 16844114; // 0x1010552
field public static final int fontProviderQuery = 16844115; // 0x1010553
field public static final int fontStyle = 16844081; // 0x1010531
field public static final int fontWeight = 16844083; // 0x1010533
field public static final int footerDividersEnabled = 16843311; // 0x101022f

View File

@@ -601,6 +601,8 @@ package android {
field public static final int font = 16844082; // 0x1010532
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int fontProviderAuthority = 16844114; // 0x1010552
field public static final int fontProviderQuery = 16844115; // 0x1010553
field public static final int fontStyle = 16844081; // 0x1010531
field public static final int fontWeight = 16844083; // 0x1010533
field public static final int footerDividersEnabled = 16843311; // 0x101022f

View File

@@ -5722,6 +5722,24 @@ public final class ActivityThread {
} finally {
StrictMode.setThreadPolicy(savedPolicy);
}
// Preload fonts resources
try {
final ApplicationInfo info =
sPackageManager.getApplicationInfo(
data.appInfo.packageName,
PackageManager.GET_META_DATA /*flags*/,
UserHandle.myUserId());
if (info.metaData != null) {
final int preloadedFontsResource = info.metaData.getInt(
ApplicationInfo.METADATA_PRELOADED_FONTS, 0);
if (preloadedFontsResource != 0) {
data.info.mResources.preloadFonts(preloadedFontsResource);
}
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/*package*/ final void finishInstrumentation(int resultCode, Bundle results) {

View File

@@ -572,6 +572,11 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
*/
public int privateFlags;
/**
* @hide
*/
public static final String METADATA_PRELOADED_FONTS = "preloaded_fonts";
/**
* The required smallest screen width the application can run on. If 0,
* nothing has been specified. Comes from

View File

@@ -64,6 +64,17 @@ public class FontResourcesParser {
private static FontConfig.Family readFamily(XmlPullParser parser, Resources resources)
throws XmlPullParserException, IOException {
AttributeSet attrs = Xml.asAttributeSet(parser);
TypedArray array = resources.obtainAttributes(attrs, R.styleable.FontFamily);
String authority = array.getString(R.styleable.FontFamily_fontProviderAuthority);
String query = array.getString(R.styleable.FontFamily_fontProviderQuery);
array.recycle();
if (authority != null && query != null) {
while (parser.next() != XmlPullParser.END_TAG) {
skip(parser);
}
return new FontConfig.Family(authority, query);
}
List<FontConfig.Font> fonts = new ArrayList<>();
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
@@ -84,11 +95,12 @@ public class FontResourcesParser {
int weight = array.getInt(R.styleable.FontFamilyFont_fontWeight, NORMAL_WEIGHT);
boolean isItalic = ITALIC == array.getInt(R.styleable.FontFamilyFont_fontStyle, 0);
String filename = array.getString(R.styleable.FontFamilyFont_font);
int resourceId = array.getResourceId(R.styleable.FontFamilyFont_font, 0);
array.recycle();
while (parser.next() != XmlPullParser.END_TAG) {
skip(parser);
}
return new FontConfig.Font(filename, 0, null, weight, isItalic);
return new FontConfig.Font(filename, 0, null, weight, isItalic, resourceId);
}
private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {

View File

@@ -372,6 +372,20 @@ public class Resources {
return mResourcesImpl.loadFont(this, value, id);
}
/**
* @hide
*/
public void preloadFonts(@FontRes int id) {
final TypedValue value = obtainTempTypedValue();
try {
final ResourcesImpl impl = mResourcesImpl;
impl.getValue(id, value, true);
impl.preloadFonts(this, value, id);
} finally {
releaseTempTypedValue(value);
}
}
/**
* Returns the character sequence necessary for grammatically correct pluralization
* of the given resource ID for the given quantity.

View File

@@ -32,6 +32,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.Config;
import android.content.res.Configuration.NativeConfig;
import android.content.res.Resources.NotFoundException;
import android.graphics.FontFamily;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -52,6 +53,7 @@ import android.view.DisplayAdjustments;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
/**
@@ -781,6 +783,43 @@ public class ResourcesImpl {
return null;
}
/**
* @hide
*/
public void preloadFonts(Resources wrapper, TypedValue value, int id) {
if (value.string == null) {
throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
+ Integer.toHexString(id) + ") is not a Font: " + value);
}
final String file = value.string.toString();
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
try {
final XmlResourceParser rp = loadXmlResourceParser(
file, id, value.assetCookie, "font");
final FontConfig config = FontResourcesParser.parse(rp, wrapper);
final List<FontConfig.Family> families = config.getFamilies();
if (families == null || families.isEmpty()) {
return;
}
for (int j = 0; j < families.size(); j++) {
final FontConfig.Family family = families.get(j);
final List<FontConfig.Font> fonts = family.getFonts();
for (int i = 0; i < fonts.size(); i++) {
int resourceId = fonts.get(i).getResourceId();
wrapper.getFont(resourceId);
}
}
} catch (XmlPullParserException e) {
Log.e(TAG, "Failed to parse xml resource " + file, e);
} catch (IOException e) {
Log.e(TAG, "Failed to read xml resource " + file, e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
/**
* Given the value and id, we can get the XML filename as in value.data, based on that, we
* first try to load CSL from the cache. If not found, try to get from the constant state.

View File

@@ -170,14 +170,24 @@ public final class FontConfig implements Parcelable {
private final int mWeight;
private final boolean mIsItalic;
private ParcelFileDescriptor mFd;
private final int mResourceId;
public Font(String fontName, int ttcIndex, List<Axis> axes, int weight, boolean isItalic) {
/**
* @hide
*/
public Font(String fontName, int ttcIndex, List<Axis> axes, int weight, boolean isItalic,
int resourceId) {
mFontName = fontName;
mTtcIndex = ttcIndex;
mAxes = axes;
mWeight = weight;
mIsItalic = isItalic;
mFd = null;
mResourceId = resourceId;
}
public Font(String fontName, int ttcIndex, List<Axis> axes, int weight, boolean isItalic) {
this(fontName, ttcIndex, axes, weight, isItalic, 0);
}
public Font(Font origin) {
@@ -193,6 +203,7 @@ public final class FontConfig implements Parcelable {
e.printStackTrace();
}
}
mResourceId = origin.mResourceId;
}
/**
@@ -251,6 +262,13 @@ public final class FontConfig implements Parcelable {
mFd = fd;
}
/**
* @hide
*/
public int getResourceId() {
return mResourceId;
}
/**
* @hide
*/
@@ -269,6 +287,7 @@ public final class FontConfig implements Parcelable {
} else {
mFd = null;
}
mResourceId = in.readInt();
}
@Override
@@ -285,6 +304,7 @@ public final class FontConfig implements Parcelable {
if (mFd != null) {
mFd.writeToParcel(out, flag);
}
out.writeInt(mResourceId);
}
@Override
@@ -382,22 +402,40 @@ public final class FontConfig implements Parcelable {
private final List<Font> mFonts;
private final String mLanguage;
private final String mVariant;
private final String mProviderAuthority;
private final String mQuery;
public Family(String name, List<Font> fonts, String language, String variant) {
this.mName = name;
this.mFonts = fonts;
this.mLanguage = language;
this.mVariant = variant;
mName = name;
mFonts = fonts;
mLanguage = language;
mVariant = variant;
mProviderAuthority = null;
mQuery = null;
}
/**
* @hide
*/
public Family(String providerAuthority, String query) {
mName = null;
mFonts = null;
mLanguage = null;
mVariant = null;
mProviderAuthority = providerAuthority;
mQuery = query;
}
public Family(Family origin) {
this.mName = origin.mName;
this.mLanguage = origin.mLanguage;
this.mVariant = origin.mVariant;
this.mFonts = new ArrayList<>();
mName = origin.mName;
mLanguage = origin.mLanguage;
mVariant = origin.mVariant;
mFonts = new ArrayList<>();
for (int i = 0; i < origin.mFonts.size(); i++) {
mFonts.add(new Font(origin.mFonts.get(i)));
}
mProviderAuthority = origin.mProviderAuthority;
mQuery = origin.mQuery;
}
/**
@@ -428,6 +466,20 @@ public final class FontConfig implements Parcelable {
return mVariant;
}
/**
* @hide
*/
public String getProviderAuthority() {
return mProviderAuthority;
}
/**
* @hide
*/
public String getQuery() {
return mQuery;
}
/**
* @hide
*/
@@ -440,6 +492,16 @@ public final class FontConfig implements Parcelable {
}
mLanguage = in.readString();
mVariant = in.readString();
if (in.readInt() == 1) {
mProviderAuthority = in.readString();
} else {
mProviderAuthority = null;
}
if (in.readInt() == 1) {
mQuery = in.readString();
} else {
mQuery = null;
}
}
@Override
@@ -451,6 +513,14 @@ public final class FontConfig implements Parcelable {
}
out.writeString(mLanguage);
out.writeString(mVariant);
out.writeInt(mProviderAuthority == null ? 0 : 1);
if (mProviderAuthority != null) {
out.writeString(mProviderAuthority);
}
out.writeInt(mQuery == null ? 0 : 1);
if (mQuery != null) {
out.writeString(mQuery);
}
}
@Override

View File

@@ -8507,6 +8507,12 @@
<attr name="fontWeight" format="integer" />
</declare-styleable>
<!-- Attributes that are read when parsing a <fontfamily> tag, -->
<declare-styleable name="FontFamily">
<attr name="fontProviderAuthority" format="string" />
<attr name="fontProviderQuery" format="string" />
</declare-styleable>
<!-- @hide -->
<declare-styleable name="RecyclerView">
<attr name="layoutManager" format="string" />

View File

@@ -2794,6 +2794,8 @@
<public name="canCaptureFingerprintGestures" />
<public name="alphabeticModifiers" />
<public name="numericModifiers" />
<public name="fontProviderAuthority" />
<public name="fontProviderQuery" />
</public-group>
<public-group type="style" first-id="0x010302e0">
@@ -2806,6 +2808,7 @@
<public type="attr" name="primaryContentAlpha" />
<public type="attr" name="secondaryContentAlpha" />
<!-- ===============================================================
DO NOT ADD UN-GROUPED ITEMS HERE

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android"
android:fontProviderAuthority="com.example.test.fontprovider"
android:fontProviderQuery="MyRequestedFont">
</font-family>

View File

@@ -15,6 +15,8 @@
*/
package android.content.res;
import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -78,4 +80,19 @@ public class FontResourcesParserTest {
assertEquals(true, font4.isItalic());
assertEquals("res/font/samplefont4.ttf", font4.getFontName());
}
@Test
public void testParseDownloadableFont() throws IOException, XmlPullParserException {
XmlResourceParser parser = mResources.getXml(R.font.samplexmldownloadedfont);
FontConfig result = FontResourcesParser.parse(parser, mResources);
assertNotNull(result);
List<FontConfig.Family> families = result.getFamilies();
assertEquals(1, families.size());
FontConfig.Family family = families.get(0);
assertEquals("com.example.test.fontprovider", family.getProviderAuthority());
assertEquals("MyRequestedFont", family.getQuery());
assertNull(family.getFonts());
}
}

View File

@@ -163,36 +163,54 @@ public class Typeface {
@Nullable
public static Typeface createFromResources(FontConfig config, AssetManager mgr, String path) {
if (sFallbackFonts != null) {
Typeface typeface = findFromCache(mgr, path);
if (typeface != null) return typeface;
List<FontConfig.Family> families = config.getFamilies();
if (families == null || families.isEmpty()) {
throw new RuntimeException(
"Font resource " + path + " contained no font families.");
}
if (families.size() > 1) {
throw new RuntimeException(
"Font resource " + path + " contained more than one family.");
}
FontConfig.Family family = families.get(0);
if (family.getProviderAuthority() != null && family.getQuery() != null) {
// Downloadable font
typeface = findFromCache(
family.getProviderAuthority(), family.getQuery());
if (typeface != null) {
return typeface;
}
// Downloaded font and it wasn't cached, request it again and return a
// default font instead (nothing we can do now).
create(new FontRequest(family.getProviderAuthority(), family.getQuery()),
NO_OP_REQUEST_CALLBACK);
return DEFAULT;
}
FontFamily fontFamily = new FontFamily();
List<FontConfig.Font> fonts = family.getFonts();
if (fonts == null || fonts.isEmpty()) {
throw new RuntimeException("Font resource " + path + " contained no fonts.");
}
for (int i = 0; i < fonts.size(); i++) {
FontConfig.Font font = fonts.get(i);
// TODO: Use style and weight info
if (!fontFamily.addFontFromAssetManager(mgr, font.getFontName(),
0 /* resourceCookie */, false /* isAsset */)) {
return null;
}
}
fontFamily.freeze();
FontFamily[] familyChain = { fontFamily };
typeface = createFromFamiliesWithDefault(familyChain);
synchronized (sDynamicTypefaceCache) {
final String key = createAssetUid(mgr, path);
Typeface typeface = sDynamicTypefaceCache.get(key);
if (typeface != null) return typeface;
List<FontConfig.Family> families = config.getFamilies();
if (families == null || families.isEmpty()) {
throw new RuntimeException("Font resource contained no fonts.");
}
if (families.size() > 1) {
throw new RuntimeException("Font resource contained more than one family.");
}
FontConfig.Family family = families.get(0);
FontFamily fontFamily = new FontFamily();
List<FontConfig.Font> fonts = family.getFonts();
for (int i = 0; i < fonts.size(); i++) {
FontConfig.Font font = fonts.get(i);
// TODO: Use style and weight info
if (!fontFamily.addFontFromAssetManager(mgr, font.getFontName(),
0 /* resourceCookie */, false /* isAsset */)) {
return null;
}
}
fontFamily.freeze();
FontFamily[] familyChain = { fontFamily };
typeface = createFromFamiliesWithDefault(familyChain);
sDynamicTypefaceCache.put(key, typeface);
return typeface;
}
return typeface;
}
return null;
}
@@ -372,6 +390,18 @@ public class Typeface {
void onTypefaceRequestFailed(@FontRequestFailReason int reason);
}
private static final FontRequestCallback NO_OP_REQUEST_CALLBACK = new FontRequestCallback() {
@Override
public void onTypefaceRetrieved(Typeface typeface) {
// Do nothing.
}
@Override
public void onTypefaceRequestFailed(@FontRequestFailReason int reason) {
// Do nothing.
}
};
/**
* Create a typeface object given a family name, and option style information.
* If null is passed for the name, then the "default" font will be chosen.