Loading api/current.txt +0 −12 Original line number Diff line number Diff line Loading @@ -13777,7 +13777,6 @@ package android.graphics { } public class Typeface { method public static deprecated void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback); method public static android.graphics.Typeface create(java.lang.String, int); method public static android.graphics.Typeface create(android.graphics.Typeface, int); method public static android.graphics.Typeface createFromAsset(android.content.res.AssetManager, java.lang.String); Loading Loading @@ -13812,17 +13811,6 @@ package android.graphics { method public android.graphics.Typeface.Builder setWeight(int); } public static abstract deprecated interface Typeface.FontRequestCallback { method public abstract void onTypefaceRequestFailed(int); method public abstract void onTypefaceRetrieved(android.graphics.Typeface); field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1 field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2 field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe } public class Xfermode { ctor public Xfermode(); } api/system-current.txt +0 −12 Original line number Diff line number Diff line Loading @@ -14552,7 +14552,6 @@ package android.graphics { } public class Typeface { method public static deprecated void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback); method public static android.graphics.Typeface create(java.lang.String, int); method public static android.graphics.Typeface create(android.graphics.Typeface, int); method public static android.graphics.Typeface createFromAsset(android.content.res.AssetManager, java.lang.String); Loading Loading @@ -14587,17 +14586,6 @@ package android.graphics { method public android.graphics.Typeface.Builder setWeight(int); } public static abstract deprecated interface Typeface.FontRequestCallback { method public abstract void onTypefaceRequestFailed(int); method public abstract void onTypefaceRetrieved(android.graphics.Typeface); field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1 field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2 field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe } public class Xfermode { ctor public Xfermode(); } api/test-current.txt +0 −12 Original line number Diff line number Diff line Loading @@ -13819,7 +13819,6 @@ package android.graphics { } public class Typeface { method public static deprecated void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback); method public static android.graphics.Typeface create(java.lang.String, int); method public static android.graphics.Typeface create(android.graphics.Typeface, int); method public static android.graphics.Typeface createFromAsset(android.content.res.AssetManager, java.lang.String); Loading Loading @@ -13854,17 +13853,6 @@ package android.graphics { method public android.graphics.Typeface.Builder setWeight(int); } public static abstract deprecated interface Typeface.FontRequestCallback { method public abstract void onTypefaceRequestFailed(int); method public abstract void onTypefaceRetrieved(android.graphics.Typeface); field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1 field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2 field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe } public class Xfermode { ctor public Xfermode(); } core/java/android/provider/FontsContract.java +33 −59 Original line number Diff line number Diff line Loading @@ -33,7 +33,6 @@ import android.content.pm.Signature; import android.database.Cursor; import android.graphics.Typeface; import android.graphics.fonts.FontRequest; import android.graphics.fonts.FontResult; import android.graphics.fonts.FontVariationAxis; import android.net.Uri; import android.os.Bundle; Loading @@ -43,6 +42,7 @@ import android.os.HandlerThread; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.ResultReceiver; import android.util.ArraySet; import android.util.Log; import android.util.LruCache; Loading @@ -64,6 +64,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * Utility class to deal with Font ContentProviders. Loading Loading @@ -181,6 +182,8 @@ public class FontsContract { private Handler mHandler; @GuardedBy("mLock") private HandlerThread mThread; @GuardedBy("mLock") private Set<String> mInQueueSet; private static final LruCache<String, Typeface> sTypefaceCache = new LruCache<>(16); Loading Loading @@ -330,81 +333,54 @@ public class FontsContract { mThread.quitSafely(); mThread = null; mHandler = null; mInQueueSet = null; } } } }; /** @hide */ public void getFont(FontRequest request, ResultReceiver receiver) { public Typeface getFontOrWarmUpCache(FontRequest request) { final String id = request.getIdentifier(); Typeface cachedTypeface = sTypefaceCache.get(id); if (cachedTypeface != null) { return cachedTypeface; } // Unfortunately the typeface is not available at this time, but requesting from the font // provider takes too much time. For now, request the font data to ensure it is in the cache // next time and return. synchronized (mLock) { if (mHandler == null) { mThread = new HandlerThread("fonts", Process.THREAD_PRIORITY_BACKGROUND); mThread.start(); mHandler = new Handler(mThread.getLooper()); mInQueueSet = new ArraySet<String>(); } mHandler.post(() -> { ProviderInfo providerInfo; try { providerInfo = getProvider(mPackageManager, request); if (providerInfo == null) { receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null); return; } } catch (PackageManager.NameNotFoundException e) { receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null); return; } FontInfo[] fonts; try { fonts = getFontFromProvider(mContext, request, providerInfo.authority, null /* cancellation signal */); } catch (InvalidFormatException e) { receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null); return; if (mInQueueSet.contains(id)) { return null; // Already requested. } ArrayList<FontResult> result = new ArrayList<>(); int resultCode = -1; for (FontInfo font : fonts) { try { resultCode = font.getResultCode(); if (resultCode != Columns.RESULT_CODE_OK) { if (resultCode < 0) { // Negative values are reserved for the internal errors. resultCode = Columns.RESULT_CODE_FONT_NOT_FOUND; mInQueueSet.add(id); mHandler.post(() -> { synchronized (mLock) { mInQueueSet.remove(id); } for (int i = 0; i < result.size(); ++i) { try { result.get(i).getFileDescriptor().close(); } catch (IOException e) { // Ignore, as we are closing fds for cleanup. FontFamilyResult result = fetchFonts(mContext, null, request); if (result.getStatusCode() == FontFamilyResult.STATUS_OK) { Typeface typeface = buildTypeface(mContext, null, result.getFonts()); if (typeface != null) { sTypefaceCache.put(id, typeface); } } receiver.send(resultCode, null); return; } ParcelFileDescriptor pfd = mContext.getContentResolver().openFileDescriptor( font.getUri(), "r"); result.add(new FontResult(pfd, font.getTtcIndex(), FontVariationAxis.toFontVariationSettings(font.getAxes()), font.getWeight(), font.isItalic())); } catch (FileNotFoundException e) { Log.e(TAG, "FileNotFoundException raised when interacting with content " + "provider " + providerInfo.authority, e); } } if (!result.isEmpty()) { Bundle bundle = new Bundle(); bundle.putParcelableArrayList(PARCEL_FONT_RESULTS, result); receiver.send(Columns.RESULT_CODE_OK, bundle); return; } catch (NameNotFoundException e) { // Ignore. } receiver.send(Columns.RESULT_CODE_FONT_NOT_FOUND, null); }); mHandler.removeCallbacks(mReplaceDispatcherThreadRunnable); mHandler.postDelayed(mReplaceDispatcherThreadRunnable, THREAD_RENEWAL_THRESHOLD_MS); } return null; } /** Loading Loading @@ -452,16 +428,14 @@ public class FontsContract { public FontRequestCallback() {} /** * Called then a Typeface request done via {@link Typeface#create(FontRequest, * FontRequestCallback)} is complete. Note that this method will not be called if * {@link #onTypefaceRequestFailed(int)} is called instead. * Called then a Typeface request done via {@link #requestFont} is complete. Note that this * method will not be called if {@link #onTypefaceRequestFailed(int)} is called instead. * @param typeface The Typeface object retrieved. */ public void onTypefaceRetrieved(Typeface typeface) {} /** * Called when a Typeface request done via {@link Typeface#create(FontRequest, * FontRequestCallback)} fails. * Called when a Typeface request done via {@link #requestFont}} fails. * @param reason One of {@link #FAIL_REASON_PROVIDER_NOT_FOUND}, * {@link #FAIL_REASON_FONT_NOT_FOUND}, * {@link #FAIL_REASON_FONT_LOAD_ERROR}, Loading graphics/java/android/graphics/Typeface.java +7 −217 Original line number Diff line number Diff line Loading @@ -31,7 +31,6 @@ import android.content.Context; import android.content.res.AssetManager; import android.graphics.FontListParser; import android.graphics.fonts.FontRequest; import android.graphics.fonts.FontResult; import android.graphics.fonts.FontVariationAxis; import android.graphics.fonts.FontVariationAxis.InvalidFormatException; import android.net.Uri; Loading Loading @@ -102,8 +101,6 @@ public class Typeface { new LongSparseArray<>(3); @GuardedBy("sLock") private static FontsContract sFontsContract; @GuardedBy("sLock") private static Handler sHandler; /** * Cache for Typeface objects dynamically loaded from assets. Currently max size is 16. Loading Loading @@ -205,16 +202,9 @@ public class Typeface { public static Typeface createFromResources( FamilyResourceEntry entry, AssetManager mgr, String path) { if (sFallbackFonts != null) { Typeface typeface = findFromCache(mgr, path); if (typeface != null) return typeface; if (entry instanceof ProviderResourceEntry) { final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry; // Downloadable font typeface = findFromCache(providerEntry.getAuthority(), providerEntry.getQuery()); if (typeface != null) { return typeface; } List<List<String>> givenCerts = providerEntry.getCerts(); List<List<byte[]>> certs = new ArrayList<>(); if (givenCerts != null) { Loading @@ -229,11 +219,15 @@ public class 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(providerEntry.getAuthority(), providerEntry.getPackage(), providerEntry.getQuery(), certs), NO_OP_REQUEST_CALLBACK); return DEFAULT; FontRequest request = new FontRequest(providerEntry.getAuthority(), providerEntry.getPackage(), providerEntry.getQuery(), certs); Typeface typeface = sFontsContract.getFontOrWarmUpCache(request); return typeface == null ? DEFAULT : typeface; } Typeface typeface = findFromCache(mgr, path); if (typeface != null) return typeface; // family is FontFamilyFilesResourceEntry final FontFamilyFilesResourceEntry filesEntry = (FontFamilyFilesResourceEntry) entry; Loading Loading @@ -291,213 +285,9 @@ public class Typeface { synchronized (sLock) { if (sFontsContract == null) { sFontsContract = new FontsContract(context); sHandler = new Handler(); } } } /** * Create a typeface object given a font request. The font will be asynchronously fetched, * therefore the result is delivered to the given callback. See {@link FontRequest}. * Only one of the methods in callback will be invoked, depending on whether the request * succeeds or fails. These calls will happen on the main thread. * @param request A {@link FontRequest} object that identifies the provider and query for the * request. May not be null. * @param callback A callback that will be triggered when results are obtained. May not be null. */ @Deprecated 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) { sHandler.post(() -> callback.onTypefaceRetrieved(cachedTypeface)); return; } synchronized (sLock) { if (sFontsContract == null) { throw new RuntimeException("Context not initialized, can't query provider"); } final ResultReceiver receiver = new ResultReceiver(null) { @Override public void onReceiveResult(int resultCode, Bundle resultData) { sHandler.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.Columns.RESULT_CODE_OK) { callback.onTypefaceRequestFailed(resultCode); return; } if (resultData == null) { callback.onTypefaceRequestFailed( FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND); return; } List<FontResult> resultList = resultData.getParcelableArrayList(FontsContract.PARCEL_FONT_RESULTS); if (resultList == null || resultList.isEmpty()) { callback.onTypefaceRequestFailed( FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND); return; } FontFamily fontFamily = new FontFamily(); for (int i = 0; i < resultList.size(); ++i) { FontResult result = resultList.get(i); ParcelFileDescriptor fd = result.getFileDescriptor(); if (fd == null) { callback.onTypefaceRequestFailed( FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR); return; } try (FileInputStream is = new FileInputStream(fd.getFileDescriptor())) { FileChannel fileChannel = is.getChannel(); long fontSize = fileChannel.size(); ByteBuffer fontBuffer = fileChannel.map( FileChannel.MapMode.READ_ONLY, 0, fontSize); int weight = result.getWeight(); int italic = result.getItalic() ? STYLE_ITALIC : STYLE_NORMAL; FontVariationAxis[] axes = null; try { axes = FontVariationAxis.fromFontVariationSettings( result.getFontVariationSettings()); } catch (FontVariationAxis.InvalidFormatException e) { // TODO: Nice to pass FontVariationAxis[] directly instead of string. } if (!fontFamily.addFontFromBuffer(fontBuffer, result.getTtcIndex(), axes, weight, italic)) { Log.e(TAG, "Error creating font " + request.getQuery()); callback.onTypefaceRequestFailed( FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR); return; } } catch (IOException e) { Log.e(TAG, "Error reading font " + request.getQuery(), e); callback.onTypefaceRequestFailed( FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR); return; } finally { IoUtils.closeQuietly(fd); } } if (!fontFamily.freeze()) { callback.onTypefaceRequestFailed( FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR); return; } Typeface typeface = Typeface.createFromFamiliesWithDefault( new FontFamily[] { fontFamily }, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE); synchronized (sDynamicTypefaceCache) { String key = createProviderUid(request.getProviderAuthority(), request.getQuery()); sDynamicTypefaceCache.put(key, typeface); } callback.onTypefaceRetrieved(typeface); } /** * Interface used to receive asynchronously fetched typefaces. */ @Deprecated public interface FontRequestCallback { /** * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given * provider was not found on the device. */ int FAIL_REASON_PROVIDER_NOT_FOUND = FontsContract.RESULT_CODE_PROVIDER_NOT_FOUND; /** * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given * provider must be authenticated and the given certificates do not match its signature. */ int FAIL_REASON_WRONG_CERTIFICATES = FontsContract.RESULT_CODE_WRONG_CERTIFICATES; /** * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font * returned by the provider was not loaded properly. */ int FAIL_REASON_FONT_LOAD_ERROR = -3; /** * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font * provider did not return any results for the given query. */ int FAIL_REASON_FONT_NOT_FOUND = FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND; /** * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font * provider found the queried font, but it is currently unavailable. */ int FAIL_REASON_FONT_UNAVAILABLE = FontsContract.Columns.RESULT_CODE_FONT_UNAVAILABLE; /** * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given * query was not supported by the provider. */ int FAIL_REASON_MALFORMED_QUERY = FontsContract.Columns.RESULT_CODE_MALFORMED_QUERY; /** @hide */ @IntDef({ FAIL_REASON_PROVIDER_NOT_FOUND, FAIL_REASON_FONT_LOAD_ERROR, FAIL_REASON_FONT_NOT_FOUND, FAIL_REASON_FONT_UNAVAILABLE, FAIL_REASON_MALFORMED_QUERY }) @Retention(RetentionPolicy.SOURCE) @interface FontRequestFailReason {} /** * Called then a Typeface request done via {@link Typeface#create(FontRequest, * FontRequestCallback)} is complete. Note that this method will not be called if * {@link #onTypefaceRequestFailed(int)} is called instead. * @param typeface The Typeface object retrieved. */ void onTypefaceRetrieved(Typeface typeface); /** * Called when a Typeface request done via {@link Typeface#create(FontRequest, * FontRequestCallback)} fails. * @param reason May be one of {@link #FAIL_REASON_PROVIDER_NOT_FOUND}, * {@link #FAIL_REASON_FONT_NOT_FOUND}, * {@link #FAIL_REASON_FONT_LOAD_ERROR}, * {@link #FAIL_REASON_FONT_UNAVAILABLE} or * {@link #FAIL_REASON_MALFORMED_QUERY} if returned by the system. May also be * a positive value greater than 0 defined by the font provider as an * additional error code. Refer to the provider's documentation for more * information on possible returned error codes. */ 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. } }; /** * A builder class for creating new Typeface instance. Loading Loading
api/current.txt +0 −12 Original line number Diff line number Diff line Loading @@ -13777,7 +13777,6 @@ package android.graphics { } public class Typeface { method public static deprecated void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback); method public static android.graphics.Typeface create(java.lang.String, int); method public static android.graphics.Typeface create(android.graphics.Typeface, int); method public static android.graphics.Typeface createFromAsset(android.content.res.AssetManager, java.lang.String); Loading Loading @@ -13812,17 +13811,6 @@ package android.graphics { method public android.graphics.Typeface.Builder setWeight(int); } public static abstract deprecated interface Typeface.FontRequestCallback { method public abstract void onTypefaceRequestFailed(int); method public abstract void onTypefaceRetrieved(android.graphics.Typeface); field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1 field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2 field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe } public class Xfermode { ctor public Xfermode(); }
api/system-current.txt +0 −12 Original line number Diff line number Diff line Loading @@ -14552,7 +14552,6 @@ package android.graphics { } public class Typeface { method public static deprecated void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback); method public static android.graphics.Typeface create(java.lang.String, int); method public static android.graphics.Typeface create(android.graphics.Typeface, int); method public static android.graphics.Typeface createFromAsset(android.content.res.AssetManager, java.lang.String); Loading Loading @@ -14587,17 +14586,6 @@ package android.graphics { method public android.graphics.Typeface.Builder setWeight(int); } public static abstract deprecated interface Typeface.FontRequestCallback { method public abstract void onTypefaceRequestFailed(int); method public abstract void onTypefaceRetrieved(android.graphics.Typeface); field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1 field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2 field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe } public class Xfermode { ctor public Xfermode(); }
api/test-current.txt +0 −12 Original line number Diff line number Diff line Loading @@ -13819,7 +13819,6 @@ package android.graphics { } public class Typeface { method public static deprecated void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback); method public static android.graphics.Typeface create(java.lang.String, int); method public static android.graphics.Typeface create(android.graphics.Typeface, int); method public static android.graphics.Typeface createFromAsset(android.content.res.AssetManager, java.lang.String); Loading Loading @@ -13854,17 +13853,6 @@ package android.graphics { method public android.graphics.Typeface.Builder setWeight(int); } public static abstract deprecated interface Typeface.FontRequestCallback { method public abstract void onTypefaceRequestFailed(int); method public abstract void onTypefaceRetrieved(android.graphics.Typeface); field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1 field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2 field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe } public class Xfermode { ctor public Xfermode(); }
core/java/android/provider/FontsContract.java +33 −59 Original line number Diff line number Diff line Loading @@ -33,7 +33,6 @@ import android.content.pm.Signature; import android.database.Cursor; import android.graphics.Typeface; import android.graphics.fonts.FontRequest; import android.graphics.fonts.FontResult; import android.graphics.fonts.FontVariationAxis; import android.net.Uri; import android.os.Bundle; Loading @@ -43,6 +42,7 @@ import android.os.HandlerThread; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.ResultReceiver; import android.util.ArraySet; import android.util.Log; import android.util.LruCache; Loading @@ -64,6 +64,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * Utility class to deal with Font ContentProviders. Loading Loading @@ -181,6 +182,8 @@ public class FontsContract { private Handler mHandler; @GuardedBy("mLock") private HandlerThread mThread; @GuardedBy("mLock") private Set<String> mInQueueSet; private static final LruCache<String, Typeface> sTypefaceCache = new LruCache<>(16); Loading Loading @@ -330,81 +333,54 @@ public class FontsContract { mThread.quitSafely(); mThread = null; mHandler = null; mInQueueSet = null; } } } }; /** @hide */ public void getFont(FontRequest request, ResultReceiver receiver) { public Typeface getFontOrWarmUpCache(FontRequest request) { final String id = request.getIdentifier(); Typeface cachedTypeface = sTypefaceCache.get(id); if (cachedTypeface != null) { return cachedTypeface; } // Unfortunately the typeface is not available at this time, but requesting from the font // provider takes too much time. For now, request the font data to ensure it is in the cache // next time and return. synchronized (mLock) { if (mHandler == null) { mThread = new HandlerThread("fonts", Process.THREAD_PRIORITY_BACKGROUND); mThread.start(); mHandler = new Handler(mThread.getLooper()); mInQueueSet = new ArraySet<String>(); } mHandler.post(() -> { ProviderInfo providerInfo; try { providerInfo = getProvider(mPackageManager, request); if (providerInfo == null) { receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null); return; } } catch (PackageManager.NameNotFoundException e) { receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null); return; } FontInfo[] fonts; try { fonts = getFontFromProvider(mContext, request, providerInfo.authority, null /* cancellation signal */); } catch (InvalidFormatException e) { receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null); return; if (mInQueueSet.contains(id)) { return null; // Already requested. } ArrayList<FontResult> result = new ArrayList<>(); int resultCode = -1; for (FontInfo font : fonts) { try { resultCode = font.getResultCode(); if (resultCode != Columns.RESULT_CODE_OK) { if (resultCode < 0) { // Negative values are reserved for the internal errors. resultCode = Columns.RESULT_CODE_FONT_NOT_FOUND; mInQueueSet.add(id); mHandler.post(() -> { synchronized (mLock) { mInQueueSet.remove(id); } for (int i = 0; i < result.size(); ++i) { try { result.get(i).getFileDescriptor().close(); } catch (IOException e) { // Ignore, as we are closing fds for cleanup. FontFamilyResult result = fetchFonts(mContext, null, request); if (result.getStatusCode() == FontFamilyResult.STATUS_OK) { Typeface typeface = buildTypeface(mContext, null, result.getFonts()); if (typeface != null) { sTypefaceCache.put(id, typeface); } } receiver.send(resultCode, null); return; } ParcelFileDescriptor pfd = mContext.getContentResolver().openFileDescriptor( font.getUri(), "r"); result.add(new FontResult(pfd, font.getTtcIndex(), FontVariationAxis.toFontVariationSettings(font.getAxes()), font.getWeight(), font.isItalic())); } catch (FileNotFoundException e) { Log.e(TAG, "FileNotFoundException raised when interacting with content " + "provider " + providerInfo.authority, e); } } if (!result.isEmpty()) { Bundle bundle = new Bundle(); bundle.putParcelableArrayList(PARCEL_FONT_RESULTS, result); receiver.send(Columns.RESULT_CODE_OK, bundle); return; } catch (NameNotFoundException e) { // Ignore. } receiver.send(Columns.RESULT_CODE_FONT_NOT_FOUND, null); }); mHandler.removeCallbacks(mReplaceDispatcherThreadRunnable); mHandler.postDelayed(mReplaceDispatcherThreadRunnable, THREAD_RENEWAL_THRESHOLD_MS); } return null; } /** Loading Loading @@ -452,16 +428,14 @@ public class FontsContract { public FontRequestCallback() {} /** * Called then a Typeface request done via {@link Typeface#create(FontRequest, * FontRequestCallback)} is complete. Note that this method will not be called if * {@link #onTypefaceRequestFailed(int)} is called instead. * Called then a Typeface request done via {@link #requestFont} is complete. Note that this * method will not be called if {@link #onTypefaceRequestFailed(int)} is called instead. * @param typeface The Typeface object retrieved. */ public void onTypefaceRetrieved(Typeface typeface) {} /** * Called when a Typeface request done via {@link Typeface#create(FontRequest, * FontRequestCallback)} fails. * Called when a Typeface request done via {@link #requestFont}} fails. * @param reason One of {@link #FAIL_REASON_PROVIDER_NOT_FOUND}, * {@link #FAIL_REASON_FONT_NOT_FOUND}, * {@link #FAIL_REASON_FONT_LOAD_ERROR}, Loading
graphics/java/android/graphics/Typeface.java +7 −217 Original line number Diff line number Diff line Loading @@ -31,7 +31,6 @@ import android.content.Context; import android.content.res.AssetManager; import android.graphics.FontListParser; import android.graphics.fonts.FontRequest; import android.graphics.fonts.FontResult; import android.graphics.fonts.FontVariationAxis; import android.graphics.fonts.FontVariationAxis.InvalidFormatException; import android.net.Uri; Loading Loading @@ -102,8 +101,6 @@ public class Typeface { new LongSparseArray<>(3); @GuardedBy("sLock") private static FontsContract sFontsContract; @GuardedBy("sLock") private static Handler sHandler; /** * Cache for Typeface objects dynamically loaded from assets. Currently max size is 16. Loading Loading @@ -205,16 +202,9 @@ public class Typeface { public static Typeface createFromResources( FamilyResourceEntry entry, AssetManager mgr, String path) { if (sFallbackFonts != null) { Typeface typeface = findFromCache(mgr, path); if (typeface != null) return typeface; if (entry instanceof ProviderResourceEntry) { final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry; // Downloadable font typeface = findFromCache(providerEntry.getAuthority(), providerEntry.getQuery()); if (typeface != null) { return typeface; } List<List<String>> givenCerts = providerEntry.getCerts(); List<List<byte[]>> certs = new ArrayList<>(); if (givenCerts != null) { Loading @@ -229,11 +219,15 @@ public class 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(providerEntry.getAuthority(), providerEntry.getPackage(), providerEntry.getQuery(), certs), NO_OP_REQUEST_CALLBACK); return DEFAULT; FontRequest request = new FontRequest(providerEntry.getAuthority(), providerEntry.getPackage(), providerEntry.getQuery(), certs); Typeface typeface = sFontsContract.getFontOrWarmUpCache(request); return typeface == null ? DEFAULT : typeface; } Typeface typeface = findFromCache(mgr, path); if (typeface != null) return typeface; // family is FontFamilyFilesResourceEntry final FontFamilyFilesResourceEntry filesEntry = (FontFamilyFilesResourceEntry) entry; Loading Loading @@ -291,213 +285,9 @@ public class Typeface { synchronized (sLock) { if (sFontsContract == null) { sFontsContract = new FontsContract(context); sHandler = new Handler(); } } } /** * Create a typeface object given a font request. The font will be asynchronously fetched, * therefore the result is delivered to the given callback. See {@link FontRequest}. * Only one of the methods in callback will be invoked, depending on whether the request * succeeds or fails. These calls will happen on the main thread. * @param request A {@link FontRequest} object that identifies the provider and query for the * request. May not be null. * @param callback A callback that will be triggered when results are obtained. May not be null. */ @Deprecated 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) { sHandler.post(() -> callback.onTypefaceRetrieved(cachedTypeface)); return; } synchronized (sLock) { if (sFontsContract == null) { throw new RuntimeException("Context not initialized, can't query provider"); } final ResultReceiver receiver = new ResultReceiver(null) { @Override public void onReceiveResult(int resultCode, Bundle resultData) { sHandler.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.Columns.RESULT_CODE_OK) { callback.onTypefaceRequestFailed(resultCode); return; } if (resultData == null) { callback.onTypefaceRequestFailed( FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND); return; } List<FontResult> resultList = resultData.getParcelableArrayList(FontsContract.PARCEL_FONT_RESULTS); if (resultList == null || resultList.isEmpty()) { callback.onTypefaceRequestFailed( FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND); return; } FontFamily fontFamily = new FontFamily(); for (int i = 0; i < resultList.size(); ++i) { FontResult result = resultList.get(i); ParcelFileDescriptor fd = result.getFileDescriptor(); if (fd == null) { callback.onTypefaceRequestFailed( FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR); return; } try (FileInputStream is = new FileInputStream(fd.getFileDescriptor())) { FileChannel fileChannel = is.getChannel(); long fontSize = fileChannel.size(); ByteBuffer fontBuffer = fileChannel.map( FileChannel.MapMode.READ_ONLY, 0, fontSize); int weight = result.getWeight(); int italic = result.getItalic() ? STYLE_ITALIC : STYLE_NORMAL; FontVariationAxis[] axes = null; try { axes = FontVariationAxis.fromFontVariationSettings( result.getFontVariationSettings()); } catch (FontVariationAxis.InvalidFormatException e) { // TODO: Nice to pass FontVariationAxis[] directly instead of string. } if (!fontFamily.addFontFromBuffer(fontBuffer, result.getTtcIndex(), axes, weight, italic)) { Log.e(TAG, "Error creating font " + request.getQuery()); callback.onTypefaceRequestFailed( FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR); return; } } catch (IOException e) { Log.e(TAG, "Error reading font " + request.getQuery(), e); callback.onTypefaceRequestFailed( FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR); return; } finally { IoUtils.closeQuietly(fd); } } if (!fontFamily.freeze()) { callback.onTypefaceRequestFailed( FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR); return; } Typeface typeface = Typeface.createFromFamiliesWithDefault( new FontFamily[] { fontFamily }, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE); synchronized (sDynamicTypefaceCache) { String key = createProviderUid(request.getProviderAuthority(), request.getQuery()); sDynamicTypefaceCache.put(key, typeface); } callback.onTypefaceRetrieved(typeface); } /** * Interface used to receive asynchronously fetched typefaces. */ @Deprecated public interface FontRequestCallback { /** * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given * provider was not found on the device. */ int FAIL_REASON_PROVIDER_NOT_FOUND = FontsContract.RESULT_CODE_PROVIDER_NOT_FOUND; /** * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given * provider must be authenticated and the given certificates do not match its signature. */ int FAIL_REASON_WRONG_CERTIFICATES = FontsContract.RESULT_CODE_WRONG_CERTIFICATES; /** * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font * returned by the provider was not loaded properly. */ int FAIL_REASON_FONT_LOAD_ERROR = -3; /** * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font * provider did not return any results for the given query. */ int FAIL_REASON_FONT_NOT_FOUND = FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND; /** * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font * provider found the queried font, but it is currently unavailable. */ int FAIL_REASON_FONT_UNAVAILABLE = FontsContract.Columns.RESULT_CODE_FONT_UNAVAILABLE; /** * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given * query was not supported by the provider. */ int FAIL_REASON_MALFORMED_QUERY = FontsContract.Columns.RESULT_CODE_MALFORMED_QUERY; /** @hide */ @IntDef({ FAIL_REASON_PROVIDER_NOT_FOUND, FAIL_REASON_FONT_LOAD_ERROR, FAIL_REASON_FONT_NOT_FOUND, FAIL_REASON_FONT_UNAVAILABLE, FAIL_REASON_MALFORMED_QUERY }) @Retention(RetentionPolicy.SOURCE) @interface FontRequestFailReason {} /** * Called then a Typeface request done via {@link Typeface#create(FontRequest, * FontRequestCallback)} is complete. Note that this method will not be called if * {@link #onTypefaceRequestFailed(int)} is called instead. * @param typeface The Typeface object retrieved. */ void onTypefaceRetrieved(Typeface typeface); /** * Called when a Typeface request done via {@link Typeface#create(FontRequest, * FontRequestCallback)} fails. * @param reason May be one of {@link #FAIL_REASON_PROVIDER_NOT_FOUND}, * {@link #FAIL_REASON_FONT_NOT_FOUND}, * {@link #FAIL_REASON_FONT_LOAD_ERROR}, * {@link #FAIL_REASON_FONT_UNAVAILABLE} or * {@link #FAIL_REASON_MALFORMED_QUERY} if returned by the system. May also be * a positive value greater than 0 defined by the font provider as an * additional error code. Refer to the provider's documentation for more * information on possible returned error codes. */ 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. } }; /** * A builder class for creating new Typeface instance. Loading