Merge "Expose Resources.getFont"
diff --git a/api/current.txt b/api/current.txt
index 2b07a5c..a59e2a1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10723,6 +10723,7 @@
     method public android.graphics.drawable.Drawable getDrawable(int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
     method public deprecated android.graphics.drawable.Drawable getDrawableForDensity(int, int) throws android.content.res.Resources.NotFoundException;
     method public android.graphics.drawable.Drawable getDrawableForDensity(int, int, android.content.res.Resources.Theme);
+    method public android.graphics.Typeface getFont(int) throws android.content.res.Resources.NotFoundException;
     method public float getFraction(int, int, int);
     method public int getIdentifier(java.lang.String, java.lang.String, java.lang.String);
     method public int[] getIntArray(int) throws android.content.res.Resources.NotFoundException;
diff --git a/api/system-current.txt b/api/system-current.txt
index 7cb8aaf..ed52bd1 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -11245,6 +11245,7 @@
     method public android.graphics.drawable.Drawable getDrawable(int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
     method public deprecated android.graphics.drawable.Drawable getDrawableForDensity(int, int) throws android.content.res.Resources.NotFoundException;
     method public android.graphics.drawable.Drawable getDrawableForDensity(int, int, android.content.res.Resources.Theme);
+    method public android.graphics.Typeface getFont(int) throws android.content.res.Resources.NotFoundException;
     method public float getFraction(int, int, int);
     method public int getIdentifier(java.lang.String, java.lang.String, java.lang.String);
     method public int[] getIntArray(int) throws android.content.res.Resources.NotFoundException;
diff --git a/api/test-current.txt b/api/test-current.txt
index 82715b3..3fe04f7 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -10755,6 +10755,7 @@
     method public android.graphics.drawable.Drawable getDrawable(int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
     method public deprecated android.graphics.drawable.Drawable getDrawableForDensity(int, int) throws android.content.res.Resources.NotFoundException;
     method public android.graphics.drawable.Drawable getDrawableForDensity(int, int, android.content.res.Resources.Theme);
+    method public android.graphics.Typeface getFont(int) throws android.content.res.Resources.NotFoundException;
     method public float getFraction(int, int, int);
     method public int getIdentifier(java.lang.String, java.lang.String, java.lang.String);
     method public int[] getIntArray(int) throws android.content.res.Resources.NotFoundException;
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index ad11307..c3185a7 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -40,6 +40,7 @@
 import android.annotation.XmlRes;
 import android.content.pm.ActivityInfo;
 import android.graphics.Movie;
+import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Drawable.ConstantState;
 import android.graphics.drawable.DrawableInflater;
@@ -333,7 +334,35 @@
             return res;
         }
         throw new NotFoundException("String resource ID #0x"
-                                    + Integer.toHexString(id));
+                + Integer.toHexString(id));
+    }
+
+    /**
+     * Return the Typeface value associated with a particular resource ID.
+     * {@more}
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return Typeface The Typeface data associated with the resource.
+     */
+    @NonNull public Typeface getFont(@StringRes int id) throws NotFoundException {
+        final TypedValue value = obtainTempTypedValue();
+        try {
+            final ResourcesImpl impl = mResourcesImpl;
+            impl.getValue(id, value, true);
+            Typeface typeface = impl.loadFont(value, id);
+            if (typeface != null) {
+                return typeface;
+            }
+        } finally {
+            releaseTempTypedValue(value);
+        }
+        throw new NotFoundException("Font resource ID #0x"
+                + Integer.toHexString(id));
     }
 
     /**
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index eb010e4..05892e0 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -31,6 +31,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.Resources.NotFoundException;
+import android.graphics.Typeface;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.icu.text.PluralRules;
@@ -47,6 +48,7 @@
 import android.view.Display;
 import android.view.DisplayAdjustments;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.util.Arrays;
 import java.util.Locale;
@@ -740,6 +742,36 @@
     }
 
     /**
+     * Loads a font from XML or resources stream.
+     */
+    @Nullable
+    public Typeface loadFont(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();
+
+        if (DEBUG_LOAD) {
+            Log.v(TAG, "Loading font for cookie " + value.assetCookie + ": " + file);
+        }
+
+        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
+        try {
+            if (file.endsWith(".xml")) {
+                // TODO handle xml type font definitions
+            } else {
+                return Typeface.createFromResources(
+                        mAssets, value.string.toString(), value.assetCookie);
+            }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+        }
+        return null;
+    }
+
+    /**
      * 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.
      * Last, parse the XML and generate the CSL.
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 15e7165..2504e54 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -190,8 +190,8 @@
     delete static_cast<Asset*>(context);
 }
 
-static jboolean FontFamily_addFontFromAsset(JNIEnv* env, jobject, jlong familyPtr,
-        jobject jassetMgr, jstring jpath) {
+static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong familyPtr,
+        jobject jassetMgr, jstring jpath, jint cookie, jboolean isAsset) {
     NPE_CHECK_RETURN_ZERO(env, jassetMgr);
     NPE_CHECK_RETURN_ZERO(env, jpath);
 
@@ -201,7 +201,18 @@
     }
 
     ScopedUtfChars str(env, jpath);
-    Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
+    if (str.c_str() == nullptr) {
+        return false;
+    }
+
+    Asset* asset;
+    if (isAsset) {
+        asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
+    } else {
+        asset = cookie ? mgr->openNonAsset(static_cast<int32_t>(cookie), str.c_str(),
+                Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
+    }
+
     if (NULL == asset) {
         return false;
     }
@@ -234,8 +245,8 @@
     { "nAddFont",              "(JLjava/nio/ByteBuffer;I)Z", (void*)FontFamily_addFont },
     { "nAddFontWeightStyle",   "(JLjava/nio/ByteBuffer;ILjava/util/List;IZ)Z",
             (void*)FontFamily_addFontWeightStyle },
-    { "nAddFontFromAsset",     "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z",
-            (void*)FontFamily_addFontFromAsset },
+    { "nAddFontFromAssetManager",     "(JLandroid/content/res/AssetManager;Ljava/lang/String;IZ)Z",
+            (void*)FontFamily_addFontFromAssetManager },
 };
 
 int register_android_graphics_FontFamily(JNIEnv* env)
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index e48bf79..cd5071e8 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -85,8 +85,9 @@
         return nAddFontWeightStyle(mNativePtr, font, ttcIndex, axes, weight, style);
     }
 
-    public boolean addFontFromAsset(AssetManager mgr, String path) {
-        return nAddFontFromAsset(mNativePtr, mgr, path);
+    public boolean addFontFromAssetManager(AssetManager mgr, String path, int cookie,
+            boolean isAsset) {
+        return nAddFontFromAssetManager(mNativePtr, mgr, path, cookie, isAsset);
     }
 
     private static native long nCreateFamily(String lang, int variant);
@@ -95,6 +96,6 @@
     private static native boolean nAddFontWeightStyle(long nativeFamily, ByteBuffer font,
             int ttcIndex, List<FontListParser.Axis> listOfAxis,
             int weight, boolean isItalic);
-    private static native boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr,
-            String path);
+    private static native boolean nAddFontFromAssetManager(long nativeFamily, AssetManager mgr,
+            String path, int cookie, boolean isAsset);
 }
diff --git a/graphics/java/android/graphics/FontResourcesParser.java b/graphics/java/android/graphics/FontResourcesParser.java
new file mode 100644
index 0000000..b4109cf
--- /dev/null
+++ b/graphics/java/android/graphics/FontResourcesParser.java
@@ -0,0 +1,113 @@
+/*
+ * 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 org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * Parser for xml type font resources.
+ * @hide
+ */
+public class FontResourcesParser {
+    private static final String ANDROID_NAMESPACE = "http://schemas.android.com/apk/res/android";
+
+    /* Parse fallback list (no names) */
+    public static FontListParser.Config parse(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        int type;
+        //noinspection StatementWithEmptyBody
+        while ((type=parser.next()) != XmlPullParser.START_TAG
+                && type != XmlPullParser.END_DOCUMENT) {
+            // Empty loop.
+        }
+
+        if (type != XmlPullParser.START_TAG) {
+            throw new XmlPullParserException("No start tag found");
+        }
+        return readFamilies(parser);
+    }
+
+    private static FontListParser.Config readFamilies(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        FontListParser.Config config = new FontListParser.Config();
+        parser.require(XmlPullParser.START_TAG, null, "font-family");
+        String tag = parser.getName();
+        if (tag.equals("font-family")) {
+            config.families.add(readFamily(parser));
+        } else {
+            skip(parser);
+        }
+        return config;
+    }
+
+    private static FontListParser.Family readFamily(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        String name = parser.getAttributeValue(null, "name");
+        String lang = parser.getAttributeValue(null, "lang");
+        String variant = parser.getAttributeValue(null, "variant");
+        List<FontListParser.Font> fonts = new ArrayList<>();
+        while (parser.next() != XmlPullParser.END_TAG) {
+            if (parser.getEventType() != XmlPullParser.START_TAG) continue;
+            String tag = parser.getName();
+            if (tag.equals("font")) {
+                fonts.add(readFont(parser));
+            } else {
+                skip(parser);
+            }
+        }
+        return new FontListParser.Family(name, fonts, lang, variant);
+    }
+
+    /** Matches leading and trailing XML whitespace. */
+    private static final Pattern FILENAME_WHITESPACE_PATTERN =
+            Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$");
+
+    private static FontListParser.Font readFont(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+
+        List<FontListParser.Axis> axes = new ArrayList<>();
+
+        String weightStr = parser.getAttributeValue(ANDROID_NAMESPACE, "fontWeight");
+        int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
+
+        boolean isItalic = "italic".equals(
+                parser.getAttributeValue(ANDROID_NAMESPACE, "fontStyle"));
+
+        String filename = parser.getAttributeValue(ANDROID_NAMESPACE, "font");
+        String fullFilename = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
+        return new FontListParser.Font(fullFilename, 0, axes, weight, isItalic);
+    }
+
+    private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
+        int depth = 1;
+        while (depth > 0) {
+            switch (parser.next()) {
+                case XmlPullParser.START_TAG:
+                    depth++;
+                    break;
+                case XmlPullParser.END_TAG:
+                    depth--;
+                    break;
+            }
+        }
+    }
+}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 2886f0d..eebe538 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -16,6 +16,7 @@
 
 package android.graphics;
 
+import android.annotation.NonNull;
 import android.content.res.AssetManager;
 import android.util.Log;
 import android.util.LongSparseArray;
@@ -109,6 +110,30 @@
     }
 
     /**
+     * @hide
+     * Used by Resources.
+     */
+    @NonNull
+    public static Typeface createFromResources(AssetManager mgr, String path, int cookie) {
+        if (sFallbackFonts != null) {
+            synchronized (sDynamicTypefaceCache) {
+                final String key = createAssetUid(mgr, path);
+                Typeface typeface = sDynamicTypefaceCache.get(key);
+                if (typeface != null) return typeface;
+
+                FontFamily fontFamily = new FontFamily();
+                if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */)) {
+                    FontFamily[] families = { fontFamily };
+                    typeface = createFromFamiliesWithDefault(families);
+                    sDynamicTypefaceCache.put(key, typeface);
+                    return typeface;
+                }
+            }
+        }
+        throw new RuntimeException("Font resource not found " + path);
+    }
+
+    /**
      * 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.
      * The resulting typeface object can be queried (getStyle()) to discover what
@@ -195,7 +220,7 @@
                 if (typeface != null) return typeface;
 
                 FontFamily fontFamily = new FontFamily();
-                if (fontFamily.addFontFromAsset(mgr, path)) {
+                if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */)) {
                     FontFamily[] families = { fontFamily };
                     typeface = createFromFamiliesWithDefault(families);
                     sDynamicTypefaceCache.put(key, typeface);