diff --git a/api/current.txt b/api/current.txt index 2c2098278ed76..5a5f9bc96b3f0 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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 diff --git a/api/system-current.txt b/api/system-current.txt index bafd355684c3b..acbd8b9af7e2b 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -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 diff --git a/api/test-current.txt b/api/test-current.txt index 43f1a2e4bdbef..4de1b1d93d14b 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -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 diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 50fee835168e2..376823ed09da4 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -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) { diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 8465f0fd5571c..1fa4181cc2f8a 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -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 diff --git a/core/java/android/content/res/FontResourcesParser.java b/core/java/android/content/res/FontResourcesParser.java index 7ea62db3b1f00..3f8f90ebf0548 100644 --- a/core/java/android/content/res/FontResourcesParser.java +++ b/core/java/android/content/res/FontResourcesParser.java @@ -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 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 { diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 04e4454a95c4f..21d4b2277a8e7 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -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. diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index bf8109627e7e9..38efa49015534 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -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 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 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. diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java index 3048a38820100..82e44dc86f897 100644 --- a/core/java/android/text/FontConfig.java +++ b/core/java/android/text/FontConfig.java @@ -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 axes, int weight, boolean isItalic) { + /** + * @hide + */ + public Font(String fontName, int ttcIndex, List 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 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 mFonts; private final String mLanguage; private final String mVariant; + private final String mProviderAuthority; + private final String mQuery; public Family(String name, List 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 diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index d8fc1d183f659..36bb821bbc418 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -8507,6 +8507,12 @@ + + + + + + diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 34659aaa96795..78489ebc7e6e1 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2794,6 +2794,8 @@ + + @@ -2806,6 +2808,7 @@ +