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:
@ -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