Merge "Declarative downloadable fonts"
This commit is contained in:
committed by
Android (Google) Code Review
commit
02761acc67
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user