From 0b73a4287e8f630c45bf7ef0a7e0b2541d6b4067 Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Tue, 11 Apr 2017 15:59:58 -0700 Subject: [PATCH] Introduce FontsContract.requestFont and deprecate Typeface.create Since background fetch should be synchronized with life cycle of Activities, so should not create new background thread inside this method. Not to break existing apps, mark as deprecated and will be removed from public API untile next release. New API accepts Handler so that the developer can pass their own thread . Test: TBD Bug: 37253785 Change-Id: I87fdc6a354a829db679253824f42489f25698fa0 --- api/current.txt | 17 ++- api/system-current.txt | 17 ++- api/test-current.txt | 17 ++- core/java/android/provider/FontsContract.java | 144 ++++++++++++++++++ graphics/java/android/graphics/Typeface.java | 2 + 5 files changed, 191 insertions(+), 6 deletions(-) diff --git a/api/current.txt b/api/current.txt index 0873990a3fbf1..0db689c8f19f0 100644 --- a/api/current.txt +++ b/api/current.txt @@ -13761,7 +13761,7 @@ package android.graphics { } public class Typeface { - method public static void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback); + 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); @@ -13795,7 +13795,7 @@ package android.graphics { method public android.graphics.Typeface.Builder setWeight(int); } - public static abstract interface Typeface.FontRequestCallback { + 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 @@ -34504,6 +34504,7 @@ package android.provider { method public static android.graphics.Typeface buildTypeface(android.content.Context, android.os.CancellationSignal, android.provider.FontsContract.FontInfo[], int, boolean, java.lang.String); method public static android.graphics.Typeface buildTypeface(android.content.Context, android.os.CancellationSignal, android.provider.FontsContract.FontInfo[]); method public static android.provider.FontsContract.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal, android.graphics.fonts.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException; + method public static void requestFont(android.content.Context, android.graphics.fonts.FontRequest, android.provider.FontsContract.FontRequestCallback, android.os.Handler); } public static final class FontsContract.Columns implements android.provider.BaseColumns { @@ -34537,6 +34538,18 @@ package android.provider { method public boolean isItalic(); } + public static class FontsContract.FontRequestCallback { + ctor public FontsContract.FontRequestCallback(); + method public void onTypefaceRequestFailed(int); + method public 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 final deprecated class LiveFolders implements android.provider.BaseColumns { field public static final java.lang.String ACTION_CREATE_LIVE_FOLDER = "android.intent.action.CREATE_LIVE_FOLDER"; field public static final java.lang.String DESCRIPTION = "description"; diff --git a/api/system-current.txt b/api/system-current.txt index 5cd3b0a25a37f..30c08a721d68a 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -14528,7 +14528,7 @@ package android.graphics { } public class Typeface { - method public static void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback); + 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); @@ -14562,7 +14562,7 @@ package android.graphics { method public android.graphics.Typeface.Builder setWeight(int); } - public static abstract interface Typeface.FontRequestCallback { + 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 @@ -37481,6 +37481,7 @@ package android.provider { method public static android.graphics.Typeface buildTypeface(android.content.Context, android.os.CancellationSignal, android.provider.FontsContract.FontInfo[], int, boolean, java.lang.String); method public static android.graphics.Typeface buildTypeface(android.content.Context, android.os.CancellationSignal, android.provider.FontsContract.FontInfo[]); method public static android.provider.FontsContract.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal, android.graphics.fonts.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException; + method public static void requestFont(android.content.Context, android.graphics.fonts.FontRequest, android.provider.FontsContract.FontRequestCallback, android.os.Handler); } public static final class FontsContract.Columns implements android.provider.BaseColumns { @@ -37514,6 +37515,18 @@ package android.provider { method public boolean isItalic(); } + public static class FontsContract.FontRequestCallback { + ctor public FontsContract.FontRequestCallback(); + method public void onTypefaceRequestFailed(int); + method public 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 final deprecated class LiveFolders implements android.provider.BaseColumns { field public static final java.lang.String ACTION_CREATE_LIVE_FOLDER = "android.intent.action.CREATE_LIVE_FOLDER"; field public static final java.lang.String DESCRIPTION = "description"; diff --git a/api/test-current.txt b/api/test-current.txt index c65d8dadf095b..b21aa5ab49528 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -13812,7 +13812,7 @@ package android.graphics { } public class Typeface { - method public static void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback); + 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); @@ -13846,7 +13846,7 @@ package android.graphics { method public android.graphics.Typeface.Builder setWeight(int); } - public static abstract interface Typeface.FontRequestCallback { + 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 @@ -34647,6 +34647,7 @@ package android.provider { method public static android.graphics.Typeface buildTypeface(android.content.Context, android.os.CancellationSignal, android.provider.FontsContract.FontInfo[], int, boolean, java.lang.String); method public static android.graphics.Typeface buildTypeface(android.content.Context, android.os.CancellationSignal, android.provider.FontsContract.FontInfo[]); method public static android.provider.FontsContract.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal, android.graphics.fonts.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException; + method public static void requestFont(android.content.Context, android.graphics.fonts.FontRequest, android.provider.FontsContract.FontRequestCallback, android.os.Handler); } public static final class FontsContract.Columns implements android.provider.BaseColumns { @@ -34680,6 +34681,18 @@ package android.provider { method public boolean isItalic(); } + public static class FontsContract.FontRequestCallback { + ctor public FontsContract.FontRequestCallback(); + method public void onTypefaceRequestFailed(int); + method public 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 final deprecated class LiveFolders implements android.provider.BaseColumns { field public static final java.lang.String ACTION_CREATE_LIVE_FOLDER = "android.intent.action.CREATE_LIVE_FOLDER"; field public static final java.lang.String DESCRIPTION = "description"; diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java index f9508902d49f4..7e56ff988158e 100644 --- a/core/java/android/provider/FontsContract.java +++ b/core/java/android/provider/FontsContract.java @@ -393,6 +393,150 @@ public class FontsContract { } } + /** + * Interface used to receive asynchronously fetched typefaces. + */ + public static class FontRequestCallback { + /** + * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given + * provider was not found on the device. + */ + public static final int FAIL_REASON_PROVIDER_NOT_FOUND = 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. + */ + public static final int FAIL_REASON_WRONG_CERTIFICATES = RESULT_CODE_WRONG_CERTIFICATES; + /** + * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font + * returned by the provider was not loaded properly. + */ + public static final 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. + */ + public static final int FAIL_REASON_FONT_NOT_FOUND = 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. + */ + public static final int FAIL_REASON_FONT_UNAVAILABLE = Columns.RESULT_CODE_FONT_UNAVAILABLE; + /** + * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given + * query was not supported by the provider. + */ + public static final int FAIL_REASON_MALFORMED_QUERY = 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 {} + + 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. + * @param typeface The Typeface object retrieved. + */ + public void onTypefaceRetrieved(Typeface typeface) {} + + /** + * Called when a Typeface request done via {@link Typeface#create(FontRequest, + * FontRequestCallback)} fails. + * @param reason 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}. + */ + public void onTypefaceRequestFailed(@FontRequestFailReason int reason) {} + } + + /** + * 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 caller thread. + * @param context A context to be used for fetching from font provider. + * @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. + * @param handler A handler to be processed the font fetching. + */ + public static void requestFont(@NonNull Context context, @NonNull FontRequest request, + @NonNull FontRequestCallback callback, @NonNull Handler handler) { + + final Handler callerThreadHandler = new Handler(); + handler.post(() -> { + // TODO: Cache the result. + FontFamilyResult result; + try { + result = fetchFonts(context, null /* cancellation signal */, request); + } catch (NameNotFoundException e) { + callerThreadHandler.post(() -> callback.onTypefaceRequestFailed( + FontRequestCallback.FAIL_REASON_PROVIDER_NOT_FOUND)); + return; + } + + if (result.getStatusCode() != FontFamilyResult.STATUS_OK) { + switch (result.getStatusCode()) { + case FontFamilyResult.STATUS_WRONG_CERTIFICATES: + callerThreadHandler.post(() -> callback.onTypefaceRequestFailed( + FontRequestCallback.FAIL_REASON_WRONG_CERTIFICATES)); + return; + case FontFamilyResult.STATUS_UNEXPECTED_DATA_PROVIDED: + callerThreadHandler.post(() -> callback.onTypefaceRequestFailed( + FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR)); + return; + default: + // fetchFont returns unexpected status type. Fallback to load error. + callerThreadHandler.post(() -> callback.onTypefaceRequestFailed( + FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR)); + return; + } + } + + final FontInfo[] fonts = result.getFonts(); + if (fonts == null || fonts.length == 0) { + callerThreadHandler.post(() -> callback.onTypefaceRequestFailed( + FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND)); + return; + } + for (final FontInfo font : fonts) { + if (font.getResultCode() != Columns.RESULT_CODE_OK) { + // We proceed if all font entry is ready to use. Otherwise report the first + // error. + final int resultCode = font.getResultCode(); + if (resultCode < 0) { + // Negative values are reserved for internal errors. Fallback to load error. + callerThreadHandler.post(() -> callback.onTypefaceRequestFailed( + FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR)); + } else { + callerThreadHandler.post(() -> callback.onTypefaceRequestFailed( + resultCode)); + } + return; + } + } + + final Typeface typeface = buildTypeface(context, null /* cancellation signal */, fonts); + if (typeface == null) { + // Something went wrong during reading font files. This happens if the given font + // file is an unsupported font type. + callerThreadHandler.post(() -> callback.onTypefaceRequestFailed( + FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR)); + return; + } + + callerThreadHandler.post(() -> callback.onTypefaceRetrieved(typeface)); + }); + } + /** * Fetch fonts given a font request. * diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 21533f8ce6c91..c3588dc3c10d6 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -292,6 +292,7 @@ public class Typeface { * 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 @@ -404,6 +405,7 @@ public class Typeface { /** * Interface used to receive asynchronously fetched typefaces. */ + @Deprecated public interface FontRequestCallback { /** * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given