Merge "Cache Downloaded Fonts in Typeface"
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 3cf36d7..bf81096 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -753,7 +753,7 @@
         }
 
         final String file = value.string.toString();
-        Typeface cached = Typeface.createFromCache(mAssets, file);
+        Typeface cached = Typeface.findFromCache(mAssets, file);
         if (cached != null) {
             return cached;
         }
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 5531871..750ef3f 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -198,9 +198,10 @@
     }
 
     /**
+     * Used by resources for cached loading if the font is available.
      * @hide
      */
-    public static Typeface createFromCache(AssetManager mgr, String path) {
+    public static Typeface findFromCache(AssetManager mgr, String path) {
         synchronized (sDynamicTypefaceCache) {
             final String key = createAssetUid(mgr, path);
             Typeface typeface = sDynamicTypefaceCache.get(key);
@@ -221,6 +222,15 @@
      * @param callback A callback that will be triggered when results are obtained. May not be null.
      */
     public static void create(@NonNull FontRequest request, @NonNull FontRequestCallback callback) {
+        // Check the cache first
+        // TODO: would the developer want to avoid a cache hit and always ask for the freshest
+        // result?
+        Typeface cachedTypeface = findFromCache(
+                request.getProviderAuthority(), request.getQuery());
+        if (cachedTypeface != null) {
+            mHandler.post(() -> callback.onTypefaceRetrieved(cachedTypeface));
+            return;
+        }
         synchronized (sLock) {
             if (sFontsContract == null) {
                 sFontsContract = new FontsContract();
@@ -229,20 +239,34 @@
             final ResultReceiver receiver = new ResultReceiver(null) {
                 @Override
                 public void onReceiveResult(int resultCode, Bundle resultData) {
-                    mHandler.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            receiveResult(request, callback, resultCode, resultData);
-                        }
-                    });
+                    mHandler.post(() -> receiveResult(request, callback, resultCode, resultData));
                 }
             };
             sFontsContract.getFont(request, receiver);
         }
     }
 
+    private static Typeface findFromCache(String providerAuthority, String query) {
+        synchronized (sDynamicTypefaceCache) {
+            final String key = createProviderUid(providerAuthority, query);
+            Typeface typeface = sDynamicTypefaceCache.get(key);
+            if (typeface != null) {
+                return typeface;
+            }
+        }
+        return null;
+    }
+
     private static void receiveResult(FontRequest request, FontRequestCallback callback,
             int resultCode, Bundle resultData) {
+        Typeface cachedTypeface = findFromCache(
+                request.getProviderAuthority(), request.getQuery());
+        if (cachedTypeface != null) {
+            // We already know the result.
+            // Probably the requester requests the same font again in a short interval.
+            callback.onTypefaceRetrieved(cachedTypeface);
+            return;
+        }
         if (resultCode == FontsContract.RESULT_CODE_PROVIDER_NOT_FOUND) {
             callback.onTypefaceRequestFailed(
                     FontRequestCallback.FAIL_REASON_PROVIDER_NOT_FOUND);
@@ -296,8 +320,12 @@
             }
         }
         fontFamily.freeze();
-        callback.onTypefaceRetrieved(Typeface.createFromFamiliesWithDefault(
-                new FontFamily[] {fontFamily}));
+        Typeface typeface = Typeface.createFromFamiliesWithDefault(new FontFamily[] { fontFamily });
+        synchronized (sDynamicTypefaceCache) {
+            String key = createProviderUid(request.getProviderAuthority(), request.getQuery());
+            sDynamicTypefaceCache.put(key, typeface);
+        }
+        callback.onTypefaceRetrieved(typeface);
     }
 
     /**
@@ -464,6 +492,7 @@
     private static String createAssetUid(final AssetManager mgr, String path) {
         final SparseArray<String> pkgs = mgr.getAssignedPackageIdentifiers();
         final StringBuilder builder = new StringBuilder();
+        builder.append("asset:");
         final int size = pkgs.size();
         for (int i = 0; i < size; i++) {
             builder.append(pkgs.valueAt(i));
@@ -474,6 +503,18 @@
     }
 
     /**
+     * Creates a unique id for a given font provider and query.
+     */
+    private static String createProviderUid(String authority, String query) {
+        final StringBuilder builder = new StringBuilder();
+        builder.append("provider:");
+        builder.append(authority);
+        builder.append("-");
+        builder.append(query);
+        return builder.toString();
+    }
+
+    /**
      * Create a new typeface from the specified font file.
      *
      * @param path The path to the font data.