Introduce Fonts Content Provider
This change exposes to developers the ability to request fonts from a provider via Typeface. Until further security is implemented, only system apps can provide fonts. Test: See topic for CTS change Change-Id: Ic7d5e2648340ee561f4d4c2d73a673748d2af076
This commit is contained in:
@@ -13226,6 +13226,7 @@ package android.graphics {
|
||||
}
|
||||
|
||||
public class Typeface {
|
||||
method public static 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);
|
||||
@@ -13246,6 +13247,14 @@ package android.graphics {
|
||||
field public static final android.graphics.Typeface SERIF;
|
||||
}
|
||||
|
||||
public static abstract 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 = 1; // 0x1
|
||||
field public static final int FAIL_REASON_FONT_NOT_FOUND = 2; // 0x2
|
||||
field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = 0; // 0x0
|
||||
}
|
||||
|
||||
public class Xfermode {
|
||||
ctor public Xfermode();
|
||||
}
|
||||
@@ -13800,6 +13809,19 @@ package android.graphics.drawable.shapes {
|
||||
|
||||
}
|
||||
|
||||
package android.graphics.fonts {
|
||||
|
||||
public final class FontRequest implements android.os.Parcelable {
|
||||
ctor public FontRequest(java.lang.String, java.lang.String);
|
||||
method public int describeContents();
|
||||
method public java.lang.String getProviderAuthority();
|
||||
method public java.lang.String getQuery();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontRequest> CREATOR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.graphics.pdf {
|
||||
|
||||
public class PdfDocument {
|
||||
@@ -32951,6 +32973,16 @@ package android.provider {
|
||||
method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
|
||||
}
|
||||
|
||||
public class FontsContract {
|
||||
}
|
||||
|
||||
public static final class FontsContract.Columns implements android.provider.BaseColumns {
|
||||
ctor public FontsContract.Columns();
|
||||
field public static final java.lang.String STYLE = "font_style";
|
||||
field public static final java.lang.String TTC_INDEX = "font_ttc_index";
|
||||
field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings";
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
@@ -13771,6 +13771,7 @@ package android.graphics {
|
||||
}
|
||||
|
||||
public class Typeface {
|
||||
method public static 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);
|
||||
@@ -13791,6 +13792,14 @@ package android.graphics {
|
||||
field public static final android.graphics.Typeface SERIF;
|
||||
}
|
||||
|
||||
public static abstract 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 = 1; // 0x1
|
||||
field public static final int FAIL_REASON_FONT_NOT_FOUND = 2; // 0x2
|
||||
field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = 0; // 0x0
|
||||
}
|
||||
|
||||
public class Xfermode {
|
||||
ctor public Xfermode();
|
||||
}
|
||||
@@ -14345,6 +14354,19 @@ package android.graphics.drawable.shapes {
|
||||
|
||||
}
|
||||
|
||||
package android.graphics.fonts {
|
||||
|
||||
public final class FontRequest implements android.os.Parcelable {
|
||||
ctor public FontRequest(java.lang.String, java.lang.String);
|
||||
method public int describeContents();
|
||||
method public java.lang.String getProviderAuthority();
|
||||
method public java.lang.String getQuery();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontRequest> CREATOR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.graphics.pdf {
|
||||
|
||||
public class PdfDocument {
|
||||
@@ -35774,6 +35796,16 @@ package android.provider {
|
||||
method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
|
||||
}
|
||||
|
||||
public class FontsContract {
|
||||
}
|
||||
|
||||
public static final class FontsContract.Columns implements android.provider.BaseColumns {
|
||||
ctor public FontsContract.Columns();
|
||||
field public static final java.lang.String STYLE = "font_style";
|
||||
field public static final java.lang.String TTC_INDEX = "font_ttc_index";
|
||||
field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings";
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
@@ -13258,6 +13258,7 @@ package android.graphics {
|
||||
}
|
||||
|
||||
public class Typeface {
|
||||
method public static 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);
|
||||
@@ -13278,6 +13279,14 @@ package android.graphics {
|
||||
field public static final android.graphics.Typeface SERIF;
|
||||
}
|
||||
|
||||
public static abstract 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 = 1; // 0x1
|
||||
field public static final int FAIL_REASON_FONT_NOT_FOUND = 2; // 0x2
|
||||
field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = 0; // 0x0
|
||||
}
|
||||
|
||||
public class Xfermode {
|
||||
ctor public Xfermode();
|
||||
}
|
||||
@@ -13832,6 +13841,19 @@ package android.graphics.drawable.shapes {
|
||||
|
||||
}
|
||||
|
||||
package android.graphics.fonts {
|
||||
|
||||
public final class FontRequest implements android.os.Parcelable {
|
||||
ctor public FontRequest(java.lang.String, java.lang.String);
|
||||
method public int describeContents();
|
||||
method public java.lang.String getProviderAuthority();
|
||||
method public java.lang.String getQuery();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontRequest> CREATOR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.graphics.pdf {
|
||||
|
||||
public class PdfDocument {
|
||||
@@ -33067,6 +33089,16 @@ package android.provider {
|
||||
method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
|
||||
}
|
||||
|
||||
public class FontsContract {
|
||||
}
|
||||
|
||||
public static final class FontsContract.Columns implements android.provider.BaseColumns {
|
||||
ctor public FontsContract.Columns();
|
||||
field public static final java.lang.String STYLE = "font_style";
|
||||
field public static final java.lang.String TTC_INDEX = "font_ttc_index";
|
||||
field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings";
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
197
core/java/android/provider/FontsContract.java
Normal file
197
core/java/android/provider/FontsContract.java
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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.provider;
|
||||
|
||||
import android.app.ActivityThread;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.fonts.FontRequest;
|
||||
import android.graphics.fonts.FontResult;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.Process;
|
||||
import android.os.ResultReceiver;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Utility class to deal with Font ContentProviders.
|
||||
*/
|
||||
public class FontsContract {
|
||||
private static final String TAG = "FontsContract";
|
||||
|
||||
/**
|
||||
* Defines the constants used in a response from a Font Provider. The cursor returned from the
|
||||
* query should have the ID column populated with the content uri ID for the resulting font.
|
||||
* This should point to a real file or shared memory, as the client will mmap the given file
|
||||
* descriptor. Pipes, sockets and other non-mmap-able file descriptors will fail to load in the
|
||||
* client application.
|
||||
*/
|
||||
public static final class Columns implements BaseColumns {
|
||||
/**
|
||||
* Constant used to request data from a font provider. The cursor returned from the query
|
||||
* should have this column populated with an int for the ttc index for the resulting font.
|
||||
*/
|
||||
public static final String TTC_INDEX = "font_ttc_index";
|
||||
/**
|
||||
* Constant used to request data from a font provider. The cursor returned from the query
|
||||
* may populate this column with the font variation settings String information for the
|
||||
* font.
|
||||
*/
|
||||
public static final String VARIATION_SETTINGS = "font_variation_settings";
|
||||
/**
|
||||
* Constant used to request data from a font provider. The cursor returned from the query
|
||||
* should have this column populated with the int style for the resulting font. This should
|
||||
* be one of {@link android.graphics.Typeface#NORMAL},
|
||||
* {@link android.graphics.Typeface#BOLD}, {@link android.graphics.Typeface#ITALIC} or
|
||||
* {@link android.graphics.Typeface#BOLD_ITALIC}
|
||||
*/
|
||||
public static final String STYLE = "font_style";
|
||||
}
|
||||
|
||||
/**
|
||||
* Constant used to identify the List of {@link ParcelFileDescriptor} item in the Bundle
|
||||
* returned to the ResultReceiver in getFont.
|
||||
* @hide
|
||||
*/
|
||||
public static final String PARCEL_FONT_RESULTS = "font_results";
|
||||
|
||||
/** @hide */
|
||||
public static final int RESULT_CODE_OK = 0;
|
||||
/** @hide */
|
||||
public static final int RESULT_CODE_FONT_NOT_FOUND = 1;
|
||||
/** @hide */
|
||||
public static final int RESULT_CODE_PROVIDER_NOT_FOUND = 2;
|
||||
|
||||
private static final int THREAD_RENEWAL_THRESHOLD_MS = 10000;
|
||||
|
||||
private final Context mContext;
|
||||
private final PackageManager mPackageManager;
|
||||
private final Object mLock = new Object();
|
||||
@GuardedBy("mLock")
|
||||
private Handler mHandler;
|
||||
@GuardedBy("mLock")
|
||||
private HandlerThread mThread;
|
||||
|
||||
/** @hide */
|
||||
public FontsContract() {
|
||||
// TODO: investigate if the system context is the best option here. ApplicationContext or
|
||||
// the one passed by developer?
|
||||
// TODO: Looks like ActivityThread.currentActivityThread() can return null. Check when it
|
||||
// returns null and check if we need to handle null case.
|
||||
mContext = ActivityThread.currentActivityThread().getSystemContext();
|
||||
mPackageManager = mContext.getPackageManager();
|
||||
}
|
||||
|
||||
// We use a background thread to post the content resolving work for all requests on. This
|
||||
// thread should be quit/stopped after all requests are done.
|
||||
private final Runnable mReplaceDispatcherThreadRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (mLock) {
|
||||
if (mThread != null) {
|
||||
mThread.quitSafely();
|
||||
mThread = null;
|
||||
mHandler = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void getFont(FontRequest request, ResultReceiver receiver) {
|
||||
synchronized (mLock) {
|
||||
if (mHandler == null) {
|
||||
mThread = new HandlerThread("fonts", Process.THREAD_PRIORITY_BACKGROUND);
|
||||
mThread.start();
|
||||
mHandler = new Handler(mThread.getLooper());
|
||||
}
|
||||
mHandler.post(() -> {
|
||||
String providerAuthority = request.getProviderAuthority();
|
||||
// TODO: Implement cert checking for non-system apps
|
||||
ProviderInfo providerInfo = mPackageManager.resolveContentProvider(
|
||||
providerAuthority, PackageManager.MATCH_SYSTEM_ONLY);
|
||||
if (providerInfo == null) {
|
||||
receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null);
|
||||
return;
|
||||
}
|
||||
Bundle result = getFontFromProvider(request, receiver, providerInfo);
|
||||
if (result == null) {
|
||||
receiver.send(RESULT_CODE_FONT_NOT_FOUND, null);
|
||||
return;
|
||||
}
|
||||
receiver.send(RESULT_CODE_OK, result);
|
||||
});
|
||||
mHandler.removeCallbacks(mReplaceDispatcherThreadRunnable);
|
||||
mHandler.postDelayed(mReplaceDispatcherThreadRunnable, THREAD_RENEWAL_THRESHOLD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
private Bundle getFontFromProvider(FontRequest request, ResultReceiver receiver,
|
||||
ProviderInfo providerInfo) {
|
||||
ArrayList<FontResult> result = null;
|
||||
Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(providerInfo.authority)
|
||||
.build();
|
||||
try (Cursor cursor = mContext.getContentResolver().query(uri, new String[] { Columns._ID,
|
||||
Columns.TTC_INDEX, Columns.VARIATION_SETTINGS, Columns.STYLE },
|
||||
"query = ?", new String[] { request.getQuery() }, null);) {
|
||||
// TODO: Should we restrict the amount of fonts that can be returned?
|
||||
// TODO: Write documentation explaining that all results should be from the same family.
|
||||
if (cursor != null && cursor.getCount() > 0) {
|
||||
result = new ArrayList<>();
|
||||
final int idColumnIndex = cursor.getColumnIndex(Columns._ID);
|
||||
final int ttcIndexColumnIndex = cursor.getColumnIndex(Columns.TTC_INDEX);
|
||||
final int vsColumnIndex = cursor.getColumnIndex(Columns.VARIATION_SETTINGS);
|
||||
final int styleColumnIndex = cursor.getColumnIndex(Columns.STYLE);
|
||||
while (cursor.moveToNext()) {
|
||||
long id = cursor.getLong(idColumnIndex);
|
||||
Uri fileUri = ContentUris.withAppendedId(uri, id);
|
||||
try {
|
||||
ParcelFileDescriptor pfd =
|
||||
mContext.getContentResolver().openFileDescriptor(fileUri, "r");
|
||||
final int ttcIndex = cursor.getInt(ttcIndexColumnIndex);
|
||||
final String variationSettings = cursor.getString(vsColumnIndex);
|
||||
final int style = cursor.getInt(styleColumnIndex);
|
||||
result.add(new FontResult(pfd, ttcIndex, variationSettings, style));
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e(TAG, "FileNotFoundException raised when interacting with content "
|
||||
+ "provider " + providerInfo.authority, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result != null && !result.isEmpty()) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelableArrayList(PARCEL_FONT_RESULTS, result);
|
||||
return bundle;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -16,20 +16,34 @@
|
||||
|
||||
package android.graphics;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.content.res.AssetManager;
|
||||
import android.graphics.fonts.FontRequest;
|
||||
import android.graphics.fonts.FontResult;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.ResultReceiver;
|
||||
import android.provider.FontsContract;
|
||||
import android.text.FontConfig;
|
||||
import android.util.Log;
|
||||
import android.util.LongSparseArray;
|
||||
import android.util.LruCache;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.ArrayList;
|
||||
@@ -64,7 +78,11 @@ public class Typeface {
|
||||
|
||||
static Typeface[] sDefaults;
|
||||
private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache =
|
||||
new LongSparseArray<SparseArray<Typeface>>(3);
|
||||
new LongSparseArray<>(3);
|
||||
@GuardedBy("sLock")
|
||||
private static FontsContract sFontsContract;
|
||||
@GuardedBy("sLock")
|
||||
private static Handler mHandler;
|
||||
|
||||
/**
|
||||
* Cache for Typeface objects dynamically loaded from assets. Currently max size is 16.
|
||||
@@ -74,6 +92,7 @@ public class Typeface {
|
||||
static Typeface sDefaultTypeface;
|
||||
static Map<String, Typeface> sSystemFontMap;
|
||||
static FontFamily[] sFallbackFonts;
|
||||
private static final Object sLock = new Object();
|
||||
|
||||
static final String FONTS_CONFIG = "fonts.xml";
|
||||
|
||||
@@ -124,7 +143,7 @@ public class Typeface {
|
||||
|
||||
FontFamily fontFamily = new FontFamily();
|
||||
if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */)) {
|
||||
FontFamily[] families = { fontFamily };
|
||||
FontFamily[] families = {fontFamily};
|
||||
typeface = createFromFamiliesWithDefault(families);
|
||||
sDynamicTypefaceCache.put(key, typeface);
|
||||
return typeface;
|
||||
@@ -134,6 +153,138 @@ public class Typeface {
|
||||
throw new RuntimeException("Font resource not found " + path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static void create(@NonNull FontRequest request, @NonNull FontRequestCallback callback) {
|
||||
synchronized (sLock) {
|
||||
if (sFontsContract == null) {
|
||||
sFontsContract = new FontsContract();
|
||||
mHandler = new Handler();
|
||||
}
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
sFontsContract.getFont(request, receiver);
|
||||
}
|
||||
}
|
||||
|
||||
private static void receiveResult(FontRequest request, FontRequestCallback callback,
|
||||
int resultCode, Bundle resultData) {
|
||||
if (resultCode == FontsContract.RESULT_CODE_PROVIDER_NOT_FOUND) {
|
||||
callback.onTypefaceRequestFailed(
|
||||
FontRequestCallback.FAIL_REASON_PROVIDER_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
if (resultCode == FontsContract.RESULT_CODE_FONT_NOT_FOUND
|
||||
|| 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 style = result.getStyle();
|
||||
int weight = (style & BOLD) != 0 ? 700 : 400;
|
||||
// TODO: this method should be
|
||||
// create(fd, ttcIndex, fontVariationSettings, style).
|
||||
if (!fontFamily.addFontWeightStyle(fontBuffer, result.getTtcIndex(),
|
||||
null, weight, (style & ITALIC) != 0)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
callback.onTypefaceRetrieved(Typeface.createFromFamiliesWithDefault(
|
||||
new FontFamily[] {fontFamily}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface used to receive asynchronously fetched typefaces.
|
||||
*/
|
||||
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 = 0;
|
||||
/**
|
||||
* Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font
|
||||
* returned by the provider was not loaded properly.
|
||||
*/
|
||||
int FAIL_REASON_FONT_LOAD_ERROR = 1;
|
||||
/**
|
||||
* Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
|
||||
* provider did not return any results for the given query.
|
||||
*/
|
||||
int FAIL_REASON_FONT_NOT_FOUND = 2;
|
||||
|
||||
/** @hide */
|
||||
@IntDef({FAIL_REASON_PROVIDER_NOT_FOUND, FAIL_REASON_FONT_LOAD_ERROR,
|
||||
FAIL_REASON_FONT_NOT_FOUND})
|
||||
@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 One of {@link #FAIL_REASON_PROVIDER_NOT_FOUND},
|
||||
* {@link #FAIL_REASON_FONT_NOT_FOUND} or
|
||||
* {@link #FAIL_REASON_FONT_LOAD_ERROR}.
|
||||
*/
|
||||
void onTypefaceRequestFailed(@FontRequestFailReason int reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
93
graphics/java/android/graphics/fonts/FontRequest.java
Normal file
93
graphics/java/android/graphics/fonts/FontRequest.java
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.fonts;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
/**
|
||||
* Information about a font request that may be sent to a Font Provider.
|
||||
*/
|
||||
public final class FontRequest implements Parcelable {
|
||||
private final String mProviderAuthority;
|
||||
private final String mQuery;
|
||||
|
||||
/**
|
||||
* @param providerAuthority The authority of the Font Provider to be used for the request.
|
||||
* @param query The query to be sent over to the provider. Refer to your font provider's
|
||||
* documentation on the format of this string.
|
||||
*/
|
||||
public FontRequest(@NonNull String providerAuthority, @NonNull String query) {
|
||||
mProviderAuthority = Preconditions.checkNotNull(providerAuthority);
|
||||
mQuery = Preconditions.checkNotNull(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected font provider's authority. This tells the system what font provider
|
||||
* it should request the font from.
|
||||
*/
|
||||
public String getProviderAuthority() {
|
||||
return mProviderAuthority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the query string. Refer to your font provider's documentation on the format of this
|
||||
* string.
|
||||
*/
|
||||
public String getQuery() {
|
||||
return mQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(mProviderAuthority);
|
||||
dest.writeString(mQuery);
|
||||
}
|
||||
|
||||
private FontRequest(Parcel in) {
|
||||
mProviderAuthority = in.readString();
|
||||
mQuery = in.readString();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<FontRequest> CREATOR =
|
||||
new Parcelable.Creator<FontRequest>() {
|
||||
@Override
|
||||
public FontRequest createFromParcel(Parcel in) {
|
||||
return new FontRequest(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FontRequest[] newArray(int size) {
|
||||
return new FontRequest[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FontRequest {"
|
||||
+ "mProviderAuthority: " + mProviderAuthority
|
||||
+ ", mQuery: " + mQuery
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
108
graphics/java/android/graphics/fonts/FontResult.java
Normal file
108
graphics/java/android/graphics/fonts/FontResult.java
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.fonts;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.Paint;
|
||||
import android.os.Parcel;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Results returned from a Font Provider to the system.
|
||||
* @hide
|
||||
*/
|
||||
public final class FontResult implements Parcelable {
|
||||
private final ParcelFileDescriptor mFileDescriptor;
|
||||
private final int mTtcIndex;
|
||||
private final String mFontVariationSettings;
|
||||
private final int mStyle;
|
||||
|
||||
/**
|
||||
* Creates a FontResult with all the information needed about a provided font.
|
||||
* @param fileDescriptor A ParcelFileDescriptor pointing to the font file. This shoult point to
|
||||
* a real file or shared memory, as the client will mmap the given file
|
||||
* descriptor. Pipes, sockets and other non-mmap-able file descriptors
|
||||
* will fail to load in the client application.
|
||||
* @param ttcIndex If providing a TTC_INDEX file, the index to point to. Otherwise, 0.
|
||||
* @param fontVariationSettings If providing a variation font, the settings for it. May be null.
|
||||
* @param style One of {@link android.graphics.Typeface#NORMAL},
|
||||
* {@link android.graphics.Typeface#BOLD}, {@link android.graphics.Typeface#ITALIC}
|
||||
* or {@link android.graphics.Typeface#BOLD_ITALIC}
|
||||
*/
|
||||
public FontResult(@NonNull ParcelFileDescriptor fileDescriptor, int ttcIndex,
|
||||
@Nullable String fontVariationSettings, int style) {
|
||||
mFileDescriptor = Preconditions.checkNotNull(fileDescriptor);
|
||||
mTtcIndex = ttcIndex;
|
||||
mFontVariationSettings = fontVariationSettings;
|
||||
mStyle = style;
|
||||
}
|
||||
|
||||
public ParcelFileDescriptor getFileDescriptor() {
|
||||
return mFileDescriptor;
|
||||
}
|
||||
|
||||
public int getTtcIndex() {
|
||||
return mTtcIndex;
|
||||
}
|
||||
|
||||
public String getFontVariationSettings() {
|
||||
return mFontVariationSettings;
|
||||
}
|
||||
|
||||
public int getStyle() {
|
||||
return mStyle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeParcelable(mFileDescriptor, flags);
|
||||
dest.writeInt(mTtcIndex);
|
||||
dest.writeString(mFontVariationSettings);
|
||||
dest.writeInt(mStyle);
|
||||
}
|
||||
|
||||
private FontResult(Parcel in) {
|
||||
mFileDescriptor = in.readParcelable(null);
|
||||
mTtcIndex = in.readInt();
|
||||
mFontVariationSettings = in.readString();
|
||||
mStyle = in.readInt();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<FontResult> CREATOR =
|
||||
new Parcelable.Creator<FontResult>() {
|
||||
@Override
|
||||
public FontResult createFromParcel(Parcel in) {
|
||||
return new FontResult(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FontResult[] newArray(int size) {
|
||||
return new FontResult[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
18
graphics/java/android/graphics/fonts/FontSpec.aidl
Normal file
18
graphics/java/android/graphics/fonts/FontSpec.aidl
Normal file
@@ -0,0 +1,18 @@
|
||||
/* Copyright 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.fonts;
|
||||
|
||||
parcelable FontSpec;
|
||||
Reference in New Issue
Block a user