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 java.lang.annotation.RetentionPolicy.SOURCE;
1969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
2069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport android.annotation.IntDef;
2169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport android.annotation.IntRange;
2269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport android.annotation.NonNull;
2369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport android.annotation.Nullable;
24b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.content.ContentResolver;
25b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.content.ContentUris;
26b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.content.Context;
273c4be77db95ea716889568bde853be082e764da9Clara Bayarriimport android.content.pm.PackageInfo;
2869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport android.content.pm.PackageManager.NameNotFoundException;
29b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.content.pm.PackageManager;
30b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.content.pm.ProviderInfo;
313c4be77db95ea716889568bde853be082e764da9Clara Bayarriimport android.content.pm.Signature;
32b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.database.Cursor;
333c4be77db95ea716889568bde853be082e764da9Clara Bayarriimport android.graphics.Typeface;
3469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport android.graphics.fonts.FontVariationAxis;
35b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.net.Uri;
36b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.Bundle;
3769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport android.os.CancellationSignal;
38b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.Handler;
39b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.HandlerThread;
40b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.ParcelFileDescriptor;
41b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.Process;
42b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.ResultReceiver;
435a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonakaimport android.util.ArraySet;
44b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.util.Log;
45daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonakaimport android.util.LruCache;
46b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
47b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport com.android.internal.annotations.GuardedBy;
483c4be77db95ea716889568bde853be082e764da9Clara Bayarriimport com.android.internal.annotations.VisibleForTesting;
4969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport com.android.internal.util.Preconditions;
50b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
5169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport java.io.FileInputStream;
52b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport java.io.FileNotFoundException;
53bacf2352ab892724551431551ae973ae3b68db35Clara Bayarriimport java.io.IOException;
5469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport java.lang.annotation.Retention;
5569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport java.lang.annotation.RetentionPolicy;
5669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport java.nio.ByteBuffer;
5769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport java.nio.channels.FileChannel;
58b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport java.util.ArrayList;
59fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarriimport java.util.Arrays;
60fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarriimport java.util.Collections;
61fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarriimport java.util.Comparator;
6269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport java.util.HashMap;
633c4be77db95ea716889568bde853be082e764da9Clara Bayarriimport java.util.List;
6469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonakaimport java.util.Map;
655a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonakaimport java.util.Set;
66d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonakaimport java.util.concurrent.TimeUnit;
67d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonakaimport java.util.concurrent.locks.Condition;
68d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonakaimport java.util.concurrent.locks.Lock;
69d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonakaimport java.util.concurrent.locks.ReentrantLock;
70d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonakaimport java.util.concurrent.atomic.AtomicBoolean;
71d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonakaimport java.util.concurrent.atomic.AtomicReference;
72b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
73b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri/**
74b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * Utility class to deal with Font ContentProviders.
75b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri */
76b0812a30499376e09e2deb5995e998c629f24985Clara Bayarripublic class FontsContract {
77b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    private static final String TAG = "FontsContract";
78b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
79b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    /**
80b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     * Defines the constants used in a response from a Font Provider. The cursor returned from the
81b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     * query should have the ID column populated with the content uri ID for the resulting font.
82b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     * This should point to a real file or shared memory, as the client will mmap the given file
83b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     * descriptor. Pipes, sockets and other non-mmap-able file descriptors will fail to load in the
84b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     * client application.
85b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     */
86b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    public static final class Columns implements BaseColumns {
877fea2e264e2e793586bb999ee80897d488e62141Clara Bayarri
887fea2e264e2e793586bb999ee80897d488e62141Clara Bayarri        // Do not instantiate.
897fea2e264e2e793586bb999ee80897d488e62141Clara Bayarri        private Columns() {}
907fea2e264e2e793586bb999ee80897d488e62141Clara Bayarri
91b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        /**
92b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         * Constant used to request data from a font provider. The cursor returned from the query
9343c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka         * may populate this column with a long for the font file ID. The client will request a file
9443c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka         * descriptor to "file/FILE_ID" with this ID immediately under the top-level content URI. If
9543c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka         * not present, the client will request a file descriptor to the top-level URI with the
9643c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka         * given base font ID. Note that several results may return the same file ID, e.g. for TTC
9743c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka         * files with different indices.
9843c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka         */
9943c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka        public static final String FILE_ID = "file_id";
10043c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka        /**
10143c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka         * Constant used to request data from a font provider. The cursor returned from the query
102b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         * should have this column populated with an int for the ttc index for the resulting font.
103b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         */
104b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        public static final String TTC_INDEX = "font_ttc_index";
105b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        /**
106b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         * Constant used to request data from a font provider. The cursor returned from the query
107b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         * may populate this column with the font variation settings String information for the
108b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         * font.
109b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         */
110b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        public static final String VARIATION_SETTINGS = "font_variation_settings";
111b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        /**
112bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         * Constant used to request data from a font provider. The cursor returned from the query
113fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka         * should have this column populated with the int weight for the resulting font. This value
114fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka         * should be between 100 and 900. The most common values are 400 for regular weight and 700
115fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka         * for bold weight.
116fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka         */
117fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka        public static final String WEIGHT = "font_weight";
118fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka        /**
119fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka         * Constant used to request data from a font provider. The cursor returned from the query
120fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka         * should have this column populated with the int italic for the resulting font. This should
121fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka         * be 0 for regular style and 1 for italic.
122fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka         */
123fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka        public static final String ITALIC = "font_italic";
124fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka        /**
125fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka         * Constant used to request data from a font provider. The cursor returned from the query
126bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         * should have this column populated to indicate the result status of the
127bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         * query. This will be checked before any other data in the cursor. Possible values are
128bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         * {@link #RESULT_CODE_OK}, {@link #RESULT_CODE_FONT_NOT_FOUND},
1295706a1b12dd891098fb7bf479623d42979171144Clara Bayarri         * {@link #RESULT_CODE_MALFORMED_QUERY} and {@link #RESULT_CODE_FONT_UNAVAILABLE} for system
1305706a1b12dd891098fb7bf479623d42979171144Clara Bayarri         * defined values. You may also define your own values in the 0x000010000..0xFFFF0000 range.
1315706a1b12dd891098fb7bf479623d42979171144Clara Bayarri         * If not present, {@link #RESULT_CODE_OK} will be assumed.
132bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         */
133bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri        public static final String RESULT_CODE = "result_code";
134bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri
135bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri        /**
136bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         * Constant used to represent a result was retrieved successfully. The given fonts will be
137bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         * attempted to retrieve immediately via
138bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         * {@link android.content.ContentProvider#openFile(Uri, String)}. See {@link #RESULT_CODE}.
139bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         */
140bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri        public static final int RESULT_CODE_OK = 0;
141bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri        /**
142bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         * Constant used to represent a result was not found. See {@link #RESULT_CODE}.
143bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         */
144bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri        public static final int RESULT_CODE_FONT_NOT_FOUND = 1;
145bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri        /**
146bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         * Constant used to represent a result was found, but cannot be provided at this moment. Use
147bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         * this to indicate, for example, that a font needs to be fetched from the network. See
148bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         * {@link #RESULT_CODE}.
149bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         */
150bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri        public static final int RESULT_CODE_FONT_UNAVAILABLE = 2;
151bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri        /**
152bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         * Constant used to represent that the query was not in a supported format by the provider.
153bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         * See {@link #RESULT_CODE}.
154bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri         */
155bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri        public static final int RESULT_CODE_MALFORMED_QUERY = 3;
156b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    }
157b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
15854084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka    private static final Object sLock = new Object();
15954084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka    @GuardedBy("sLock")
16054084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka    private static Handler sHandler;
16154084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka    @GuardedBy("sLock")
16254084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka    private static HandlerThread sThread;
16354084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka    @GuardedBy("sLock")
16454084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka    private static Set<String> sInQueueSet;
165b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
16654084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka    private volatile static Context sContext;  // set once in setApplicationContextForResources
167b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
168daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka    private static final LruCache<String, Typeface> sTypefaceCache = new LruCache<>(16);
169daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka
17054084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka    private FontsContract() {
17154084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka    }
17254084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka
173b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    /** @hide */
17454084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka    public static void setApplicationContextForResources(Context context) {
17554084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka        sContext = context.getApplicationContext();
176b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    }
177b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
17869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    /**
17969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * Object represent a font entry in the family returned from {@link #fetchFonts}.
18069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     */
18169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    public static class FontInfo {
18269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        private final Uri mUri;
18369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        private final int mTtcIndex;
18469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        private final FontVariationAxis[] mAxes;
18569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        private final int mWeight;
18669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        private final boolean mItalic;
18769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        private final int mResultCode;
18869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
18969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        /**
19069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * Creates a Font with all the information needed about a provided font.
19169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * @param uri A URI associated to the font file.
19269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * @param ttcIndex If providing a TTC_INDEX file, the index to point to. Otherwise, 0.
19369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * @param axes If providing a variation font, the settings for it. May be null.
19469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * @param weight An integer that indicates the font weight.
19569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * @param italic A boolean that indicates the font is italic style or not.
19669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * @param resultCode A boolean that indicates the font contents is ready.
19769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         */
19869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        /** @hide */
19969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        public FontInfo(@NonNull Uri uri, @IntRange(from = 0) int ttcIndex,
20069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                @Nullable FontVariationAxis[] axes, @IntRange(from = 1, to = 1000) int weight,
20169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                boolean italic, int resultCode) {
20269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            mUri = Preconditions.checkNotNull(uri);
20369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            mTtcIndex = ttcIndex;
20469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            mAxes = axes;
20569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            mWeight = weight;
20669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            mItalic = italic;
20769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            mResultCode = resultCode;
20869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        }
20969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
21069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        /**
21169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * Returns a URI associated to this record.
21269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         */
21369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        public @NonNull Uri getUri() {
21469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            return mUri;
21569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        }
21669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
21769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        /**
21869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * Returns the index to be used to access this font when accessing a TTC file.
21969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         */
22069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        public @IntRange(from = 0) int getTtcIndex() {
22169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            return mTtcIndex;
22269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        }
22369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
22469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        /**
22569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * Returns the list of axes associated to this font.
22669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         */
22769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        public @Nullable FontVariationAxis[] getAxes() {
22869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            return mAxes;
22969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        }
23069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
23169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        /**
23269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * Returns the weight value for this font.
23369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         */
23469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        public @IntRange(from = 1, to = 1000) int getWeight() {
23569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            return mWeight;
23669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        }
23769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
23869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        /**
23969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * Returns whether this font is italic.
24069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         */
24169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        public boolean isItalic() {
24269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            return mItalic;
24369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        }
24469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
24569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        /**
24669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * Returns result code.
24769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         *
24869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * {@link FontsContract.Columns#RESULT_CODE}
24969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         */
25069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        public int getResultCode() {
25169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            return mResultCode;
25269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        }
25369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    }
25469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
25569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    /**
25669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * Object returned from {@link #fetchFonts}.
25769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     */
25869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    public static class FontFamilyResult {
25969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        /**
26069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * Constant represents that the font was successfully retrieved. Note that when this value
26169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * is set and {@link #getFonts} returns an empty array, it means there were no fonts
26269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * matching the given query.
26369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         */
26469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        public static final int STATUS_OK = 0;
26569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
26669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        /**
26769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * Constant represents that the given certificate was not matched with the provider's
26869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * signature. {@link #getFonts} returns null if this status was set.
26969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         */
27069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        public static final int STATUS_WRONG_CERTIFICATES = 1;
27169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
27269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        /**
27369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * Constant represents that the provider returns unexpected data. {@link #getFonts} returns
27469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * null if this status was set. For example, this value is set when the font provider
27569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         * gives invalid format of variation settings.
27669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka         */
27769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        public static final int STATUS_UNEXPECTED_DATA_PROVIDED = 2;
27869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
279ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka        /**
280ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka         * Constant represents that the fetching font data was rejected by system. This happens if
281ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka         * the passed context is restricted.
282ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka         */
283ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka        public static final int STATUS_REJECTED = 3;
284ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka
28569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        /** @hide */
286ce8db9911494225fcd99711d7df85a130de5a6ceJeff Sharkey        @IntDef(prefix = { "STATUS_" }, value = {
287ce8db9911494225fcd99711d7df85a130de5a6ceJeff Sharkey                STATUS_OK,
288ce8db9911494225fcd99711d7df85a130de5a6ceJeff Sharkey                STATUS_WRONG_CERTIFICATES,
289ce8db9911494225fcd99711d7df85a130de5a6ceJeff Sharkey                STATUS_UNEXPECTED_DATA_PROVIDED
290ce8db9911494225fcd99711d7df85a130de5a6ceJeff Sharkey        })
29169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        @Retention(RetentionPolicy.SOURCE)
29269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        @interface FontResultStatus {}
29369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
29469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        private final @FontResultStatus int mStatusCode;
29569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        private final FontInfo[] mFonts;
29669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
29769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        /** @hide */
29869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        public FontFamilyResult(@FontResultStatus int statusCode, @Nullable FontInfo[] fonts) {
29969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            mStatusCode = statusCode;
30069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            mFonts = fonts;
30169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        }
30269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
30369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        public @FontResultStatus int getStatusCode() {
30469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            return mStatusCode;
30569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        }
30669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
30769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        public @NonNull FontInfo[] getFonts() {
30869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            return mFonts;
30969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        }
3103c4be77db95ea716889568bde853be082e764da9Clara Bayarri    }
3113c4be77db95ea716889568bde853be082e764da9Clara Bayarri
31254084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka    private static final int THREAD_RENEWAL_THRESHOLD_MS = 10000;
31354084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka
314d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka    private static final long SYNC_FONT_FETCH_TIMEOUT_MS = 500;
315d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka
316b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    // We use a background thread to post the content resolving work for all requests on. This
317b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    // thread should be quit/stopped after all requests are done.
31854084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka    // TODO: Factor out to other class. Consider to switch MessageQueue.IdleHandler.
31954084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka    private static final Runnable sReplaceDispatcherThreadRunnable = new Runnable() {
320b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        @Override
321b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        public void run() {
32254084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka            synchronized (sLock) {
32354084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka                if (sThread != null) {
32454084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka                    sThread.quitSafely();
32554084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka                    sThread = null;
32654084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka                    sHandler = null;
327b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                }
328b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            }
329b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        }
330b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    };
331b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
332bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri    /** @hide */
333d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka    public static Typeface getFontSync(FontRequest request) {
3345a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka        final String id = request.getIdentifier();
3355a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka        Typeface cachedTypeface = sTypefaceCache.get(id);
3365a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka        if (cachedTypeface != null) {
3375a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka            return cachedTypeface;
3385a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka        }
3395a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka
3405a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka        // Unfortunately the typeface is not available at this time, but requesting from the font
3415a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka        // provider takes too much time. For now, request the font data to ensure it is in the cache
3425a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka        // next time and return.
34354084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka        synchronized (sLock) {
34454084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka            if (sHandler == null) {
34554084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka                sThread = new HandlerThread("fonts", Process.THREAD_PRIORITY_BACKGROUND);
34654084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka                sThread.start();
34754084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka                sHandler = new Handler(sThread.getLooper());
3485a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka            }
349d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka            final Lock lock = new ReentrantLock();
350d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka            final Condition cond = lock.newCondition();
351d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka            final AtomicReference<Typeface> holder = new AtomicReference<>();
352d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka            final AtomicBoolean waiting = new AtomicBoolean(true);
353d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka            final AtomicBoolean timeout = new AtomicBoolean(false);
354d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka
35554084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka            sHandler.post(() -> {
35669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                try {
35754084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka                    FontFamilyResult result = fetchFonts(sContext, null, request);
3585a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka                    if (result.getStatusCode() == FontFamilyResult.STATUS_OK) {
35954084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka                        Typeface typeface = buildTypeface(sContext, null, result.getFonts());
3605a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka                        if (typeface != null) {
3615a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka                            sTypefaceCache.put(id, typeface);
36269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                        }
363d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                        holder.set(typeface);
36469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                    }
3655a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka                } catch (NameNotFoundException e) {
3665a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka                    // Ignore.
36769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                }
368d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                lock.lock();
369d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                try {
370d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                    if (!timeout.get()) {
371d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                      waiting.set(false);
372d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                      cond.signal();
373d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                    }
374d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                } finally {
375d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                    lock.unlock();
376d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                }
377b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            });
37854084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka            sHandler.removeCallbacks(sReplaceDispatcherThreadRunnable);
37954084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka            sHandler.postDelayed(sReplaceDispatcherThreadRunnable, THREAD_RENEWAL_THRESHOLD_MS);
380d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka
381d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka            long remaining = TimeUnit.MILLISECONDS.toNanos(SYNC_FONT_FETCH_TIMEOUT_MS);
382d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka            lock.lock();
383d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka            try {
384d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                if (!waiting.get()) {
385d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                    return holder.get();
386d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                }
387d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                for (;;) {
388d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                    try {
389d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                        remaining = cond.awaitNanos(remaining);
390d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                    } catch (InterruptedException e) {
391d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                        // do nothing.
392d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                    }
393d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                    if (!waiting.get()) {
394d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                        return holder.get();
395d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                    }
396d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                    if (remaining <= 0) {
397d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                        timeout.set(true);
398d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                        Log.w(TAG, "Remote font fetch timed out: " +
399d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                                request.getProviderAuthority() + "/" + request.getQuery());
400d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                        return null;
401d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                    }
402d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                }
403d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka            } finally {
404d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                lock.unlock();
405d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka            }
406b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        }
407b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    }
408b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
40969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    /**
4100b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka     * Interface used to receive asynchronously fetched typefaces.
4110b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka     */
4120b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka    public static class FontRequestCallback {
4130b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        /**
4140b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
4150b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         * provider was not found on the device.
4160b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         */
41754084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka        public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1;
4180b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        /**
4190b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
4200b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         * provider must be authenticated and the given certificates do not match its signature.
4210b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         */
42254084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka        public static final int FAIL_REASON_WRONG_CERTIFICATES = -2;
4230b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        /**
4240b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font
4250b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         * returned by the provider was not loaded properly.
4260b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         */
4270b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        public static final int FAIL_REASON_FONT_LOAD_ERROR = -3;
4280b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        /**
4290b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font
4300b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         * provider did not return any results for the given query.
4310b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         */
4320b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        public static final int FAIL_REASON_FONT_NOT_FOUND = Columns.RESULT_CODE_FONT_NOT_FOUND;
4330b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        /**
4340b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font
4350b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         * provider found the queried font, but it is currently unavailable.
4360b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         */
4370b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        public static final int FAIL_REASON_FONT_UNAVAILABLE = Columns.RESULT_CODE_FONT_UNAVAILABLE;
4380b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        /**
4390b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
4400b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         * query was not supported by the provider.
4410b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         */
4420b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        public static final int FAIL_REASON_MALFORMED_QUERY = Columns.RESULT_CODE_MALFORMED_QUERY;
4430b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka
4440b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        /** @hide */
445ce8db9911494225fcd99711d7df85a130de5a6ceJeff Sharkey        @IntDef(prefix = { "FAIL_" }, value = {
446ce8db9911494225fcd99711d7df85a130de5a6ceJeff Sharkey                FAIL_REASON_PROVIDER_NOT_FOUND,
447ce8db9911494225fcd99711d7df85a130de5a6ceJeff Sharkey                FAIL_REASON_FONT_LOAD_ERROR,
448ce8db9911494225fcd99711d7df85a130de5a6ceJeff Sharkey                FAIL_REASON_FONT_NOT_FOUND,
449ce8db9911494225fcd99711d7df85a130de5a6ceJeff Sharkey                FAIL_REASON_FONT_UNAVAILABLE,
450ce8db9911494225fcd99711d7df85a130de5a6ceJeff Sharkey                FAIL_REASON_MALFORMED_QUERY
451ce8db9911494225fcd99711d7df85a130de5a6ceJeff Sharkey        })
4520b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        @Retention(RetentionPolicy.SOURCE)
4530b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        @interface FontRequestFailReason {}
4540b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka
4550b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        public FontRequestCallback() {}
4560b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka
4570b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        /**
45867f9d5070a74a0bf34f0335899a96dedcac26c96Jeff Sharkey         * Called then a Typeface request done via {@link #requestFonts} is complete. Note that this
4595a09c64345ba35783b5d24ed7c4ca0ea8afbcc0aSeigo Nonaka         * method will not be called if {@link #onTypefaceRequestFailed(int)} is called instead.
4600b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         * @param typeface  The Typeface object retrieved.
4610b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         */
4620b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        public void onTypefaceRetrieved(Typeface typeface) {}
4630b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka
4640b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        /**
46567f9d5070a74a0bf34f0335899a96dedcac26c96Jeff Sharkey         * Called when a Typeface request done via {@link #requestFonts}} fails.
4660b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         * @param reason One of {@link #FAIL_REASON_PROVIDER_NOT_FOUND},
4670b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         *               {@link #FAIL_REASON_FONT_NOT_FOUND},
4680b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         *               {@link #FAIL_REASON_FONT_LOAD_ERROR},
4690b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         *               {@link #FAIL_REASON_FONT_UNAVAILABLE} or
4705706a1b12dd891098fb7bf479623d42979171144Clara Bayarri         *               {@link #FAIL_REASON_MALFORMED_QUERY} if returned by the system. May also be
4715706a1b12dd891098fb7bf479623d42979171144Clara Bayarri         *               a positive value greater than 0 defined by the font provider as an
4725706a1b12dd891098fb7bf479623d42979171144Clara Bayarri         *               additional error code. Refer to the provider's documentation for more
4735706a1b12dd891098fb7bf479623d42979171144Clara Bayarri         *               information on possible returned error codes.
4740b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka         */
4750b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        public void onTypefaceRequestFailed(@FontRequestFailReason int reason) {}
4760b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka    }
4770b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka
4780b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka    /**
4790b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka     * Create a typeface object given a font request. The font will be asynchronously fetched,
4800b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka     * therefore the result is delivered to the given callback. See {@link FontRequest}.
4810b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka     * Only one of the methods in callback will be invoked, depending on whether the request
4820b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka     * succeeds or fails. These calls will happen on the caller thread.
483daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka     *
484daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka     * Note that the result Typeface may be cached internally and the same instance will be returned
485daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka     * the next time you call this method with the same request. If you want to bypass this cache,
486daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka     * use {@link #fetchFonts} and {@link #buildTypeface} instead.
487daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka     *
4880b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka     * @param context A context to be used for fetching from font provider.
4890b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka     * @param request A {@link FontRequest} object that identifies the provider and query for the
4900b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka     *                request. May not be null.
4910b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka     * @param handler A handler to be processed the font fetching.
4928ea62b036dd28231cb07beb210d361d9341c618eSeigo Nonaka     * @param cancellationSignal A signal to cancel the operation in progress, or null if none. If
4938ea62b036dd28231cb07beb210d361d9341c618eSeigo Nonaka     *                           the operation is canceled, then {@link
4948ea62b036dd28231cb07beb210d361d9341c618eSeigo Nonaka     *                           android.os.OperationCanceledException} will be thrown.
4958ea62b036dd28231cb07beb210d361d9341c618eSeigo Nonaka     * @param callback A callback that will be triggered when results are obtained. May not be null.
4960b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka     */
4978ea62b036dd28231cb07beb210d361d9341c618eSeigo Nonaka    public static void requestFonts(@NonNull Context context, @NonNull FontRequest request,
4988ea62b036dd28231cb07beb210d361d9341c618eSeigo Nonaka            @NonNull Handler handler, @Nullable CancellationSignal cancellationSignal,
4998ea62b036dd28231cb07beb210d361d9341c618eSeigo Nonaka            @NonNull FontRequestCallback callback) {
5000b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka
5010b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        final Handler callerThreadHandler = new Handler();
502daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka        final Typeface cachedTypeface = sTypefaceCache.get(request.getIdentifier());
503daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka        if (cachedTypeface != null) {
504daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka            callerThreadHandler.post(() -> callback.onTypefaceRetrieved(cachedTypeface));
505daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka            return;
506daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka        }
507daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka
5080b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        handler.post(() -> {
5090b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka            FontFamilyResult result;
5100b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka            try {
5118ea62b036dd28231cb07beb210d361d9341c618eSeigo Nonaka                result = fetchFonts(context, cancellationSignal, request);
5120b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka            } catch (NameNotFoundException e) {
5130b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                callerThreadHandler.post(() -> callback.onTypefaceRequestFailed(
5140b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                        FontRequestCallback.FAIL_REASON_PROVIDER_NOT_FOUND));
5150b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                return;
5160b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka            }
5170b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka
518daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka            // Same request might be dispatched during fetchFonts. Check the cache again.
519daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka            final Typeface anotherCachedTypeface = sTypefaceCache.get(request.getIdentifier());
520daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka            if (anotherCachedTypeface != null) {
521daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka                callerThreadHandler.post(() -> callback.onTypefaceRetrieved(anotherCachedTypeface));
522daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka                return;
523daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka            }
524daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka
5250b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka            if (result.getStatusCode() != FontFamilyResult.STATUS_OK) {
5260b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                switch (result.getStatusCode()) {
5270b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                    case FontFamilyResult.STATUS_WRONG_CERTIFICATES:
5280b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                        callerThreadHandler.post(() -> callback.onTypefaceRequestFailed(
5290b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                                FontRequestCallback.FAIL_REASON_WRONG_CERTIFICATES));
5300b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                        return;
5310b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                    case FontFamilyResult.STATUS_UNEXPECTED_DATA_PROVIDED:
5320b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                        callerThreadHandler.post(() -> callback.onTypefaceRequestFailed(
5330b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                                FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR));
5340b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                        return;
5350b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                    default:
5360b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                        // fetchFont returns unexpected status type. Fallback to load error.
5370b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                        callerThreadHandler.post(() -> callback.onTypefaceRequestFailed(
5380b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                                FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR));
5390b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                        return;
5400b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                }
5410b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka            }
5420b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka
5430b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka            final FontInfo[] fonts = result.getFonts();
5440b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka            if (fonts == null || fonts.length == 0) {
5450b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                callerThreadHandler.post(() -> callback.onTypefaceRequestFailed(
5460b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                        FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND));
5470b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                return;
5480b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka            }
5490b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka            for (final FontInfo font : fonts) {
5500b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                if (font.getResultCode() != Columns.RESULT_CODE_OK) {
5510b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                    // We proceed if all font entry is ready to use. Otherwise report the first
5520b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                    // error.
5530b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                    final int resultCode = font.getResultCode();
5540b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                    if (resultCode < 0) {
5550b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                        // Negative values are reserved for internal errors. Fallback to load error.
5560b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                        callerThreadHandler.post(() -> callback.onTypefaceRequestFailed(
5570b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                                FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR));
5580b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                    } else {
5590b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                        callerThreadHandler.post(() -> callback.onTypefaceRequestFailed(
5600b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                                resultCode));
5610b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                    }
5620b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                    return;
5630b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                }
5640b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka            }
5650b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka
5668ea62b036dd28231cb07beb210d361d9341c618eSeigo Nonaka            final Typeface typeface = buildTypeface(context, cancellationSignal, fonts);
5670b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka            if (typeface == null) {
5680b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                // Something went wrong during reading font files. This happens if the given font
5690b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                // file is an unsupported font type.
5700b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                callerThreadHandler.post(() -> callback.onTypefaceRequestFailed(
5710b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                        FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR));
5720b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka                return;
5730b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka            }
5740b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka
575daa8dfc690c4755510ccc15da5ed5bbdd9731fa4Seigo Nonaka            sTypefaceCache.put(request.getIdentifier(), typeface);
5760b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka            callerThreadHandler.post(() -> callback.onTypefaceRetrieved(typeface));
5770b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka        });
5780b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka    }
5790b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka
5800b73a4287e8f630c45bf7ef0a7e0b2541d6b4067Seigo Nonaka    /**
58169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * Fetch fonts given a font request.
58269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     *
58369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * @param context A {@link Context} to be used for fetching fonts.
58469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * @param cancellationSignal A signal to cancel the operation in progress, or null if none. If
58569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     *                           the operation is canceled, then {@link
58669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     *                           android.os.OperationCanceledException} will be thrown when the
58769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     *                           query is executed.
58869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * @param request A {@link FontRequest} object that identifies the provider and query for the
58969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     *                request.
59069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     *
59169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * @return {@link FontFamilyResult}
59269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     *
59369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * @throws NameNotFoundException If requested package or authority was not found in system.
59469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     */
59569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    public static @NonNull FontFamilyResult fetchFonts(
59669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            @NonNull Context context, @Nullable CancellationSignal cancellationSignal,
59769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            @NonNull FontRequest request) throws NameNotFoundException {
598ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka        if (context.isRestricted()) {
599ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka            // TODO: Should we allow if the peer process is system or myself?
600ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka            return new FontFamilyResult(FontFamilyResult.STATUS_REJECTED, null);
601ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka        }
60269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        ProviderInfo providerInfo = getProvider(context.getPackageManager(), request);
60369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        if (providerInfo == null) {
60469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            return new FontFamilyResult(FontFamilyResult.STATUS_WRONG_CERTIFICATES, null);
60569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
60669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        }
60769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        try {
60869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            FontInfo[] fonts = getFontFromProvider(
60969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                    context, request, providerInfo.authority, cancellationSignal);
61069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            return new FontFamilyResult(FontFamilyResult.STATUS_OK, fonts);
6110f07dd5b14e76695bbae758e948e00b86935232cSeigo Nonaka        } catch (IllegalArgumentException e) {
61269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            return new FontFamilyResult(FontFamilyResult.STATUS_UNEXPECTED_DATA_PROVIDED, null);
61369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        }
61469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    }
61569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
61669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    /**
61769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * Build a Typeface from an array of {@link FontInfo}
61869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     *
61969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * Results that are marked as not ready will be skipped.
62069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     *
62169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * @param context A {@link Context} that will be used to fetch the font contents.
62269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * @param cancellationSignal A signal to cancel the operation in progress, or null if none. If
62369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     *                           the operation is canceled, then {@link
62469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     *                           android.os.OperationCanceledException} will be thrown.
62569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * @param fonts An array of {@link FontInfo} to be used to create a Typeface.
62669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * @return A Typeface object. Returns null if typeface creation fails.
62769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     */
62869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    public static Typeface buildTypeface(@NonNull Context context,
62969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts) {
630ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka        if (context.isRestricted()) {
631ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka            // TODO: Should we allow if the peer process is system or myself?
632ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka            return null;
633ebecd7e802cd65bfc5cf1d8de1e6ea031580ae79Seigo Nonaka        }
63469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        final Map<Uri, ByteBuffer> uriBuffer =
63569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                prepareFontData(context, fonts, cancellationSignal);
636d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka        if (uriBuffer.isEmpty()) {
637d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka            return null;
638d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka        }
63969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        return new Typeface.Builder(fonts, uriBuffer).build();
64069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    }
64169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
64269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    /**
64369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * A helper function to create a mapping from {@link Uri} to {@link ByteBuffer}.
64469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     *
64569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * Skip if the file contents is not ready to be read.
64669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     *
64769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * @param context A {@link Context} to be used for resolving content URI in
64869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     *                {@link FontInfo}.
64969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * @param fonts An array of {@link FontInfo}.
65069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     * @return A map from {@link Uri} to {@link ByteBuffer}.
65169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka     */
65269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    private static Map<Uri, ByteBuffer> prepareFontData(Context context, FontInfo[] fonts,
65369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            CancellationSignal cancellationSignal) {
65469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        final HashMap<Uri, ByteBuffer> out = new HashMap<>();
65569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        final ContentResolver resolver = context.getContentResolver();
65669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
65769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        for (FontInfo font : fonts) {
65869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            if (font.getResultCode() != Columns.RESULT_CODE_OK) {
65969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                continue;
66069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            }
66169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
66269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            final Uri uri = font.getUri();
66369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            if (out.containsKey(uri)) {
66469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                continue;
66569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            }
66669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
66769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            ByteBuffer buffer = null;
66869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            try (final ParcelFileDescriptor pfd =
669d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                    resolver.openFileDescriptor(uri, "r", cancellationSignal)) {
670d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                if (pfd != null) {
671d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                    try (final FileInputStream fis =
672d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                            new FileInputStream(pfd.getFileDescriptor())) {
673d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                        final FileChannel fileChannel = fis.getChannel();
674d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                        final long size = fileChannel.size();
675d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                        buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);
676d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                    } catch (IOException e) {
677d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                        // ignore
678d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                    }
679d9de8be233d18ccc881cb647a5de2b32ff3e737cSeigo Nonaka                }
68069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            } catch (IOException e) {
68169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                // ignore
68269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            }
68369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
68469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            // TODO: try other approach?, e.g. read all contents instead of mmap.
68569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
68669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            out.put(uri, buffer);
68769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        }
68869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        return Collections.unmodifiableMap(out);
68969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    }
69069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
6913c4be77db95ea716889568bde853be082e764da9Clara Bayarri    /** @hide */
6923c4be77db95ea716889568bde853be082e764da9Clara Bayarri    @VisibleForTesting
69369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    public static @Nullable ProviderInfo getProvider(
69469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            PackageManager packageManager, FontRequest request) throws NameNotFoundException {
6953c4be77db95ea716889568bde853be082e764da9Clara Bayarri        String providerAuthority = request.getProviderAuthority();
69669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        ProviderInfo info = packageManager.resolveContentProvider(providerAuthority, 0);
6973c4be77db95ea716889568bde853be082e764da9Clara Bayarri        if (info == null) {
69869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            throw new NameNotFoundException("No package found for authority: " + providerAuthority);
6993c4be77db95ea716889568bde853be082e764da9Clara Bayarri        }
7003c4be77db95ea716889568bde853be082e764da9Clara Bayarri
7013c4be77db95ea716889568bde853be082e764da9Clara Bayarri        if (!info.packageName.equals(request.getProviderPackage())) {
70269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            throw new NameNotFoundException("Found content provider " + providerAuthority
70369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                    + ", but package was not " + request.getProviderPackage());
7043c4be77db95ea716889568bde853be082e764da9Clara Bayarri        }
7053c4be77db95ea716889568bde853be082e764da9Clara Bayarri        // Trust system apps without signature checks
7063c4be77db95ea716889568bde853be082e764da9Clara Bayarri        if (info.applicationInfo.isSystemApp()) {
7073c4be77db95ea716889568bde853be082e764da9Clara Bayarri            return info;
7083c4be77db95ea716889568bde853be082e764da9Clara Bayarri        }
7093c4be77db95ea716889568bde853be082e764da9Clara Bayarri
710fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri        List<byte[]> signatures;
71169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        PackageInfo packageInfo = packageManager.getPackageInfo(info.packageName,
71269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                PackageManager.GET_SIGNATURES);
71369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        signatures = convertToByteArrayList(packageInfo.signatures);
71469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        Collections.sort(signatures, sByteArrayComparator);
71569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
7163c4be77db95ea716889568bde853be082e764da9Clara Bayarri        List<List<byte[]>> requestCertificatesList = request.getCertificates();
7173c4be77db95ea716889568bde853be082e764da9Clara Bayarri        for (int i = 0; i < requestCertificatesList.size(); ++i) {
718fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri            // Make a copy so we can sort it without modifying the incoming data.
719fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri            List<byte[]> requestSignatures = new ArrayList<>(requestCertificatesList.get(i));
720fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri            Collections.sort(requestSignatures, sByteArrayComparator);
721fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri            if (equalsByteArrayList(signatures, requestSignatures)) {
7223c4be77db95ea716889568bde853be082e764da9Clara Bayarri                return info;
7233c4be77db95ea716889568bde853be082e764da9Clara Bayarri            }
7243c4be77db95ea716889568bde853be082e764da9Clara Bayarri        }
7253c4be77db95ea716889568bde853be082e764da9Clara Bayarri        return null;
7263c4be77db95ea716889568bde853be082e764da9Clara Bayarri    }
7273c4be77db95ea716889568bde853be082e764da9Clara Bayarri
728fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri    private static final Comparator<byte[]> sByteArrayComparator = (l, r) -> {
729fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri        if (l.length != r.length) {
730fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri            return l.length - r.length;
7313c4be77db95ea716889568bde853be082e764da9Clara Bayarri        }
732fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri        for (int i = 0; i < l.length; ++i) {
733fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri            if (l[i] != r[i]) {
734fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri                return l[i] - r[i];
735fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri            }
736fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri        }
737fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri        return 0;
738fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri    };
739fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri
74069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    private static boolean equalsByteArrayList(
74169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            List<byte[]> signatures, List<byte[]> requestSignatures) {
742fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri        if (signatures.size() != requestSignatures.size()) {
743fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri            return false;
744fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri        }
745fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri        for (int i = 0; i < signatures.size(); ++i) {
746fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri            if (!Arrays.equals(signatures.get(i), requestSignatures.get(i))) {
747fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri                return false;
748fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri            }
749fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri        }
750fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri        return true;
7513c4be77db95ea716889568bde853be082e764da9Clara Bayarri    }
7523c4be77db95ea716889568bde853be082e764da9Clara Bayarri
75369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    private static List<byte[]> convertToByteArrayList(Signature[] signatures) {
754fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri        List<byte[]> shas = new ArrayList<>();
755fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri        for (int i = 0; i < signatures.length; ++i) {
756fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri            shas.add(signatures[i].toByteArray());
757fb483cc90c27a9c0fcafa28343a8fd644f8384a4Clara Bayarri        }
7583c4be77db95ea716889568bde853be082e764da9Clara Bayarri        return shas;
7593c4be77db95ea716889568bde853be082e764da9Clara Bayarri    }
7603c4be77db95ea716889568bde853be082e764da9Clara Bayarri
7613c4be77db95ea716889568bde853be082e764da9Clara Bayarri    /** @hide */
7623c4be77db95ea716889568bde853be082e764da9Clara Bayarri    @VisibleForTesting
76369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka    public static @NonNull FontInfo[] getFontFromProvider(
76469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka            Context context, FontRequest request, String authority,
7650f07dd5b14e76695bbae758e948e00b86935232cSeigo Nonaka            CancellationSignal cancellationSignal) {
76669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        ArrayList<FontInfo> result = new ArrayList<>();
76743c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka        final Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
7683c4be77db95ea716889568bde853be082e764da9Clara Bayarri                .authority(authority)
769b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                .build();
77043c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka        final Uri fileBaseUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
77143c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka                .authority(authority)
77243c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka                .appendPath("file")
77343c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka                .build();
77469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        try (Cursor cursor = context.getContentResolver().query(uri, new String[] { Columns._ID,
77543c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka                        Columns.FILE_ID, Columns.TTC_INDEX, Columns.VARIATION_SETTINGS,
77654084b64b1860b652b1c50ba942b4cfc7fb28805Seigo Nonaka                        Columns.WEIGHT, Columns.ITALIC, Columns.RESULT_CODE },
77769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                "query = ?", new String[] { request.getQuery() }, null, cancellationSignal);) {
778b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            // TODO: Should we restrict the amount of fonts that can be returned?
779b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            // TODO: Write documentation explaining that all results should be from the same family.
780b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            if (cursor != null && cursor.getCount() > 0) {
781bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri                final int resultCodeColumnIndex = cursor.getColumnIndex(Columns.RESULT_CODE);
782b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                result = new ArrayList<>();
783bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri                final int idColumnIndex = cursor.getColumnIndexOrThrow(Columns._ID);
78443c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka                final int fileIdColumnIndex = cursor.getColumnIndex(Columns.FILE_ID);
785b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                final int ttcIndexColumnIndex = cursor.getColumnIndex(Columns.TTC_INDEX);
786b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                final int vsColumnIndex = cursor.getColumnIndex(Columns.VARIATION_SETTINGS);
787fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka                final int weightColumnIndex = cursor.getColumnIndex(Columns.WEIGHT);
788fe04aa840e0682e9813e1ac4958772f898eb02caSeigo Nonaka                final int italicColumnIndex = cursor.getColumnIndex(Columns.ITALIC);
789b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                while (cursor.moveToNext()) {
79069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                    int resultCode = resultCodeColumnIndex != -1
791bacf2352ab892724551431551ae973ae3b68db35Clara Bayarri                            ? cursor.getInt(resultCodeColumnIndex) : Columns.RESULT_CODE_OK;
79269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                    final int ttcIndex = ttcIndexColumnIndex != -1
79369754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                            ? cursor.getInt(ttcIndexColumnIndex) : 0;
79469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                    final String variationSettings = vsColumnIndex != -1
79569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                            ? cursor.getString(vsColumnIndex) : null;
79669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka
79743c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka                    Uri fileUri;
79843c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka                    if (fileIdColumnIndex == -1) {
79943c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka                        long id = cursor.getLong(idColumnIndex);
80043c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka                        fileUri = ContentUris.withAppendedId(uri, id);
80143c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka                    } else {
80243c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka                        long id = cursor.getLong(fileIdColumnIndex);
80343c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka                        fileUri = ContentUris.withAppendedId(fileBaseUri, id);
80443c20cf6d4bd661d85bed76c78953aa656dbcc62Seigo Nonaka                    }
80569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                    int weight;
80669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                    boolean italic;
80769754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                    if (weightColumnIndex != -1 && italicColumnIndex != -1) {
80869754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                        weight = cursor.getInt(weightColumnIndex);
80969754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                        italic = cursor.getInt(italicColumnIndex) == 1;
81069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                    } else {
81169754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                        weight = Typeface.Builder.NORMAL_WEIGHT;
81269754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                        italic = false;
813b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                    }
81469754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                    FontVariationAxis[] axes =
81569754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                            FontVariationAxis.fromFontVariationSettings(variationSettings);
81669754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka                    result.add(new FontInfo(fileUri, ttcIndex, axes, weight, italic, resultCode));
817b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                }
818b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            }
819b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        }
82069754bf66dae9d047d5a0ff2c71820aa35b9cc70Seigo Nonaka        return result.toArray(new FontInfo[0]);
821b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    }
822b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri}
823