diff options
Diffstat (limited to 'core/java')
5 files changed, 426 insertions, 8 deletions
diff --git a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java new file mode 100644 index 000000000000..25758e9d9a61 --- /dev/null +++ b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2021 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.SystemApi; + +import com.android.internal.util.Preconditions; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Request for updating or adding a font family on the system. + * + * <p>You can update or add a font family with custom style parameters. The following example + * defines a font family called "roboto" using "Roboto-Regular" font file that is already available + * on the system by preloading or {@link FontManager#updateFontFile}. + * <pre> + * FontManager fm = getContext().getSystemService(FontManager.class); + * fm.updateFontFamily(new FontFamilyUpdateRequest.Builder() + * .addFontFamily(new FontFamilyUpdateRequest.FontFamily("roboto", Arrays.asList( + * new FontFamilyUpdateRequest.Font( + * "Roboto-Regular", + * new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT), + * Collections.emptyList()), + * new FontFamilyUpdateRequest.Font( + * "Roboto-Regular", + * new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_ITALIC), + * Collections.emptyList())))) + * .build(), fm.getFontConfig().getConfigVersion()); + * </pre> + * + * <p>You can update or add font files in the same request by calling + * {@link FontFamilyUpdateRequest.Builder#addFontFileUpdateRequest(FontFileUpdateRequest)}. + * The following example adds "YourFont" font file and defines "your-font" font family in the same + * request. In this case, the font file represented by {@code yourFontFd} should be an OpenType + * compliant font file and have "YourFont" as PostScript name (ID=6) in 'name' table. + * <pre> + * FontManager fm = getContext().getSystemService(FontManager.class); + * fm.updateFontFamily(new FontFamilyUpdateRequest.Builder() + * .addFontFileUpdateRequest(new FontFileUpdateRequest(yourFontFd, signature)) + * .addFontFamily(new FontFamilyUpdateRequest.FontFamily("your-font", Arrays.asList( + * new FontFamilyUpdateRequest.Font( + * "YourFont", + * new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT), + * Collections.emptyList())))) + * .build(), fm.getFontConfig().getConfigVersion()); + * </pre> + * + * @hide + */ +@SystemApi +public final class FontFamilyUpdateRequest { + + /** + * A font family definition. + */ + public static final class FontFamily { + @NonNull + private final String mName; + @NonNull + private final List<Font> mFonts; + + /** + * Constructs a FontFamily. + * + * <p>A font family has a name to identify the font family. Apps can use + * {@link android.graphics.Typeface#create(String, int)} or XML resources to use a specific + * font family. + * + * <p>A font family consists of multiple fonts with different styles. The style information + * can be specified by {@link Font}. + * + * @see android.graphics.Typeface#create(String, int) + * @see Font + */ + public FontFamily(@NonNull String name, @NonNull List<Font> fonts) { + Objects.requireNonNull(name); + Preconditions.checkStringNotEmpty(name); + Objects.requireNonNull(fonts); + Preconditions.checkCollectionElementsNotNull(fonts, "fonts"); + Preconditions.checkCollectionNotEmpty(fonts, "fonts"); + mName = name; + mFonts = fonts; + } + + /** + * Returns the name of this family. + */ + @NonNull + public String getName() { + return mName; + } + + /** + * Returns the fonts in this family. + */ + @NonNull + public List<Font> getFonts() { + return mFonts; + } + } + + /** + * A single entry in a font family representing a font. + */ + public static final class Font { + + @NonNull + private final String mPostScriptName; + @NonNull + private final FontStyle mStyle; + @NonNull + private final List<FontVariationAxis> mAxes; + + /** + * Constructs a FontStyleVariation. + * + * <p>A font has a PostScript name to identify the font file to use, a {@link FontStyle} + * to specify the style, and a list of {@link FontVariationAxis} to specify axis tags and + * values for variable fonts. If the font file identified by {@code postScriptName} is not a + * variable font, {@code axes} must be empty. + * + * @param postScriptName The PostScript name of the font file to use. PostScript name is in + * Name ID 6 field in 'name' table, as specified by OpenType + * specification. + * @param style The style for this font. + * @param axes A list of {@link FontVariationAxis} to specify axis tags and values + * for variable fonts. + */ + public Font(@NonNull String postScriptName, @NonNull FontStyle style, + @NonNull List<FontVariationAxis> axes) { + Objects.requireNonNull(postScriptName); + Preconditions.checkStringNotEmpty(postScriptName); + Objects.requireNonNull(style); + Objects.requireNonNull(axes); + Preconditions.checkCollectionElementsNotNull(axes, "axes"); + mPostScriptName = postScriptName; + mStyle = style; + mAxes = axes; + } + + /** + * Returns PostScript name of the font file to use. + */ + @NonNull + public String getPostScriptName() { + return mPostScriptName; + } + + /** + * Returns the style. + */ + @NonNull + public FontStyle getStyle() { + return mStyle; + } + + /** + * Returns the list of {@link FontVariationAxis}. + */ + @NonNull + public List<FontVariationAxis> getAxes() { + return mAxes; + } + } + + /** + * Builds a {@link FontFamilyUpdateRequest}. + */ + public static final class Builder { + @NonNull + private final List<FontFileUpdateRequest> mFontFileUpdateRequests = new ArrayList<>(); + @NonNull + private final List<FontFamily> mFontFamilies = new ArrayList<>(); + + /** + * Constructs a FontFamilyUpdateRequest.Builder. + */ + public Builder() { + } + + /** + * Adds a {@link FontFileUpdateRequest} to execute as a part of the constructed + * {@link FontFamilyUpdateRequest}. + * + * @param request A font file update request. + * @return This builder object. + */ + @NonNull + public Builder addFontFileUpdateRequest(@NonNull FontFileUpdateRequest request) { + Objects.requireNonNull(request); + mFontFileUpdateRequests.add(request); + return this; + } + + /** + * Adds a font family to update an existing font family in the system font config or + * add as a new font family to the system font config. + * + * @param fontFamily An font family definition to add or update. + * @return This builder object. + */ + @NonNull + public Builder addFontFamily(@NonNull FontFamily fontFamily) { + Objects.requireNonNull(fontFamily); + mFontFamilies.add(fontFamily); + return this; + } + + /** + * Builds a {@link FontFamilyUpdateRequest}. + */ + @NonNull + public FontFamilyUpdateRequest build() { + return new FontFamilyUpdateRequest(mFontFileUpdateRequests, mFontFamilies); + } + } + + @NonNull + private final List<FontFileUpdateRequest> mFontFiles; + + @NonNull + private final List<FontFamily> mFontFamilies; + + private FontFamilyUpdateRequest(@NonNull List<FontFileUpdateRequest> fontFiles, + @NonNull List<FontFamily> fontFamilies) { + mFontFiles = fontFiles; + mFontFamilies = fontFamilies; + } + + /** + * Returns the list of {@link FontFileUpdateRequest} that will be executed as a part of this + * request. + */ + @NonNull + public List<FontFileUpdateRequest> getFontFileUpdateRequests() { + return mFontFiles; + } + + /** + * Returns the list of {@link FontFamily} that will be updated in this request. + */ + @NonNull + public List<FontFamily> getFontFamilies() { + return mFontFamilies; + } +} diff --git a/core/java/android/graphics/fonts/FontFileUpdateRequest.java b/core/java/android/graphics/fonts/FontFileUpdateRequest.java new file mode 100644 index 000000000000..cf1dca965216 --- /dev/null +++ b/core/java/android/graphics/fonts/FontFileUpdateRequest.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2021 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.SystemApi; +import android.os.ParcelFileDescriptor; + +import java.util.Objects; + +/** + * Request for updating a font file on the system. + * + * @hide + */ +@SystemApi +public final class FontFileUpdateRequest { + + private final ParcelFileDescriptor mParcelFileDescriptor; + private final byte[] mSignature; + + /** + * Creates a FontFileUpdateRequest with the given file and signature. + * + * @param parcelFileDescriptor A file descriptor of the font file. + * @param signature A PKCS#7 detached signature for verifying the font file. + */ + public FontFileUpdateRequest(@NonNull ParcelFileDescriptor parcelFileDescriptor, + @NonNull byte[] signature) { + Objects.requireNonNull(parcelFileDescriptor); + Objects.requireNonNull(signature); + mParcelFileDescriptor = parcelFileDescriptor; + mSignature = signature; + } + + /** + * Returns the file descriptor of the font file. + */ + @NonNull + public ParcelFileDescriptor getParcelFileDescriptor() { + return mParcelFileDescriptor; + } + + /** + * Returns the PKCS#7 detached signature for verifying the font file. + */ + @NonNull + public byte[] getSignature() { + return mSignature; + } +} diff --git a/core/java/android/graphics/fonts/FontManager.java b/core/java/android/graphics/fonts/FontManager.java index abb4f9fa7eef..f087dd075816 100644 --- a/core/java/android/graphics/fonts/FontManager.java +++ b/core/java/android/graphics/fonts/FontManager.java @@ -35,6 +35,8 @@ import com.android.internal.graphics.fonts.IFontManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; /** @@ -58,7 +60,7 @@ public class FontManager { RESULT_ERROR_VERIFICATION_FAILURE, RESULT_ERROR_VERSION_MISMATCH, RESULT_ERROR_INVALID_FONT_FILE, RESULT_ERROR_INVALID_FONT_NAME, RESULT_ERROR_DOWNGRADING, RESULT_ERROR_FAILED_UPDATE_CONFIG, - RESULT_ERROR_FONT_UPDATER_DISABLED, RESULT_ERROR_REMOTE_EXCEPTION }) + RESULT_ERROR_FONT_UPDATER_DISABLED, RESULT_ERROR_FONT_NOT_FOUND }) @Retention(RetentionPolicy.SOURCE) public @interface ResultCode {} @@ -131,9 +133,10 @@ public class FontManager { public static final int RESULT_ERROR_VERSION_MISMATCH = -8; /** - * Indicates a failure due to IPC communication. + * Indicates a failure occurred because a font with the specified PostScript name could not be + * found. */ - public static final int RESULT_ERROR_REMOTE_EXCEPTION = -9; + public static final int RESULT_ERROR_FONT_NOT_FOUND = -9; /** * Indicates a failure of opening font file. @@ -253,7 +256,6 @@ public class FontManager { * @see #RESULT_ERROR_DOWNGRADING * @see #RESULT_ERROR_FAILED_UPDATE_CONFIG * @see #RESULT_ERROR_FONT_UPDATER_DISABLED - * @see #RESULT_ERROR_REMOTE_EXCEPTION */ @RequiresPermission(Manifest.permission.UPDATE_FONTS) public @ResultCode int updateFontFile( @NonNull ParcelFileDescriptor pfd, @@ -261,10 +263,69 @@ public class FontManager { @IntRange(from = 0) int baseVersion ) { try { - return mIFontManager.updateFont(baseVersion, new FontUpdateRequest(pfd, signature)); + return mIFontManager.updateFontFile(new FontUpdateRequest(pfd, signature), baseVersion); } catch (RemoteException e) { - Log.e(TAG, "Failed to call updateFont API", e); - return RESULT_ERROR_REMOTE_EXCEPTION; + throw e.rethrowFromSystemServer(); + } + } + + /** + * Update or add system wide font families. + * + * <p>This method will update existing font families or add new font families. The updated + * font family definitions will be used when creating {@link android.graphics.Typeface} objects + * with using {@link android.graphics.Typeface#create(String, int)} specifying the family name, + * or through XML resources. Note that system fallback fonts cannot be modified by this method. + * Apps must use {@link android.graphics.Typeface.CustomFallbackBuilder} to use custom fallback + * fonts. + * + * <p>Font files can be updated by including {@link FontFileUpdateRequest} to {@code request} + * via {@link FontFamilyUpdateRequest.Builder#addFontFileUpdateRequest(FontFileUpdateRequest)}. + * The same constraints as {@link #updateFontFile} will apply when updating font files. + * + * <p>The caller must specify the base config version for keeping the font configuration + * consistent. If the font configuration is updated for some reason between the time you get + * a configuration with {@link #getFontConfig()} and the time when you call this method, + * {@link #RESULT_ERROR_VERSION_MISMATCH} will be returned. Get the latest font configuration by + * calling {@link #getFontConfig()} and call this method again with the latest config version. + * + * @param request A {@link FontFamilyUpdateRequest} to execute. + * @param baseVersion A base config version to be updated. You can get the latest config version + * by {@link FontConfig#getConfigVersion()} via {@link #getFontConfig()}. If + * the system has a newer config version, the update will fail with + * {@link #RESULT_ERROR_VERSION_MISMATCH}. + * @return A result code. + * @see FontConfig#getConfigVersion() + * @see #getFontConfig() + * @see #RESULT_SUCCESS + * @see #RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE + * @see #RESULT_ERROR_VERIFICATION_FAILURE + * @see #RESULT_ERROR_VERSION_MISMATCH + * @see #RESULT_ERROR_INVALID_FONT_FILE + * @see #RESULT_ERROR_INVALID_FONT_NAME + * @see #RESULT_ERROR_DOWNGRADING + * @see #RESULT_ERROR_FAILED_UPDATE_CONFIG + * @see #RESULT_ERROR_FONT_UPDATER_DISABLED + * @see #RESULT_ERROR_FONT_NOT_FOUND + */ + @RequiresPermission(Manifest.permission.UPDATE_FONTS) public @ResultCode int updateFontFamily( + @NonNull FontFamilyUpdateRequest request, @IntRange(from = 0) int baseVersion) { + List<FontUpdateRequest> requests = new ArrayList<>(); + List<FontFileUpdateRequest> fontFileUpdateRequests = request.getFontFileUpdateRequests(); + for (int i = 0; i < fontFileUpdateRequests.size(); i++) { + FontFileUpdateRequest fontFile = fontFileUpdateRequests.get(i); + requests.add(new FontUpdateRequest(fontFile.getParcelFileDescriptor(), + fontFile.getSignature())); + } + List<FontFamilyUpdateRequest.FontFamily> fontFamilies = request.getFontFamilies(); + for (int i = 0; i < fontFamilies.size(); i++) { + FontFamilyUpdateRequest.FontFamily fontFamily = fontFamilies.get(i); + requests.add(new FontUpdateRequest(fontFamily.getName(), fontFamily.getFonts())); + } + try { + return mIFontManager.updateFontFamily(requests, baseVersion); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } } diff --git a/core/java/android/graphics/fonts/FontUpdateRequest.java b/core/java/android/graphics/fonts/FontUpdateRequest.java index f551d6a175da..b79c8f62d492 100644 --- a/core/java/android/graphics/fonts/FontUpdateRequest.java +++ b/core/java/android/graphics/fonts/FontUpdateRequest.java @@ -19,13 +19,17 @@ package android.graphics.fonts; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.LocaleList; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.text.FontConfig; +import java.io.File; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; /** * Represents a font update request. Currently only font install request is supported. @@ -80,6 +84,26 @@ public final class FontUpdateRequest implements Parcelable { mFontFamily = fontFamily; } + public FontUpdateRequest(@NonNull String postScriptName, + @NonNull List<FontFamilyUpdateRequest.Font> variations) { + // TODO: Serialize the request directly instead of reusing FontConfig.FontFamily. + this(createFontFamily(postScriptName, variations)); + } + + private static FontConfig.FontFamily createFontFamily(@NonNull String postScriptName, + @NonNull List<FontFamilyUpdateRequest.Font> fonts) { + List<FontConfig.Font> configFonts = new ArrayList<>(fonts.size()); + for (FontFamilyUpdateRequest.Font font : fonts) { + // TODO: Support .otf. + configFonts.add(new FontConfig.Font(new File(font.getPostScriptName() + ".ttf"), null, + font.getStyle(), 0 /* index */, + FontVariationAxis.toFontVariationSettings(font.getAxes()), + null /* fontFamilyName */)); + } + return new FontConfig.FontFamily(configFonts, postScriptName, + LocaleList.getEmptyLocaleList(), FontConfig.FontFamily.VARIANT_DEFAULT); + } + protected FontUpdateRequest(Parcel in) { mType = in.readInt(); mFd = in.readParcelable(ParcelFileDescriptor.class.getClassLoader()); diff --git a/core/java/com/android/internal/graphics/fonts/IFontManager.aidl b/core/java/com/android/internal/graphics/fonts/IFontManager.aidl index dfcc91421b9e..1c7eca8b6c8e 100644 --- a/core/java/com/android/internal/graphics/fonts/IFontManager.aidl +++ b/core/java/com/android/internal/graphics/fonts/IFontManager.aidl @@ -20,6 +20,8 @@ import android.os.ParcelFileDescriptor; import android.graphics.fonts.FontUpdateRequest; import android.text.FontConfig; +import java.util.List; + /** * System private interface for talking with * {@link com.android.server.graphics.fonts.FontManagerService}. @@ -28,5 +30,7 @@ import android.text.FontConfig; interface IFontManager { FontConfig getFontConfig(); - int updateFont(int baseVersion, in FontUpdateRequest request); + int updateFontFile(in FontUpdateRequest request, int baseVersion); + + int updateFontFamily(in List<FontUpdateRequest> request, int baseVersion); } |
