FontsContract.java revision 54084b64b1860b652b1c50ba942b4cfc7fb28805
1b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri/* 2b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * Copyright (C) 2017 The Android Open Source Project 3b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * 4b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * Licensed under the Apache License, Version 2.0 (the "License"); 5b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * you may not use this file except in compliance with the License. 6b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * You may obtain a copy of the License at 7b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * 8b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * http://www.apache.org/licenses/LICENSE-2.0 9b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * 10b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * Unless required by applicable law or agreed to in writing, software 11b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * distributed under the License is distributed on an "AS IS" BASIS, 12b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * See the License for the specific language governing permissions and 14b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * limitations under the License. 15b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri */ 16b0812a30499376e09e2deb5995e998c629f24985Clara Bayarripackage android.provider; 17b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri 1869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport static android.graphics.fonts.FontVariationAxis.InvalidFormatException; 1969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport static java.lang.annotation.RetentionPolicy.SOURCE; 2069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 2169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport android.annotation.IntDef; 2269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport android.annotation.IntRange; 2369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport android.annotation.NonNull; 2469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport android.annotation.Nullable; 25b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.content.ContentResolver; 26b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.content.ContentUris; 27b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.content.Context; 283c4be77db95ea716889568bde853be082e764da9Clara Bayarriimport android.content.pm.PackageInfo; 2969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport android.content.pm.PackageManager.NameNotFoundException; 30b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.content.pm.PackageManager; 31b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.content.pm.ProviderInfo; 323c4be77db95ea716889568bde853be082e764da9Clara Bayarriimport android.content.pm.Signature; 33b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.database.Cursor; 343c4be77db95ea716889568bde853be082e764da9Clara Bayarriimport android.graphics.Typeface; 35b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.graphics.fonts.FontRequest; 3669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport android.graphics.fonts.FontVariationAxis; 37b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.net.Uri; 38b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.Bundle; 3969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport android.os.CancellationSignal; 40b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.Handler; 41b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.HandlerThread; 42b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.ParcelFileDescriptor; 43b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.Process; 44b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.ResultReceiver; 455a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonakaimport android.util.ArraySet; 46b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.util.Log; 47daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonakaimport android.util.LruCache; 48b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri 49b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport com.android.internal.annotations.GuardedBy; 503c4be77db95ea716889568bde853be082e764da9Clara Bayarriimport com.android.internal.annotations.VisibleForTesting; 5169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport com.android.internal.util.Preconditions; 52b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri 5369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport java.io.FileInputStream; 54b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport java.io.FileNotFoundException; 55bacf2352ab892724551431551ae973ae3b68db35Clara Bayarriimport java.io.IOException; 5669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport java.lang.annotation.Retention; 5769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport java.lang.annotation.RetentionPolicy; 5869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport java.nio.ByteBuffer; 5969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport java.nio.channels.FileChannel; 60b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport java.util.ArrayList; 61fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarriimport java.util.Arrays; 62fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarriimport java.util.Collections; 63fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarriimport java.util.Comparator; 6469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport java.util.HashMap; 653c4be77db95ea716889568bde853be082e764da9Clara Bayarriimport java.util.List; 6669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport java.util.Map; 675a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonakaimport java.util.Set; 68b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri 69b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri/** 70b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * Utility class to deal with Font ContentProviders. 71b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri */ 72b0812a30499376e09e2deb5995e998c629f24985Clara Bayarripublic class FontsContract { 73b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri private static final String TAG = "FontsContract"; 74b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri 75b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri /** 76b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * Defines the constants used in a response from a Font Provider. The cursor returned from the 77b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * query should have the ID column populated with the content uri ID for the resulting font. 78b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * This should point to a real file or shared memory, as the client will mmap the given file 79b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * descriptor. Pipes, sockets and other non-mmap-able file descriptors will fail to load in the 80b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * client application. 81b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri */ 82b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri public static final class Columns implements BaseColumns { 837fea2e264e2e793586bb999ee80897d488e62141Clara Bayarri 847fea2e264e2e793586bb999ee80897d488e62141Clara Bayarri // Do not instantiate. 857fea2e264e2e793586bb999ee80897d488e62141Clara Bayarri private Columns() {} 867fea2e264e2e793586bb999ee80897d488e62141Clara Bayarri 87b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri /** 88b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * Constant used to request data from a font provider. The cursor returned from the query 8943c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka * may populate this column with a long for the font file ID. The client will request a file 9043c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka * descriptor to "file/FILE_ID" with this ID immediately under the top-level content URI. If 9143c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka * not present, the client will request a file descriptor to the top-level URI with the 9243c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka * given base font ID. Note that several results may return the same file ID, e.g. for TTC 9343c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka * files with different indices. 9443c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka */ 9543c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka public static final String FILE_ID = "file_id"; 9643c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka /** 9743c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka * Constant used to request data from a font provider. The cursor returned from the query 98b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * should have this column populated with an int for the ttc index for the resulting font. 99b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri */ 100b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri public static final String TTC_INDEX = "font_ttc_index"; 101b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri /** 102b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * Constant used to request data from a font provider. The cursor returned from the query 103b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * may populate this column with the font variation settings String information for the 104b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * font. 105b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri */ 106b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri public static final String VARIATION_SETTINGS = "font_variation_settings"; 107b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri /** 108bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri * Constant used to request data from a font provider. The cursor returned from the query 109fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka * should have this column populated with the int weight for the resulting font. This value 110fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka * should be between 100 and 900. The most common values are 400 for regular weight and 700 111fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka * for bold weight. 112fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka */ 113fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka public static final String WEIGHT = "font_weight"; 114fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka /** 115fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka * Constant used to request data from a font provider. The cursor returned from the query 116fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka * should have this column populated with the int italic for the resulting font. This should 117fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka * be 0 for regular style and 1 for italic. 118fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka */ 119fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka public static final String ITALIC = "font_italic"; 120fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka /** 121fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka * Constant used to request data from a font provider. The cursor returned from the query 122bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri * should have this column populated to indicate the result status of the 123bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri * query. This will be checked before any other data in the cursor. Possible values are 124bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri * {@link #RESULT_CODE_OK}, {@link #RESULT_CODE_FONT_NOT_FOUND}, 1255706a1b12dd891098fb7bf479623d42979171144Clara Bayarri * {@link #RESULT_CODE_MALFORMED_QUERY} and {@link #RESULT_CODE_FONT_UNAVAILABLE} for system 1265706a1b12dd891098fb7bf479623d42979171144Clara Bayarri * defined values. You may also define your own values in the 0x000010000..0xFFFF0000 range. 1275706a1b12dd891098fb7bf479623d42979171144Clara Bayarri * If not present, {@link #RESULT_CODE_OK} will be assumed. 128bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri */ 129bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri public static final String RESULT_CODE = "result_code"; 130bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri 131bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri /** 132bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri * Constant used to represent a result was retrieved successfully. The given fonts will be 133bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri * attempted to retrieve immediately via 134bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri * {@link android.content.ContentProvider#openFile(Uri, String)}. See {@link #RESULT_CODE}. 135bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri */ 136bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri public static final int RESULT_CODE_OK = 0; 137bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri /** 138bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri * Constant used to represent a result was not found. See {@link #RESULT_CODE}. 139bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri */ 140bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri public static final int RESULT_CODE_FONT_NOT_FOUND = 1; 141bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri /** 142bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri * Constant used to represent a result was found, but cannot be provided at this moment. Use 143bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri * this to indicate, for example, that a font needs to be fetched from the network. See 144bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri * {@link #RESULT_CODE}. 145bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri */ 146bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; 147bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri /** 148bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri * Constant used to represent that the query was not in a supported format by the provider. 149bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri * See {@link #RESULT_CODE}. 150bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri */ 151bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri public static final int RESULT_CODE_MALFORMED_QUERY = 3; 152b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri } 153b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri 15454084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka private static final Object sLock = new Object(); 15554084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka @GuardedBy("sLock") 15654084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka private static Handler sHandler; 15754084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka @GuardedBy("sLock") 15854084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka private static HandlerThread sThread; 15954084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka @GuardedBy("sLock") 16054084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka private static Set<String> sInQueueSet; 161b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri 16254084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka private volatile static Context sContext; // set once in setApplicationContextForResources 163b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri 164daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka private static final LruCache<String, Typeface> sTypefaceCache = new LruCache<>(16); 165daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka 16654084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka private FontsContract() { 16754084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka } 16854084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka 169b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri /** @hide */ 17054084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka public static void setApplicationContextForResources(Context context) { 17154084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka sContext = context.getApplicationContext(); 172b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri } 173b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri 17469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** 17569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Object represent a font entry in the family returned from {@link #fetchFonts}. 17669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka */ 17769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public static class FontInfo { 17869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka private final Uri mUri; 17969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka private final int mTtcIndex; 18069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka private final FontVariationAxis[] mAxes; 18169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka private final int mWeight; 18269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka private final boolean mItalic; 18369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka private final int mResultCode; 18469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 18569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** 18669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Creates a Font with all the information needed about a provided font. 18769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param uri A URI associated to the font file. 18869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param ttcIndex If providing a TTC_INDEX file, the index to point to. Otherwise, 0. 18969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param axes If providing a variation font, the settings for it. May be null. 19069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param weight An integer that indicates the font weight. 19169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param italic A boolean that indicates the font is italic style or not. 19269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param resultCode A boolean that indicates the font contents is ready. 19369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka */ 19469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** @hide */ 19569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public FontInfo(@NonNull Uri uri, @IntRange(from = 0) int ttcIndex, 19669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka @Nullable FontVariationAxis[] axes, @IntRange(from = 1, to = 1000) int weight, 19769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka boolean italic, int resultCode) { 19869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka mUri = Preconditions.checkNotNull(uri); 19969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka mTtcIndex = ttcIndex; 20069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka mAxes = axes; 20169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka mWeight = weight; 20269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka mItalic = italic; 20369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka mResultCode = resultCode; 20469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 20569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 20669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** 20769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Returns a URI associated to this record. 20869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka */ 20969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public @NonNull Uri getUri() { 21069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka return mUri; 21169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 21269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 21369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** 21469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Returns the index to be used to access this font when accessing a TTC file. 21569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka */ 21669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public @IntRange(from = 0) int getTtcIndex() { 21769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka return mTtcIndex; 21869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 21969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 22069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** 22169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Returns the list of axes associated to this font. 22269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka */ 22369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public @Nullable FontVariationAxis[] getAxes() { 22469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka return mAxes; 22569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 22669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 22769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** 22869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Returns the weight value for this font. 22969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka */ 23069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public @IntRange(from = 1, to = 1000) int getWeight() { 23169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka return mWeight; 23269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 23369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 23469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** 23569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Returns whether this font is italic. 23669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka */ 23769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public boolean isItalic() { 23869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka return mItalic; 23969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 24069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 24169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** 24269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Returns result code. 24369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * 24469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * {@link FontsContract.Columns#RESULT_CODE} 24569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka */ 24669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public int getResultCode() { 24769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka return mResultCode; 24869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 24969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 25069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 25169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** 25269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Object returned from {@link #fetchFonts}. 25369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka */ 25469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public static class FontFamilyResult { 25569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** 25669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Constant represents that the font was successfully retrieved. Note that when this value 25769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * is set and {@link #getFonts} returns an empty array, it means there were no fonts 25869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * matching the given query. 25969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka */ 26069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public static final int STATUS_OK = 0; 26169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 26269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** 26369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Constant represents that the given certificate was not matched with the provider's 26469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * signature. {@link #getFonts} returns null if this status was set. 26569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka */ 26669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public static final int STATUS_WRONG_CERTIFICATES = 1; 26769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 26869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** 26969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Constant represents that the provider returns unexpected data. {@link #getFonts} returns 27069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * null if this status was set. For example, this value is set when the font provider 27169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * gives invalid format of variation settings. 27269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka */ 27369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public static final int STATUS_UNEXPECTED_DATA_PROVIDED = 2; 27469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 275ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka /** 276ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka * Constant represents that the fetching font data was rejected by system. This happens if 277ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka * the passed context is restricted. 278ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka */ 279ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka public static final int STATUS_REJECTED = 3; 280ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka 28169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** @hide */ 28269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka @IntDef({STATUS_OK, STATUS_WRONG_CERTIFICATES, STATUS_UNEXPECTED_DATA_PROVIDED}) 28369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka @Retention(RetentionPolicy.SOURCE) 28469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka @interface FontResultStatus {} 28569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 28669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka private final @FontResultStatus int mStatusCode; 28769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka private final FontInfo[] mFonts; 28869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 28969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** @hide */ 29069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public FontFamilyResult(@FontResultStatus int statusCode, @Nullable FontInfo[] fonts) { 29169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka mStatusCode = statusCode; 29269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka mFonts = fonts; 29369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 29469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 29569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public @FontResultStatus int getStatusCode() { 29669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka return mStatusCode; 29769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 29869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 29969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public @NonNull FontInfo[] getFonts() { 30069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka return mFonts; 30169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 3023c4be77db95ea716889568bde853be082e764da9Clara Bayarri } 3033c4be77db95ea716889568bde853be082e764da9Clara Bayarri 30454084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka private static final int THREAD_RENEWAL_THRESHOLD_MS = 10000; 30554084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka 306b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri // We use a background thread to post the content resolving work for all requests on. This 307b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri // thread should be quit/stopped after all requests are done. 30854084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka // TODO: Factor out to other class. Consider to switch MessageQueue.IdleHandler. 30954084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka private static final Runnable sReplaceDispatcherThreadRunnable = new Runnable() { 310b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri @Override 311b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri public void run() { 31254084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka synchronized (sLock) { 31354084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka if (sThread != null) { 31454084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka sThread.quitSafely(); 31554084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka sThread = null; 31654084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka sHandler = null; 31754084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka sInQueueSet = null; 318b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri } 319b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri } 320b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri } 321b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri }; 322b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri 323bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri /** @hide */ 32454084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka public static Typeface getFontOrWarmUpCache(FontRequest request) { 3255a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka final String id = request.getIdentifier(); 3265a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka Typeface cachedTypeface = sTypefaceCache.get(id); 3275a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka if (cachedTypeface != null) { 3285a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka return cachedTypeface; 3295a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka } 3305a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka 3315a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka // Unfortunately the typeface is not available at this time, but requesting from the font 3325a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka // provider takes too much time. For now, request the font data to ensure it is in the cache 3335a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka // next time and return. 33454084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka synchronized (sLock) { 33554084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka if (sHandler == null) { 33654084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka sThread = new HandlerThread("fonts", Process.THREAD_PRIORITY_BACKGROUND); 33754084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka sThread.start(); 33854084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka sHandler = new Handler(sThread.getLooper()); 33954084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka sInQueueSet = new ArraySet<>(); 340b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri } 34154084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka if (sInQueueSet.contains(id)) { 3425a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka return null; // Already requested. 3435a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka } 34454084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka sInQueueSet.add(id); 34554084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka sHandler.post(() -> { 34654084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka synchronized (sLock) { 34754084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka sInQueueSet.remove(id); 34869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 34969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka try { 35054084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka FontFamilyResult result = fetchFonts(sContext, null, request); 3515a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka if (result.getStatusCode() == FontFamilyResult.STATUS_OK) { 35254084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka Typeface typeface = buildTypeface(sContext, null, result.getFonts()); 3535a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka if (typeface != null) { 3545a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka sTypefaceCache.put(id, typeface); 35569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 35669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 3575a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka } catch (NameNotFoundException e) { 3585a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka // Ignore. 35969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 360b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri }); 36154084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka sHandler.removeCallbacks(sReplaceDispatcherThreadRunnable); 36254084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka sHandler.postDelayed(sReplaceDispatcherThreadRunnable, THREAD_RENEWAL_THRESHOLD_MS); 363b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri } 3645a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka return null; 365b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri } 366b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri 36769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** 3680b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * Interface used to receive asynchronously fetched typefaces. 3690b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka */ 3700b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka public static class FontRequestCallback { 3710b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka /** 3720b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given 3730b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * provider was not found on the device. 3740b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka */ 37554084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; 3760b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka /** 3770b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given 3780b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * provider must be authenticated and the given certificates do not match its signature. 3790b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka */ 38054084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; 3810b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka /** 3820b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font 3830b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * returned by the provider was not loaded properly. 3840b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka */ 3850b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; 3860b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka /** 3870b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font 3880b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * provider did not return any results for the given query. 3890b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka */ 3900b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka public static final int FAIL_REASON_FONT_NOT_FOUND = Columns.RESULT_CODE_FONT_NOT_FOUND; 3910b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka /** 3920b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font 3930b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * provider found the queried font, but it is currently unavailable. 3940b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka */ 3950b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka public static final int FAIL_REASON_FONT_UNAVAILABLE = Columns.RESULT_CODE_FONT_UNAVAILABLE; 3960b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka /** 3970b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given 3980b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * query was not supported by the provider. 3990b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka */ 4000b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka public static final int FAIL_REASON_MALFORMED_QUERY = Columns.RESULT_CODE_MALFORMED_QUERY; 4010b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka 4020b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka /** @hide */ 4030b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka @IntDef({ FAIL_REASON_PROVIDER_NOT_FOUND, FAIL_REASON_FONT_LOAD_ERROR, 4040b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka FAIL_REASON_FONT_NOT_FOUND, FAIL_REASON_FONT_UNAVAILABLE, 4050b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka FAIL_REASON_MALFORMED_QUERY }) 4060b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka @Retention(RetentionPolicy.SOURCE) 4070b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka @interface FontRequestFailReason {} 4080b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka 4090b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka public FontRequestCallback() {} 4100b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka 4110b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka /** 4125a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka * Called then a Typeface request done via {@link #requestFont} is complete. Note that this 4135a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka * method will not be called if {@link #onTypefaceRequestFailed(int)} is called instead. 4140b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * @param typeface The Typeface object retrieved. 4150b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka */ 4160b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka public void onTypefaceRetrieved(Typeface typeface) {} 4170b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka 4180b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka /** 4195a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka * Called when a Typeface request done via {@link #requestFont}} fails. 4200b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * @param reason One of {@link #FAIL_REASON_PROVIDER_NOT_FOUND}, 4210b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * {@link #FAIL_REASON_FONT_NOT_FOUND}, 4220b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * {@link #FAIL_REASON_FONT_LOAD_ERROR}, 4230b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * {@link #FAIL_REASON_FONT_UNAVAILABLE} or 4245706a1b12dd891098fb7bf479623d42979171144Clara Bayarri * {@link #FAIL_REASON_MALFORMED_QUERY} if returned by the system. May also be 4255706a1b12dd891098fb7bf479623d42979171144Clara Bayarri * a positive value greater than 0 defined by the font provider as an 4265706a1b12dd891098fb7bf479623d42979171144Clara Bayarri * additional error code. Refer to the provider's documentation for more 4275706a1b12dd891098fb7bf479623d42979171144Clara Bayarri * information on possible returned error codes. 4280b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka */ 4290b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka public void onTypefaceRequestFailed(@FontRequestFailReason int reason) {} 4300b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka } 4310b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka 4320b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka /** 4330b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * Create a typeface object given a font request. The font will be asynchronously fetched, 4340b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * therefore the result is delivered to the given callback. See {@link FontRequest}. 4350b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * Only one of the methods in callback will be invoked, depending on whether the request 4360b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * succeeds or fails. These calls will happen on the caller thread. 437daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka * 438daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka * Note that the result Typeface may be cached internally and the same instance will be returned 439daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka * the next time you call this method with the same request. If you want to bypass this cache, 440daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka * use {@link #fetchFonts} and {@link #buildTypeface} instead. 441daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka * 4420b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * @param context A context to be used for fetching from font provider. 4430b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * @param request A {@link FontRequest} object that identifies the provider and query for the 4440b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * request. May not be null. 4450b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * @param callback A callback that will be triggered when results are obtained. May not be null. 4460b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka * @param handler A handler to be processed the font fetching. 4470b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka */ 4480b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka public static void requestFont(@NonNull Context context, @NonNull FontRequest request, 4490b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka @NonNull FontRequestCallback callback, @NonNull Handler handler) { 4500b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka 4510b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka final Handler callerThreadHandler = new Handler(); 452daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka final Typeface cachedTypeface = sTypefaceCache.get(request.getIdentifier()); 453daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka if (cachedTypeface != null) { 454daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka callerThreadHandler.post(() -> callback.onTypefaceRetrieved(cachedTypeface)); 455daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka return; 456daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka } 457daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka 4580b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka handler.post(() -> { 4590b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka FontFamilyResult result; 4600b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka try { 4610b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka result = fetchFonts(context, null /* cancellation signal */, request); 4620b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka } catch (NameNotFoundException e) { 4630b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka callerThreadHandler.post(() -> callback.onTypefaceRequestFailed( 4640b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka FontRequestCallback.FAIL_REASON_PROVIDER_NOT_FOUND)); 4650b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka return; 4660b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka } 4670b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka 468daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka // Same request might be dispatched during fetchFonts. Check the cache again. 469daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka final Typeface anotherCachedTypeface = sTypefaceCache.get(request.getIdentifier()); 470daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka if (anotherCachedTypeface != null) { 471daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka callerThreadHandler.post(() -> callback.onTypefaceRetrieved(anotherCachedTypeface)); 472daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka return; 473daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka } 474daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka 4750b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka if (result.getStatusCode() != FontFamilyResult.STATUS_OK) { 4760b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka switch (result.getStatusCode()) { 4770b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka case FontFamilyResult.STATUS_WRONG_CERTIFICATES: 4780b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka callerThreadHandler.post(() -> callback.onTypefaceRequestFailed( 4790b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka FontRequestCallback.FAIL_REASON_WRONG_CERTIFICATES)); 4800b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka return; 4810b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka case FontFamilyResult.STATUS_UNEXPECTED_DATA_PROVIDED: 4820b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka callerThreadHandler.post(() -> callback.onTypefaceRequestFailed( 4830b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR)); 4840b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka return; 4850b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka default: 4860b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka // fetchFont returns unexpected status type. Fallback to load error. 4870b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka callerThreadHandler.post(() -> callback.onTypefaceRequestFailed( 4880b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR)); 4890b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka return; 4900b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka } 4910b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka } 4920b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka 4930b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka final FontInfo[] fonts = result.getFonts(); 4940b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka if (fonts == null || fonts.length == 0) { 4950b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka callerThreadHandler.post(() -> callback.onTypefaceRequestFailed( 4960b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND)); 4970b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka return; 4980b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka } 4990b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka for (final FontInfo font : fonts) { 5000b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka if (font.getResultCode() != Columns.RESULT_CODE_OK) { 5010b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka // We proceed if all font entry is ready to use. Otherwise report the first 5020b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka // error. 5030b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka final int resultCode = font.getResultCode(); 5040b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka if (resultCode < 0) { 5050b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka // Negative values are reserved for internal errors. Fallback to load error. 5060b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka callerThreadHandler.post(() -> callback.onTypefaceRequestFailed( 5070b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR)); 5080b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka } else { 5090b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka callerThreadHandler.post(() -> callback.onTypefaceRequestFailed( 5100b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka resultCode)); 5110b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka } 5120b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka return; 5130b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka } 5140b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka } 5150b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka 5160b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka final Typeface typeface = buildTypeface(context, null /* cancellation signal */, fonts); 5170b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka if (typeface == null) { 5180b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka // Something went wrong during reading font files. This happens if the given font 5190b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka // file is an unsupported font type. 5200b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka callerThreadHandler.post(() -> callback.onTypefaceRequestFailed( 5210b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR)); 5220b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka return; 5230b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka } 5240b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka 525daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka sTypefaceCache.put(request.getIdentifier(), typeface); 5260b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka callerThreadHandler.post(() -> callback.onTypefaceRetrieved(typeface)); 5270b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka }); 5280b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka } 5290b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka 5300b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka /** 53169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Fetch fonts given a font request. 53269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * 53369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param context A {@link Context} to be used for fetching fonts. 53469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param cancellationSignal A signal to cancel the operation in progress, or null if none. If 53569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * the operation is canceled, then {@link 53669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * android.os.OperationCanceledException} will be thrown when the 53769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * query is executed. 53869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param request A {@link FontRequest} object that identifies the provider and query for the 53969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * request. 54069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * 54169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @return {@link FontFamilyResult} 54269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * 54369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @throws NameNotFoundException If requested package or authority was not found in system. 54469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka */ 54569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public static @NonNull FontFamilyResult fetchFonts( 54669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka @NonNull Context context, @Nullable CancellationSignal cancellationSignal, 54769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka @NonNull FontRequest request) throws NameNotFoundException { 548ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka if (context.isRestricted()) { 549ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka // TODO: Should we allow if the peer process is system or myself? 550ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka return new FontFamilyResult(FontFamilyResult.STATUS_REJECTED, null); 551ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka } 55269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka ProviderInfo providerInfo = getProvider(context.getPackageManager(), request); 55369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka if (providerInfo == null) { 55469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka return new FontFamilyResult(FontFamilyResult.STATUS_WRONG_CERTIFICATES, null); 55569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 55669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 55769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka try { 55869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka FontInfo[] fonts = getFontFromProvider( 55969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka context, request, providerInfo.authority, cancellationSignal); 56069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka return new FontFamilyResult(FontFamilyResult.STATUS_OK, fonts); 56169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } catch (InvalidFormatException e) { 56269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka return new FontFamilyResult(FontFamilyResult.STATUS_UNEXPECTED_DATA_PROVIDED, null); 56369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 56469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 56569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 56669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** 56769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Build a Typeface from an array of {@link FontInfo}. Results that are marked as not ready 56869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * will be skipped. 56969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * 57069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param context A {@link Context} that will be used to fetch the font contents. 57169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param cancellationSignal A signal to cancel the operation in progress, or null if none. If 57269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * the operation is canceled, then {@link 57369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * android.os.OperationCanceledException} will be thrown. 57469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param fonts An array of {@link FontInfo} to be used to create a Typeface. 57569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param weight A weight value to be used for selecting a font from a font family. 57669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param italic {@code true} if this font is of italic style. This will be used for font 57769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * selection from a font family. 57869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param fallbackFontName A fallback font name used if this method fails to create the 57969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Typeface. By passing {@code null}, this method returns {@code null} 58069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * if typeface creation fails. 58169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @return A Typeface object. May return {@code null} if that is the value passed to {@code 58269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * fallBackFontName}. 58369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka */ 58469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public static Typeface buildTypeface(@NonNull Context context, 58569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts, 58669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka int weight, boolean italic, @Nullable String fallbackFontName) { 587ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka if (context.isRestricted()) { 588ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka // TODO: Should we allow if the peer process is system or myself? 589ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka return null; 590ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka } 59169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka final Map<Uri, ByteBuffer> uriBuffer = 59269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka prepareFontData(context, fonts, cancellationSignal); 5935b6347a6af668ba47b3fab14cefbe03cc440c3a4Seigo Nonaka return new Typeface.Builder(fonts, uriBuffer) 5945b6347a6af668ba47b3fab14cefbe03cc440c3a4Seigo Nonaka .setFallback(fallbackFontName) 59569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka .setWeight(weight) 59669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka .setItalic(italic) 59769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka .build(); 59869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 59969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 60069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** 60169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Build a Typeface from an array of {@link FontInfo} 60269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * 60369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Results that are marked as not ready will be skipped. 60469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * 60569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param context A {@link Context} that will be used to fetch the font contents. 60669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param cancellationSignal A signal to cancel the operation in progress, or null if none. If 60769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * the operation is canceled, then {@link 60869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * android.os.OperationCanceledException} will be thrown. 60969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param fonts An array of {@link FontInfo} to be used to create a Typeface. 61069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @return A Typeface object. Returns null if typeface creation fails. 61169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka */ 61269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public static Typeface buildTypeface(@NonNull Context context, 61369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts) { 614ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka if (context.isRestricted()) { 615ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka // TODO: Should we allow if the peer process is system or myself? 616ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka return null; 617ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka } 61869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka final Map<Uri, ByteBuffer> uriBuffer = 61969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka prepareFontData(context, fonts, cancellationSignal); 62069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka return new Typeface.Builder(fonts, uriBuffer).build(); 62169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 62269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 62369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka /** 62469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * A helper function to create a mapping from {@link Uri} to {@link ByteBuffer}. 62569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * 62669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * Skip if the file contents is not ready to be read. 62769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * 62869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param context A {@link Context} to be used for resolving content URI in 62969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * {@link FontInfo}. 63069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @param fonts An array of {@link FontInfo}. 63169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka * @return A map from {@link Uri} to {@link ByteBuffer}. 63269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka */ 63369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka private static Map<Uri, ByteBuffer> prepareFontData(Context context, FontInfo[] fonts, 63469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka CancellationSignal cancellationSignal) { 63569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka final HashMap<Uri, ByteBuffer> out = new HashMap<>(); 63669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka final ContentResolver resolver = context.getContentResolver(); 63769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 63869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka for (FontInfo font : fonts) { 63969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka if (font.getResultCode() != Columns.RESULT_CODE_OK) { 64069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka continue; 64169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 64269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 64369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka final Uri uri = font.getUri(); 64469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka if (out.containsKey(uri)) { 64569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka continue; 64669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 64769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 64869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka ByteBuffer buffer = null; 64969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka try (final ParcelFileDescriptor pfd = 65069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka resolver.openFileDescriptor(uri, "r", cancellationSignal); 65169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka final FileInputStream fis = new FileInputStream(pfd.getFileDescriptor())) { 65269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka final FileChannel fileChannel = fis.getChannel(); 65369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka final long size = fileChannel.size(); 65469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, size); 65569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } catch (IOException e) { 65669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka // ignore 65769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 65869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 65969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka // TODO: try other approach?, e.g. read all contents instead of mmap. 66069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 66169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka out.put(uri, buffer); 66269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 66369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka return Collections.unmodifiableMap(out); 66469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } 66569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 6663c4be77db95ea716889568bde853be082e764da9Clara Bayarri /** @hide */ 6673c4be77db95ea716889568bde853be082e764da9Clara Bayarri @VisibleForTesting 66869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public static @Nullable ProviderInfo getProvider( 66969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka PackageManager packageManager, FontRequest request) throws NameNotFoundException { 6703c4be77db95ea716889568bde853be082e764da9Clara Bayarri String providerAuthority = request.getProviderAuthority(); 67169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka ProviderInfo info = packageManager.resolveContentProvider(providerAuthority, 0); 6723c4be77db95ea716889568bde853be082e764da9Clara Bayarri if (info == null) { 67369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka throw new NameNotFoundException("No package found for authority: " + providerAuthority); 6743c4be77db95ea716889568bde853be082e764da9Clara Bayarri } 6753c4be77db95ea716889568bde853be082e764da9Clara Bayarri 6763c4be77db95ea716889568bde853be082e764da9Clara Bayarri if (!info.packageName.equals(request.getProviderPackage())) { 67769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka throw new NameNotFoundException("Found content provider " + providerAuthority 67869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka + ", but package was not " + request.getProviderPackage()); 6793c4be77db95ea716889568bde853be082e764da9Clara Bayarri } 6803c4be77db95ea716889568bde853be082e764da9Clara Bayarri // Trust system apps without signature checks 6813c4be77db95ea716889568bde853be082e764da9Clara Bayarri if (info.applicationInfo.isSystemApp()) { 6823c4be77db95ea716889568bde853be082e764da9Clara Bayarri return info; 6833c4be77db95ea716889568bde853be082e764da9Clara Bayarri } 6843c4be77db95ea716889568bde853be082e764da9Clara Bayarri 685fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri List<byte[]> signatures; 68669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka PackageInfo packageInfo = packageManager.getPackageInfo(info.packageName, 68769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka PackageManager.GET_SIGNATURES); 68869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka signatures = convertToByteArrayList(packageInfo.signatures); 68969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka Collections.sort(signatures, sByteArrayComparator); 69069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 6913c4be77db95ea716889568bde853be082e764da9Clara Bayarri List<List<byte[]>> requestCertificatesList = request.getCertificates(); 6923c4be77db95ea716889568bde853be082e764da9Clara Bayarri for (int i = 0; i < requestCertificatesList.size(); ++i) { 693fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri // Make a copy so we can sort it without modifying the incoming data. 694fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri List<byte[]> requestSignatures = new ArrayList<>(requestCertificatesList.get(i)); 695fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri Collections.sort(requestSignatures, sByteArrayComparator); 696fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri if (equalsByteArrayList(signatures, requestSignatures)) { 6973c4be77db95ea716889568bde853be082e764da9Clara Bayarri return info; 6983c4be77db95ea716889568bde853be082e764da9Clara Bayarri } 6993c4be77db95ea716889568bde853be082e764da9Clara Bayarri } 7003c4be77db95ea716889568bde853be082e764da9Clara Bayarri return null; 7013c4be77db95ea716889568bde853be082e764da9Clara Bayarri } 7023c4be77db95ea716889568bde853be082e764da9Clara Bayarri 703fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri private static final Comparator<byte[]> sByteArrayComparator = (l, r) -> { 704fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri if (l.length != r.length) { 705fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri return l.length - r.length; 7063c4be77db95ea716889568bde853be082e764da9Clara Bayarri } 707fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri for (int i = 0; i < l.length; ++i) { 708fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri if (l[i] != r[i]) { 709fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri return l[i] - r[i]; 710fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri } 711fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri } 712fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri return 0; 713fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri }; 714fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri 71569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka private static boolean equalsByteArrayList( 71669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka List<byte[]> signatures, List<byte[]> requestSignatures) { 717fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri if (signatures.size() != requestSignatures.size()) { 718fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri return false; 719fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri } 720fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri for (int i = 0; i < signatures.size(); ++i) { 721fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri if (!Arrays.equals(signatures.get(i), requestSignatures.get(i))) { 722fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri return false; 723fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri } 724fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri } 725fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri return true; 7263c4be77db95ea716889568bde853be082e764da9Clara Bayarri } 7273c4be77db95ea716889568bde853be082e764da9Clara Bayarri 72869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka private static List<byte[]> convertToByteArrayList(Signature[] signatures) { 729fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri List<byte[]> shas = new ArrayList<>(); 730fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri for (int i = 0; i < signatures.length; ++i) { 731fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri shas.add(signatures[i].toByteArray()); 732fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri } 7333c4be77db95ea716889568bde853be082e764da9Clara Bayarri return shas; 7343c4be77db95ea716889568bde853be082e764da9Clara Bayarri } 7353c4be77db95ea716889568bde853be082e764da9Clara Bayarri 7363c4be77db95ea716889568bde853be082e764da9Clara Bayarri /** @hide */ 7373c4be77db95ea716889568bde853be082e764da9Clara Bayarri @VisibleForTesting 73869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka public static @NonNull FontInfo[] getFontFromProvider( 73969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka Context context, FontRequest request, String authority, 74069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka CancellationSignal cancellationSignal) throws InvalidFormatException { 74169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka ArrayList<FontInfo> result = new ArrayList<>(); 74243c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka final Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 7433c4be77db95ea716889568bde853be082e764da9Clara Bayarri .authority(authority) 744b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri .build(); 74543c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka final Uri fileBaseUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 74643c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka .authority(authority) 74743c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka .appendPath("file") 74843c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka .build(); 74969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka try (Cursor cursor = context.getContentResolver().query(uri, new String[] { Columns._ID, 75043c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka Columns.FILE_ID, Columns.TTC_INDEX, Columns.VARIATION_SETTINGS, 75154084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka Columns.WEIGHT, Columns.ITALIC, Columns.RESULT_CODE }, 75269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka "query = ?", new String[] { request.getQuery() }, null, cancellationSignal);) { 753b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri // TODO: Should we restrict the amount of fonts that can be returned? 754b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri // TODO: Write documentation explaining that all results should be from the same family. 755b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri if (cursor != null && cursor.getCount() > 0) { 756bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri final int resultCodeColumnIndex = cursor.getColumnIndex(Columns.RESULT_CODE); 757b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri result = new ArrayList<>(); 758bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri final int idColumnIndex = cursor.getColumnIndexOrThrow(Columns._ID); 75943c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka final int fileIdColumnIndex = cursor.getColumnIndex(Columns.FILE_ID); 760b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri final int ttcIndexColumnIndex = cursor.getColumnIndex(Columns.TTC_INDEX); 761b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri final int vsColumnIndex = cursor.getColumnIndex(Columns.VARIATION_SETTINGS); 762fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka final int weightColumnIndex = cursor.getColumnIndex(Columns.WEIGHT); 763fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka final int italicColumnIndex = cursor.getColumnIndex(Columns.ITALIC); 764b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri while (cursor.moveToNext()) { 76569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka int resultCode = resultCodeColumnIndex != -1 766bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri ? cursor.getInt(resultCodeColumnIndex) : Columns.RESULT_CODE_OK; 76769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka final int ttcIndex = ttcIndexColumnIndex != -1 76869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka ? cursor.getInt(ttcIndexColumnIndex) : 0; 76969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka final String variationSettings = vsColumnIndex != -1 77069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka ? cursor.getString(vsColumnIndex) : null; 77169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka 77243c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka Uri fileUri; 77343c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka if (fileIdColumnIndex == -1) { 77443c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka long id = cursor.getLong(idColumnIndex); 77543c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka fileUri = ContentUris.withAppendedId(uri, id); 77643c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka } else { 77743c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka long id = cursor.getLong(fileIdColumnIndex); 77843c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka fileUri = ContentUris.withAppendedId(fileBaseUri, id); 77943c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka } 78069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka int weight; 78169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka boolean italic; 78269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka if (weightColumnIndex != -1 && italicColumnIndex != -1) { 78369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka weight = cursor.getInt(weightColumnIndex); 78469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka italic = cursor.getInt(italicColumnIndex) == 1; 78569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka } else { 78669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka weight = Typeface.Builder.NORMAL_WEIGHT; 78769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka italic = false; 788b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri } 78969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka FontVariationAxis[] axes = 79069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka FontVariationAxis.fromFontVariationSettings(variationSettings); 79169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka result.add(new FontInfo(fileUri, ttcIndex, axes, weight, italic, resultCode)); 792b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri } 793b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri } 794b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri } 79569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka return result.toArray(new FontInfo[0]); 796b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri } 797b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri} 798