Typeface.java revision 83ba4f9a04833409080d97480a665e029ee71365
1f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa/*
2f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa * Copyright (C) 2006 The Android Open Source Project
3f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa *
4f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa * Licensed under the Apache License, Version 2.0 (the "License");
5f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa * you may not use this file except in compliance with the License.
6f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa * You may obtain a copy of the License at
7f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa *
8f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa *      http://www.apache.org/licenses/LICENSE-2.0
9f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa *
10f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa * Unless required by applicable law or agreed to in writing, software
11f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa * distributed under the License is distributed on an "AS IS" BASIS,
12f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa * See the License for the specific language governing permissions and
14f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa * limitations under the License.
15f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa */
16f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa
17517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasapackage android.graphics;
18517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
19517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport static android.content.res.FontResourcesParser.ProviderResourceEntry;
200d3a5edf232916e81adbc46fc0f4a1753166b066Pawin Vongmasaimport static android.content.res.FontResourcesParser.FontFileResourceEntry;
210d3a5edf232916e81adbc46fc0f4a1753166b066Pawin Vongmasaimport static android.content.res.FontResourcesParser.FontFamilyFilesResourceEntry;
220d3a5edf232916e81adbc46fc0f4a1753166b066Pawin Vongmasaimport static android.content.res.FontResourcesParser.FamilyResourceEntry;
230d3a5edf232916e81adbc46fc0f4a1753166b066Pawin Vongmasa
240d3a5edf232916e81adbc46fc0f4a1753166b066Pawin Vongmasaimport static java.lang.annotation.RetentionPolicy.SOURCE;
25517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
26f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasaimport android.annotation.IntDef;
27f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasaimport android.annotation.IntRange;
28517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport android.annotation.NonNull;
29f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasaimport android.annotation.Nullable;
30f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasaimport android.content.Context;
31f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasaimport android.content.res.AssetManager;
32517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport android.graphics.FontListParser;
33517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport android.graphics.fonts.FontRequest;
3404563aafa0debc95d50951ca944abf37ef2777cePawin Vongmasaimport android.graphics.fonts.FontResult;
35ac7d4125516299b8a3e6f2b25822a692bdd96311Pawin Vongmasaimport android.graphics.fonts.FontVariationAxis;
360d3a5edf232916e81adbc46fc0f4a1753166b066Pawin Vongmasaimport android.graphics.fonts.FontVariationAxis.InvalidFormatException;
37ac7d4125516299b8a3e6f2b25822a692bdd96311Pawin Vongmasaimport android.net.Uri;
38517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport android.os.Bundle;
390d3a5edf232916e81adbc46fc0f4a1753166b066Pawin Vongmasaimport android.os.Handler;
40517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport android.os.ParcelFileDescriptor;
41517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport android.os.ResultReceiver;
42517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport android.provider.FontsContract;
43517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport android.text.FontConfig;
44517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport android.util.Base64;
45517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport android.util.Log;
46517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport android.util.LongSparseArray;
470d3a5edf232916e81adbc46fc0f4a1753166b066Pawin Vongmasaimport android.util.LruCache;
480d3a5edf232916e81adbc46fc0f4a1753166b066Pawin Vongmasaimport android.util.SparseArray;
490d3a5edf232916e81adbc46fc0f4a1753166b066Pawin Vongmasa
50517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport com.android.internal.annotations.GuardedBy;
51517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport com.android.internal.util.Preconditions;
52517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
53517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport libcore.io.IoUtils;
54517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
55eeac97b18ca5c939bf2ac59334d36d54f705af3dPawin Vongmasaimport org.xmlpull.v1.XmlPullParserException;
56517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
57517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport java.io.File;
58517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport java.io.FileDescriptor;
59517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport java.io.FileInputStream;
60f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasaimport java.io.FileNotFoundException;
61517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport java.io.IOException;
62517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport java.lang.annotation.Retention;
63517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport java.lang.annotation.RetentionPolicy;
64517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport java.nio.ByteBuffer;
65517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport java.nio.channels.FileChannel;
66517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport java.util.Arrays;
67517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport java.util.ArrayList;
68517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport java.util.Arrays;
69517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport java.util.Collections;
70517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport java.util.HashMap;
71517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasaimport java.util.List;
72f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasaimport java.util.Map;
73f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasaimport java.util.concurrent.atomic.AtomicReference;
74517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
75517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa/**
76517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa * The Typeface class specifies the typeface and intrinsic style of a font.
77517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa * This is used in the paint, along with optionally Paint settings like
78517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa * textSize, textSkewX, textScaleX to specify
79517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa * how text appears when drawn (and measured).
80517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa */
81f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasapublic class Typeface {
82f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa
83f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    private static String TAG = "Typeface";
84517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
85517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /** The default NORMAL typeface object */
86517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public static final Typeface DEFAULT;
87517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /**
88517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * The default BOLD typeface object. Note: this may be not actually be
89517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * bold, depending on what fonts are installed. Call getStyle() to know
90517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * for sure.
91517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     */
92517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public static final Typeface DEFAULT_BOLD;
93517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /** The NORMAL style of the default sans serif typeface. */
94517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public static final Typeface SANS_SERIF;
95517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /** The NORMAL style of the default serif typeface. */
96517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public static final Typeface SERIF;
97517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /** The NORMAL style of the default monospace typeface. */
98517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public static final Typeface MONOSPACE;
99517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
100517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    static Typeface[] sDefaults;
101517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache =
102517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            new LongSparseArray<>(3);
103517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    @GuardedBy("sLock")
104517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    private static FontsContract sFontsContract;
105517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    @GuardedBy("sLock")
106517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    private static Handler sHandler;
107517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
108517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /**
109517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * Cache for Typeface objects dynamically loaded from assets. Currently max size is 16.
110517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     */
111517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    @GuardedBy("sLock")
112517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16);
113517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
114517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    static Typeface sDefaultTypeface;
115517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    static Map<String, Typeface> sSystemFontMap;
116517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    static FontFamily[] sFallbackFonts;
117517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    private static final Object sLock = new Object();
118517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
119517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    static final String FONTS_CONFIG = "fonts.xml";
120517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
121517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /**
122517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * @hide
123517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     */
124517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public long native_instance;
125517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
126517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    // Style
127517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public static final int NORMAL = 0;
128517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public static final int BOLD = 1;
129517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public static final int ITALIC = 2;
130517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public static final int BOLD_ITALIC = 3;
131517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
132517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    private int mStyle = 0;
133517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    private int mBaseWeight = 0;
134517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
135517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    // Value for weight and italic. Indicates the value is resolved by font metadata.
136517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp
137517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /** @hide */
138517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public static final int RESOLVE_BY_FONT_TABLE = -1;
139517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
140517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    // Style value for building typeface.
141517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    private static final int STYLE_NORMAL = 0;
142517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    private static final int STYLE_ITALIC = 1;
143517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
144517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    private int[] mSupportedAxes;
145517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    private static final int[] EMPTY_AXES = {};
146517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
147517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    private static void setDefault(Typeface t) {
148517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        sDefaultTypeface = t;
149517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        nativeSetDefault(t.native_instance);
150517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    }
151517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
152517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /** Returns the typeface's intrinsic style attributes */
153517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public int getStyle() {
154517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        return mStyle;
155517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    }
156517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
157517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /** Returns true if getStyle() has the BOLD bit set. */
158517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public final boolean isBold() {
159517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        return (mStyle & BOLD) != 0;
160517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    }
161517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
162517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /** Returns true if getStyle() has the ITALIC bit set. */
163517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public final boolean isItalic() {
164517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        return (mStyle & ITALIC) != 0;
165517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    }
166517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
167517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /**
168517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * @hide
169517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * Used by Resources to load a font resource of type font file.
170517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     */
171517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    @Nullable
172517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public static Typeface createFromResources(AssetManager mgr, String path, int cookie) {
173517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        if (sFallbackFonts != null) {
174517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            synchronized (sDynamicTypefaceCache) {
175e83be8af690ef1ac820a63414d522e77ca9d4db6Steven Moreland                final String key = Builder.createAssetUid(
176517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        mgr, path, 0 /* ttcIndex */, null /* axes */,
177e83be8af690ef1ac820a63414d522e77ca9d4db6Steven Moreland                        RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */);
178517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                Typeface typeface = sDynamicTypefaceCache.get(key);
179517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                if (typeface != null) return typeface;
180e83be8af690ef1ac820a63414d522e77ca9d4db6Steven Moreland
181517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                FontFamily fontFamily = new FontFamily();
182e83be8af690ef1ac820a63414d522e77ca9d4db6Steven Moreland                // TODO: introduce ttc index and variation settings to resource type font.
183517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */,
184e83be8af690ef1ac820a63414d522e77ca9d4db6Steven Moreland                        0 /* ttcIndex */, RESOLVE_BY_FONT_TABLE /* weight */,
185e83be8af690ef1ac820a63414d522e77ca9d4db6Steven Moreland                        RESOLVE_BY_FONT_TABLE /* italic */, null /* axes */)) {
186517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    if (!fontFamily.freeze()) {
187517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        return null;
188517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    }
189d095e65c8c125c555046c60539a0f7abf0ccf271Robert Shih                    FontFamily[] families = {fontFamily};
190d095e65c8c125c555046c60539a0f7abf0ccf271Robert Shih                    typeface = createFromFamiliesWithDefault(families,
191d095e65c8c125c555046c60539a0f7abf0ccf271Robert Shih                            RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
192d095e65c8c125c555046c60539a0f7abf0ccf271Robert Shih                    sDynamicTypefaceCache.put(key, typeface);
193d095e65c8c125c555046c60539a0f7abf0ccf271Robert Shih                    return typeface;
194d095e65c8c125c555046c60539a0f7abf0ccf271Robert Shih                }
195d095e65c8c125c555046c60539a0f7abf0ccf271Robert Shih            }
196d095e65c8c125c555046c60539a0f7abf0ccf271Robert Shih        }
197d095e65c8c125c555046c60539a0f7abf0ccf271Robert Shih        return null;
198d095e65c8c125c555046c60539a0f7abf0ccf271Robert Shih    }
199d095e65c8c125c555046c60539a0f7abf0ccf271Robert Shih
200d095e65c8c125c555046c60539a0f7abf0ccf271Robert Shih    /**
201d095e65c8c125c555046c60539a0f7abf0ccf271Robert Shih     * @hide
202517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * Used by Resources to load a font resource of type xml.
203517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     */
204517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    @Nullable
205517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public static Typeface createFromResources(
206517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            FamilyResourceEntry entry, AssetManager mgr, String path) {
207517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        if (sFallbackFonts != null) {
208517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            Typeface typeface = findFromCache(mgr, path);
209517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            if (typeface != null) return typeface;
210517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
211517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            if (entry instanceof ProviderResourceEntry) {
212517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry;
213517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                // Downloadable font
214517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                typeface = findFromCache(providerEntry.getAuthority(), providerEntry.getQuery());
215517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                if (typeface != null) {
216517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    return typeface;
217517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                }
218e83be8af690ef1ac820a63414d522e77ca9d4db6Steven Moreland                List<List<String>> givenCerts = providerEntry.getCerts();
219517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                List<List<byte[]>> certs = new ArrayList<>();
220517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                if (givenCerts != null) {
221517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    for (int i = 0; i < givenCerts.size(); i++) {
222517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        List<String> certSet = givenCerts.get(i);
223517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        List<byte[]> byteArraySet = new ArrayList<>();
224517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        for (int j = 0; j < certSet.size(); j++) {
225517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                            byteArraySet.add(Base64.decode(certSet.get(j), Base64.DEFAULT));
226517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        }
227517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        certs.add(byteArraySet);
228517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    }
229e83be8af690ef1ac820a63414d522e77ca9d4db6Steven Moreland                }
230517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                // Downloaded font and it wasn't cached, request it again and return a
231517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                // default font instead (nothing we can do now).
232517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                create(new FontRequest(providerEntry.getAuthority(), providerEntry.getPackage(),
233517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        providerEntry.getQuery(), certs), NO_OP_REQUEST_CALLBACK);
234517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                return DEFAULT;
235517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
236517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
237517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            // family is FontFamilyFilesResourceEntry
238517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            final FontFamilyFilesResourceEntry filesEntry =
239517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    (FontFamilyFilesResourceEntry) entry;
240517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
241517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            FontFamily fontFamily = new FontFamily();
242517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) {
243517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                if (!fontFamily.addFontFromAssetManager(mgr, fontFile.getFileName(),
244517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        0 /* resourceCookie */, false /* isAsset */, 0 /* ttcIndex */,
245517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        fontFile.getWeight(), fontFile.isItalic() ? STYLE_ITALIC : STYLE_NORMAL,
246517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        null /* axes */)) {
247517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    return null;
248517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                }
249517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
250517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            // Due to backward compatibility, even if the font is not supported by our font stack,
251517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            // we need to place the empty font at the first place. The typeface with empty font
252517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            // behaves different from default typeface especially in fallback font selection.
253517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            fontFamily.allowUnsupportedFont();
254517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            fontFamily.freeze();
255517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            FontFamily[] familyChain = { fontFamily };
256517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            typeface = createFromFamiliesWithDefault(familyChain,
257517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
258517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            synchronized (sDynamicTypefaceCache) {
259517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
260517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        null /* axes */, RESOLVE_BY_FONT_TABLE /* weight */,
261517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        RESOLVE_BY_FONT_TABLE /* italic */);
262517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                sDynamicTypefaceCache.put(key, typeface);
263517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
264517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            return typeface;
265517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
266517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        return null;
267517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    }
268517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
269517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /**
270517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * Used by resources for cached loading if the font is available.
271517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * @hide
272517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     */
273517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public static Typeface findFromCache(AssetManager mgr, String path) {
274517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        synchronized (sDynamicTypefaceCache) {
275517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */, null /* axes */,
276517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */);
277517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            Typeface typeface = sDynamicTypefaceCache.get(key);
278517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            if (typeface != null) {
279517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                return typeface;
280517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
281517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
282517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        return null;
283517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    }
284517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
285517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /**
2868ff40189817e95c7a56e347398d20e60d7534ee6Pawin Vongmasa     * Set the application context so we can generate font requests from the provider. This should
287517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * be called from ActivityThread when the application binds, as we preload fonts.
288517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * @hide
289517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     */
2908a21c0191f974a0b9cbd5818052e2655e0aaa306Pawin Vongmasa    public static void setApplicationContext(Context context) {
291517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        synchronized (sLock) {
292517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            if (sFontsContract == null) {
293517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                sFontsContract = new FontsContract(context);
294517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                sHandler = new Handler();
295517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
296517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
297517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    }
298517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
299517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /**
300517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * Create a typeface object given a font request. The font will be asynchronously fetched,
301517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * therefore the result is delivered to the given callback. See {@link FontRequest}.
302517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * Only one of the methods in callback will be invoked, depending on whether the request
303f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa     * succeeds or fails. These calls will happen on the main thread.
304f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa     * @param request A {@link FontRequest} object that identifies the provider and query for the
305f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa     *                request. May not be null.
306f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa     * @param callback A callback that will be triggered when results are obtained. May not be null.
307517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     */
308f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    @Deprecated
309f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa    public static void create(@NonNull FontRequest request, @NonNull FontRequestCallback callback) {
310517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        // Check the cache first
311517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        // TODO: would the developer want to avoid a cache hit and always ask for the freshest
312517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        // result?
313517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        Typeface cachedTypeface = findFromCache(
314517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                request.getProviderAuthority(), request.getQuery());
315517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        if (cachedTypeface != null) {
316517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            sHandler.post(() -> callback.onTypefaceRetrieved(cachedTypeface));
317517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            return;
318517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
319517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        synchronized (sLock) {
320517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            if (sFontsContract == null) {
321517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                throw new RuntimeException("Context not initialized, can't query provider");
322517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
323517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            final ResultReceiver receiver = new ResultReceiver(null) {
324517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                @Override
325517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                public void onReceiveResult(int resultCode, Bundle resultData) {
326517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    sHandler.post(() -> receiveResult(request, callback, resultCode, resultData));
327517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                }
328517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            };
329517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            sFontsContract.getFont(request, receiver);
330517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
331517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    }
332517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
333517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    private static Typeface findFromCache(String providerAuthority, String query) {
334517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        synchronized (sDynamicTypefaceCache) {
335517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            final String key = createProviderUid(providerAuthority, query);
3368a21c0191f974a0b9cbd5818052e2655e0aaa306Pawin Vongmasa            Typeface typeface = sDynamicTypefaceCache.get(key);
337517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            if (typeface != null) {
338517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                return typeface;
339517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
340517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
341517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        return null;
342517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    }
343517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
344517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    private static void receiveResult(FontRequest request, FontRequestCallback callback,
345517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            int resultCode, Bundle resultData) {
346517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        Typeface cachedTypeface = findFromCache(
347517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                request.getProviderAuthority(), request.getQuery());
348517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        if (cachedTypeface != null) {
349f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            // We already know the result.
350f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            // Probably the requester requests the same font again in a short interval.
351f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            callback.onTypefaceRetrieved(cachedTypeface);
352f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            return;
353517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
354f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa        if (resultCode != FontsContract.Columns.RESULT_CODE_OK) {
355f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            callback.onTypefaceRequestFailed(resultCode);
356517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            return;
357517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
358517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        if (resultData == null) {
359517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            callback.onTypefaceRequestFailed(
360517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND);
361517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            return;
362517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
363517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        List<FontResult> resultList =
364517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                resultData.getParcelableArrayList(FontsContract.PARCEL_FONT_RESULTS);
365517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        if (resultList == null || resultList.isEmpty()) {
366517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            callback.onTypefaceRequestFailed(
367517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND);
368517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            return;
369517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
370517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        FontFamily fontFamily = new FontFamily();
371517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        for (int i = 0; i < resultList.size(); ++i) {
372517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            FontResult result = resultList.get(i);
373517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            ParcelFileDescriptor fd = result.getFileDescriptor();
374517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            if (fd == null) {
375517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                callback.onTypefaceRequestFailed(
376f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa                        FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
377f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa                return;
378f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            }
379517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            try (FileInputStream is = new FileInputStream(fd.getFileDescriptor())) {
380517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                FileChannel fileChannel = is.getChannel();
381517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                long fontSize = fileChannel.size();
382517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                ByteBuffer fontBuffer = fileChannel.map(
383517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        FileChannel.MapMode.READ_ONLY, 0, fontSize);
384517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                int weight = result.getWeight();
385517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                int italic = result.getItalic() ? STYLE_ITALIC : STYLE_NORMAL;
386517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                FontVariationAxis[] axes = null;
387517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                try {
388517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    axes = FontVariationAxis.fromFontVariationSettings(
389517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                            result.getFontVariationSettings());
390517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                } catch (FontVariationAxis.InvalidFormatException e) {
391517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    // TODO: Nice to pass FontVariationAxis[] directly instead of string.
392517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                }
393517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                if (!fontFamily.addFontFromBuffer(fontBuffer, result.getTtcIndex(),
394517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        axes, weight, italic)) {
395517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    Log.e(TAG, "Error creating font " + request.getQuery());
396517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    callback.onTypefaceRequestFailed(
397517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                            FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
398517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    return;
399517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                }
400517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            } catch (IOException e) {
401517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                Log.e(TAG, "Error reading font " + request.getQuery(), e);
402517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                callback.onTypefaceRequestFailed(
403517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
404517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                return;
405517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            } finally {
406517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                IoUtils.closeQuietly(fd);
407517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
408517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
409517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        if (!fontFamily.freeze()) {
410517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            callback.onTypefaceRequestFailed(
411517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
412517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            return;
413517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
414517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        Typeface typeface = Typeface.createFromFamiliesWithDefault(
415517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                new FontFamily[] { fontFamily },
416517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
417517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        synchronized (sDynamicTypefaceCache) {
418517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            String key = createProviderUid(request.getProviderAuthority(), request.getQuery());
419517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            sDynamicTypefaceCache.put(key, typeface);
420517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
421517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        callback.onTypefaceRetrieved(typeface);
422517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    }
423517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
424517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /**
425517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * Interface used to receive asynchronously fetched typefaces.
426517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     */
427517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    @Deprecated
428517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public interface FontRequestCallback {
429517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        /**
430517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
431517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * provider was not found on the device.
432517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         */
433517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        int FAIL_REASON_PROVIDER_NOT_FOUND = FontsContract.RESULT_CODE_PROVIDER_NOT_FOUND;
434517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        /**
435517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
436517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * provider must be authenticated and the given certificates do not match its signature.
437517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         */
438517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        int FAIL_REASON_WRONG_CERTIFICATES = FontsContract.RESULT_CODE_WRONG_CERTIFICATES;
439517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        /**
440517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font
441517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * returned by the provider was not loaded properly.
442517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         */
443517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        int FAIL_REASON_FONT_LOAD_ERROR = -3;
444517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        /**
445517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font
446517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * provider did not return any results for the given query.
447517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         */
448517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        int FAIL_REASON_FONT_NOT_FOUND = FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND;
449517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        /**
450517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font
451517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * provider found the queried font, but it is currently unavailable.
452517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         */
453517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        int FAIL_REASON_FONT_UNAVAILABLE = FontsContract.Columns.RESULT_CODE_FONT_UNAVAILABLE;
454517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        /**
455517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
456517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * query was not supported by the provider.
457517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         */
458517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        int FAIL_REASON_MALFORMED_QUERY = FontsContract.Columns.RESULT_CODE_MALFORMED_QUERY;
459517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
460517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        /** @hide */
461517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        @IntDef({ FAIL_REASON_PROVIDER_NOT_FOUND, FAIL_REASON_FONT_LOAD_ERROR,
462517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                FAIL_REASON_FONT_NOT_FOUND, FAIL_REASON_FONT_UNAVAILABLE,
463517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                FAIL_REASON_MALFORMED_QUERY })
464517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        @Retention(RetentionPolicy.SOURCE)
465517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        @interface FontRequestFailReason {}
466517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
467517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        /**
468517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * Called then a Typeface request done via {@link Typeface#create(FontRequest,
469517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * FontRequestCallback)} is complete. Note that this method will not be called if
470517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * {@link #onTypefaceRequestFailed(int)} is called instead.
471517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * @param typeface  The Typeface object retrieved.
472517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         */
473517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        void onTypefaceRetrieved(Typeface typeface);
474517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
475517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        /**
476517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * Called when a Typeface request done via {@link Typeface#create(FontRequest,
477517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * FontRequestCallback)} fails.
478517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * @param reason One of {@link #FAIL_REASON_PROVIDER_NOT_FOUND},
479517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         *               {@link #FAIL_REASON_FONT_NOT_FOUND},
480517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         *               {@link #FAIL_REASON_FONT_LOAD_ERROR},
481517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         *               {@link #FAIL_REASON_FONT_UNAVAILABLE} or
482517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         *               {@link #FAIL_REASON_MALFORMED_QUERY}.
483517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         */
484517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        void onTypefaceRequestFailed(@FontRequestFailReason int reason);
485517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    }
486517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
487517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    private static final FontRequestCallback NO_OP_REQUEST_CALLBACK = new FontRequestCallback() {
488517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        @Override
489517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        public void onTypefaceRetrieved(Typeface typeface) {
490517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            // Do nothing.
491517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
492517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
493517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        @Override
494517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        public void onTypefaceRequestFailed(@FontRequestFailReason int reason) {
495517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            // Do nothing.
496517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
497517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    };
498517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
499517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /**
500517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * A builder class for creating new Typeface instance.
501517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     *
502517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * <p>
503517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * Examples,
504517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * 1) Create Typeface from ttf file.
505517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * <pre>
506517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * <code>
507517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * Typeface.Builder buidler = new Typeface.Builder("your_font_file.ttf");
508517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * Typeface typeface = builder.build();
509517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * </code>
510517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * </pre>
511517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     *
512517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * 2) Create Typeface from ttc file in assets directory.
513517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * <pre>
514517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * <code>
515517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * Typeface.Builder buidler = new Typeface.Builder(getAssets(), "your_font_file.ttc");
516517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * builder.setTtcIndex(2);  // Set index of font collection.
517517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * Typeface typeface = builder.build();
518517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * </code>
519517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * </pre>
520517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     *
521517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * 3) Create Typeface with variation settings.
522517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * <pre>
523517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * <code>
524517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * Typeface.Builder buidler = new Typeface.Builder("your_font_file.ttf");
525517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * builder.setFontVariationSettings("'wght' 700, 'slnt' 20, 'ital' 1");
526517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * builder.setWeight(700);  // Tell the system that this is a bold font.
527517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * builder.setItalic(true);  // Tell the system that this is an italic style font.
528517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * Typeface typeface = builder.build();
529517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * </code>
530517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * </pre>
531517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * </p>
532517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     */
533517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    public static final class Builder {
534517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        /** @hide */
535517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        public static final int NORMAL_WEIGHT = 400;
536517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        /** @hide */
537517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        public static final int BOLD_WEIGHT = 700;
538517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
539517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        private int mTtcIndex;
540517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        private FontVariationAxis[] mAxes;
541517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
542517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        private AssetManager mAssetManager;
543517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        private String mPath;
544517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        private FileDescriptor mFd;
545517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
546517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        private FontsContract.FontInfo[] mFonts;
547517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        private Map<Uri, ByteBuffer> mFontBuffers;
548517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
549517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        private String mFallbackFamilyName;
550517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
551517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        private int mWeight = RESOLVE_BY_FONT_TABLE;
5529c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        private int mItalic = RESOLVE_BY_FONT_TABLE;
5539c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa
5549c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        /**
5559c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * Constructs a builder with a file path.
5569c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         *
5579c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * @param path The file object refers to the font file.
5589c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         */
5599c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        public Builder(@NonNull File path) {
5609c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa            mPath = path.getAbsolutePath();
5619c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        }
5629c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa
5639c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        /**
5649c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * Constructs a builder with a file descriptor.
5659c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         *
5669c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * Caller is responsible for closing the passed file descriptor after {@link #build} is
5679c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * called.
5689c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         *
5699c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * @param fd The file descriptor. The passed fd must be mmap-able.
5709c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         */
5719c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        public Builder(@NonNull FileDescriptor fd) {
5729c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa            mFd = fd;
5739c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        }
5749c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa
5759c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        /**
5769c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * Constructs a builder with a file path.
5779c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         *
5789c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * @param path The full path to the font file.
5799c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         */
5809c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        public Builder(@NonNull String path) {
5819c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa            mPath = path;
5829c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        }
5839c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa
5849c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        /**
5859c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * Constructs a builder from an asset manager and a file path in an asset directory.
5869c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         *
5879c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * @param assetManager The application's asset manager
5889c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * @param path The file name of the font data in the asset directory
5899c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         */
5909c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        public Builder(@NonNull AssetManager assetManager, @NonNull String path) {
5919c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa            mAssetManager = Preconditions.checkNotNull(assetManager);
5929c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa            mPath = Preconditions.checkStringNotEmpty(path);
5939c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        }
5949c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa
5959c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        /**
5969c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * Constracts a builder from an array of FontsContract.FontInfo.
5979c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         *
5989c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * Since {@link FontsContract.FontInfo} holds information about TTC indices and
5999c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * variation settings, there is no need to call {@link #setTtcIndex} or
6009c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * {@link #setFontVariationSettings}. Similary, {@link FontsContract.FontInfo} holds
6019c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * weight and italic information, so {@link #setWeight} and {@link #setItalic} are used
6029c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * for style matching during font selection.
6039c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         *
6049c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * @param results The array of {@link FontsContract.FontInfo}
6059c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * @param buffers The mapping from URI to buffers to be used during building.
6069c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * @hide
6079c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         */
6089c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        public Builder(@NonNull FontsContract.FontInfo[] fonts,
6099c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa                @NonNull Map<Uri, ByteBuffer> buffers) {
6109c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa            mFonts = fonts;
6119c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa            mFontBuffers = buffers;
6129c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        }
6139c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa
6149c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        /**
6159c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * Sets weight of the font.
6169c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         *
6179c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * Tells the system the weight of the given font. If not provided, the system will resolve
6189c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * the weight value by reading font tables.
6199c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * @param weight a weight value.
6209c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         */
6219c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        public Builder setWeight(@IntRange(from = 1, to = 1000) int weight) {
622517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            mWeight = weight;
623517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            return this;
624517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
625517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
626517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        /**
627517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * Sets italic information of the font.
628517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         *
629517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * Tells the system the style of the given font. If not provided, the system will resolve
630f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa         * the style by reading font tables.
6319c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * @param italic {@code true} if the font is italic. Otherwise {@code false}.
632517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         */
633517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        public Builder setItalic(boolean italic) {
634517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            mItalic = italic ? STYLE_ITALIC : STYLE_NORMAL;
635517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            return this;
636517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
637517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
638517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        /**
639517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * Sets an index of the font collection.
640f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa         *
641517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * Can not be used for Typeface source. build() method will return null for invalid index.
642517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * @param ttcIndex An index of the font collection. If the font source is not font
643f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa         *                 collection, do not call this method or specify 0.
644517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         */
645f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa        public Builder setTtcIndex(@IntRange(from = 0) int ttcIndex) {
646f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            if (mFonts != null) {
647f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa                throw new IllegalArgumentException(
648f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa                        "TTC index can not be specified for FontResult source.");
649f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            }
650517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            mTtcIndex = ttcIndex;
651517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            return this;
652517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
653517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
6548ff40189817e95c7a56e347398d20e60d7534ee6Pawin Vongmasa        /**
6558ff40189817e95c7a56e347398d20e60d7534ee6Pawin Vongmasa         * Sets a font variation settings.
6568ff40189817e95c7a56e347398d20e60d7534ee6Pawin Vongmasa         *
6578ff40189817e95c7a56e347398d20e60d7534ee6Pawin Vongmasa         * @param variationSettings See {@link android.widget.TextView#setFontVariationSettings}.
6588ff40189817e95c7a56e347398d20e60d7534ee6Pawin Vongmasa         * @throws InvalidFormatException If given string is not a valid font variation settings
6598ff40189817e95c7a56e347398d20e60d7534ee6Pawin Vongmasa         *                                format.
6608ff40189817e95c7a56e347398d20e60d7534ee6Pawin Vongmasa         */
6618ff40189817e95c7a56e347398d20e60d7534ee6Pawin Vongmasa        public Builder setFontVariationSettings(@Nullable String variationSettings)
6628ff40189817e95c7a56e347398d20e60d7534ee6Pawin Vongmasa                throws InvalidFormatException {
663517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            if (mFonts != null) {
664517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                throw new IllegalArgumentException(
665517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        "Font variation settings can not be specified for FontResult source.");
666f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            }
667f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa            if (mAxes != null) {
668517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                throw new IllegalStateException("Font variation settings are already set.");
669517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
6709c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa            mAxes = FontVariationAxis.fromFontVariationSettings(variationSettings);
671517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            return this;
672517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
673517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
674517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        /**
6759c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         * Sets a font variation settings.
676517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         *
677517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * @param axes An array of font variation axis tag-value pairs.
678517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         */
679517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        public Builder setFontVariationSettings(@Nullable FontVariationAxis[] axes) {
680517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            if (mFonts != null) {
681517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                throw new IllegalArgumentException(
682517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        "Font variation settings can not be specified for FontResult source.");
683517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
684517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            if (mAxes != null) {
685517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                throw new IllegalStateException("Font variation settings are already set.");
686517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
687517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            mAxes = axes;
688517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            return this;
689517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
690517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
691517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        /**
692517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * Sets a fallback family name.
693517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         *
694517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * By specifying a fallback family name, a fallback Typeface will be returned if the
695517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * {@link #build} method fails to create a Typeface from the provided font. The fallback
696517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * family will be resolved with the provided weight and italic information specified by
697f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa         * {@link #setWeight} and {@link #setItalic}.
698f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa         *
699f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa         * If {@link #setWeight} is not called, the fallback family keeps the default weight.
700517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * Similary, if {@link #setItalic} is not called, the fallback family keeps the default
701517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * italic information. For example, calling {@code builder.setFallback("sans-serif-light")}
702517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * is equivalent to calling {@code builder.setFallback("sans-serif").setWeight(300)} in
703f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa         * terms of fallback. The default weight and italic information are overridden by calling
704f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa         * {@link #setWeight} and {@link #setItalic}. For example, if a Typeface is constructed
705517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * using {@code builder.setFallback("sans-serif-light").setWeight(700)}, the fallback text
706517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * will render as sans serif bold.
7078ff40189817e95c7a56e347398d20e60d7534ee6Pawin Vongmasa         *
7088ff40189817e95c7a56e347398d20e60d7534ee6Pawin Vongmasa         * @param familyName A family name to be used for fallback if the provided font can not be
7098ff40189817e95c7a56e347398d20e60d7534ee6Pawin Vongmasa         *                   used. By passing {@code null}, build() returns {@code null}.
7108ff40189817e95c7a56e347398d20e60d7534ee6Pawin Vongmasa         *                   If {@link #setFallback} is not called on the builder, {@code null}
7119c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         *                   is assumed.
7129c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa         */
7139c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        public Builder setFallback(@Nullable String familyName) {
7149c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa            mFallbackFamilyName = familyName;
7159c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa            return this;
7169c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        }
7179c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa
7189c47c97ecac581d66b6febafd156618247e86742Pawin Vongmasa        /**
719517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * Creates a unique id for a given AssetManager and asset path.
720517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         *
721517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * @param mgr  AssetManager instance
722517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * @param path The path for the asset.
723517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * @param ttcIndex The TTC index for the font.
724517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * @param axes The font variation settings.
725517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * @return Unique id for a given AssetManager and asset path.
726517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         */
727517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        private static String createAssetUid(final AssetManager mgr, String path, int ttcIndex,
728517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                @Nullable FontVariationAxis[] axes, int weight, int italic) {
729517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            final SparseArray<String> pkgs = mgr.getAssignedPackageIdentifiers();
730517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            final StringBuilder builder = new StringBuilder();
731517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            final int size = pkgs.size();
732517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            for (int i = 0; i < size; i++) {
733517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                builder.append(pkgs.valueAt(i));
734517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                builder.append("-");
735517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
736517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            builder.append(path);
737517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            builder.append("-");
738517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            builder.append(Integer.toString(ttcIndex));
739517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            builder.append("-");
740517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            builder.append(Integer.toString(weight));
741517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            builder.append("-");
742517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            builder.append(Integer.toString(italic));
743517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            builder.append("-");
744517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            if (axes != null) {
745517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                for (FontVariationAxis axis : axes) {
746517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    builder.append(axis.getTag());
747517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    builder.append("-");
748517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    builder.append(Float.toString(axis.getStyleValue()));
749517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                }
750517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
751517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            return builder.toString();
752517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
753517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
754517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        private static final Object sLock = new Object();
755517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        // TODO: Unify with Typeface.sTypefaceCache.
756517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        @GuardedBy("sLock")
757517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache =
758517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                new LongSparseArray<>(3);
759517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
760517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        private Typeface resolveFallbackTypeface() {
761517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            if (mFallbackFamilyName == null) {
762517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                return null;
763517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
764517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
765517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            Typeface base =  sSystemFontMap.get(mFallbackFamilyName);
766517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            if (base == null) {
767517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                base = sDefaultTypeface;
768517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
769517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
770517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            if (mWeight == RESOLVE_BY_FONT_TABLE && mItalic == RESOLVE_BY_FONT_TABLE) {
771517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                return base;
772517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
773517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
774517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            final int weight = (mWeight == RESOLVE_BY_FONT_TABLE) ? base.mBaseWeight : mWeight;
775517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            final boolean italic =
776517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    (mItalic == RESOLVE_BY_FONT_TABLE) ? (base.mStyle & ITALIC) != 0 : mItalic == 1;
777517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            final int key = weight << 1 | (italic ? 1 : 0);
778517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
779517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            Typeface typeface;
780517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            synchronized(sLock) {
781517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                SparseArray<Typeface> innerCache = sTypefaceCache.get(base.native_instance);
782517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                if (innerCache != null) {
783517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    typeface = innerCache.get(key);
784517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    if (typeface != null) {
785517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        return typeface;
786517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    }
787517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                }
788517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
789517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                typeface = new Typeface(
790517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        nativeCreateFromTypefaceWithExactStyle(
791517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                                base.native_instance, weight, italic));
792517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
793517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                if (innerCache == null) {
794517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    innerCache = new SparseArray<>(4); // [regular, bold] x [upright, italic]
795517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    sTypefaceCache.put(base.native_instance, innerCache);
796517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                }
797517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                innerCache.put(key, typeface);
798517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
799517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            return typeface;
800517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
801517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
802517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        /**
803517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * Generates new Typeface from specified configuration.
804517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         *
805517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         * @return Newly created Typeface. May return null if some parameters are invalid.
806517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa         */
807517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        public Typeface build() {
808517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            if (mFd != null) {  // Builder is created with file descriptor.
809517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                try (FileInputStream fis = new FileInputStream(mFd)) {
810517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    FileChannel channel = fis.getChannel();
811517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    long size = channel.size();
812517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    ByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, size);
813517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
814517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    final FontFamily fontFamily = new FontFamily();
815517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    if (!fontFamily.addFontFromBuffer(buffer, mTtcIndex, mAxes, mWeight, mItalic)) {
816517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        fontFamily.abortCreation();
817517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        return resolveFallbackTypeface();
818517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    }
819517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    if (!fontFamily.freeze()) {
820517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        return resolveFallbackTypeface();
821517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    }
822517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    FontFamily[] families = { fontFamily };
823517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    return createFromFamiliesWithDefault(families, mWeight, mItalic);
824517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                } catch (IOException e) {
825517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    return resolveFallbackTypeface();
826517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                }
827517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            } else if (mAssetManager != null) {  // Builder is created with asset manager.
828517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                final String key = createAssetUid(
829517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        mAssetManager, mPath, mTtcIndex, mAxes, mWeight, mItalic);
830517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                synchronized (sLock) {
831517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    Typeface typeface = sDynamicTypefaceCache.get(key);
832517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    if (typeface != null) return typeface;
833517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    final FontFamily fontFamily = new FontFamily();
834517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    if (!fontFamily.addFontFromAssetManager(mAssetManager, mPath, mTtcIndex,
835517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                            true /* isAsset */, mTtcIndex, mWeight, mItalic, mAxes)) {
836517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        fontFamily.abortCreation();
837517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        return resolveFallbackTypeface();
838517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    }
839517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    if (!fontFamily.freeze()) {
840517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        return resolveFallbackTypeface();
841517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    }
842517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    FontFamily[] families = { fontFamily };
843517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    typeface = createFromFamiliesWithDefault(families, mWeight, mItalic);
844517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    sDynamicTypefaceCache.put(key, typeface);
845517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    return typeface;
846517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                }
847517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            } else if (mPath != null) {  // Builder is created with file path.
848517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                final FontFamily fontFamily = new FontFamily();
849517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                if (!fontFamily.addFont(mPath, mTtcIndex, mAxes, mWeight, mItalic)) {
850517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    fontFamily.abortCreation();
851517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    return resolveFallbackTypeface();
852517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                }
853517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                if (!fontFamily.freeze()) {
854517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    return resolveFallbackTypeface();
855517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                }
856517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                FontFamily[] families = { fontFamily };
857517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                return createFromFamiliesWithDefault(families, mWeight, mItalic);
858517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            } else if (mFonts != null) {
859517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                final FontFamily fontFamily = new FontFamily();
860517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                boolean atLeastOneFont = false;
861517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                for (FontsContract.FontInfo font : mFonts) {
862517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    final ByteBuffer fontBuffer = mFontBuffers.get(font.getUri());
863517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    if (fontBuffer == null) {
864517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        continue;  // skip
865517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    }
866517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    final boolean success = fontFamily.addFontFromBuffer(fontBuffer,
867517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                            font.getTtcIndex(), font.getAxes(), font.getWeight(),
868517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                            font.isItalic() ? STYLE_ITALIC : STYLE_NORMAL);
869517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    if (!success) {
870517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        fontFamily.abortCreation();
871517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                        return null;
872517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    }
873517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    atLeastOneFont = true;
874517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                }
875517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                if (!atLeastOneFont) {
876f62ea8018813951e8f6a182880cadb3217e4ce37Pawin Vongmasa                    // No fonts are avaialble. No need to create new Typeface and returns fallback
877517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    // Typeface instead.
878517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    fontFamily.abortCreation();
879517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                    return null;
880517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                }
881517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                fontFamily.freeze();
882517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                FontFamily[] families = { fontFamily };
883517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa                return createFromFamiliesWithDefault(families, mWeight, mItalic);
884517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            }
885517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
886517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            // Must not reach here.
887517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa            throw new IllegalArgumentException("No source was set.");
888517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa        }
889517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    }
890517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa
891517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa    /**
892eeac97b18ca5c939bf2ac59334d36d54f705af3dPawin Vongmasa     * Create a typeface object given a family name, and option style information.
893517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * If null is passed for the name, then the "default" font will be chosen.
894517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * The resulting typeface object can be queried (getStyle()) to discover what
895517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * its "real" style characteristics are.
896517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     *
897517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * @param familyName May be null. The name of the font family.
898517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     * @param style  The style (normal, bold, italic) of the typeface.
899517b0e090680e378f056677201426ed9dc325c65Pawin Vongmasa     *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
900     * @return The best matching typeface.
901     */
902    public static Typeface create(String familyName, int style) {
903        if (sSystemFontMap != null) {
904            return create(sSystemFontMap.get(familyName), style);
905        }
906        return null;
907    }
908
909    /**
910     * Create a typeface object that best matches the specified existing
911     * typeface and the specified Style. Use this call if you want to pick a new
912     * style from the same family of an existing typeface object. If family is
913     * null, this selects from the default font's family.
914     *
915     * @param family May be null. The name of the existing type face.
916     * @param style  The style (normal, bold, italic) of the typeface.
917     *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
918     * @return The best matching typeface.
919     */
920    public static Typeface create(Typeface family, int style) {
921        if (style < 0 || style > 3) {
922            style = 0;
923        }
924        long ni = 0;
925        if (family != null) {
926            // Return early if we're asked for the same face/style
927            if (family.mStyle == style) {
928                return family;
929            }
930
931            ni = family.native_instance;
932        }
933
934        Typeface typeface;
935        SparseArray<Typeface> styles = sTypefaceCache.get(ni);
936
937        if (styles != null) {
938            typeface = styles.get(style);
939            if (typeface != null) {
940                return typeface;
941            }
942        }
943
944        typeface = new Typeface(nativeCreateFromTypeface(ni, style));
945        if (styles == null) {
946            styles = new SparseArray<Typeface>(4);
947            sTypefaceCache.put(ni, styles);
948        }
949        styles.put(style, typeface);
950
951        return typeface;
952    }
953
954    /** @hide */
955    public static Typeface createFromTypefaceWithVariation(Typeface family,
956            List<FontVariationAxis> axes) {
957        final long ni = family == null ? 0 : family.native_instance;
958        return new Typeface(nativeCreateFromTypefaceWithVariation(ni, axes));
959    }
960
961    /**
962     * Returns one of the default typeface objects, based on the specified style
963     *
964     * @return the default typeface that corresponds to the style
965     */
966    public static Typeface defaultFromStyle(int style) {
967        return sDefaults[style];
968    }
969
970    /**
971     * Create a new typeface from the specified font data.
972     *
973     * @param mgr  The application's asset manager
974     * @param path The file name of the font data in the assets directory
975     * @return The new typeface.
976     */
977    public static Typeface createFromAsset(AssetManager mgr, String path) {
978        if (path == null) {
979            throw new NullPointerException();  // for backward compatibility
980        }
981        if (sFallbackFonts != null) {
982            synchronized (sLock) {
983                Typeface typeface = new Builder(mgr, path).build();
984                if (typeface != null) return typeface;
985
986                final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
987                        null /* axes */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
988                typeface = sDynamicTypefaceCache.get(key);
989                if (typeface != null) return typeface;
990
991                final FontFamily fontFamily = new FontFamily();
992                if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */,
993                        0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
994                        null /* axes */)) {
995                    // Due to backward compatibility, even if the font is not supported by our font
996                    // stack, we need to place the empty font at the first place. The typeface with
997                    // empty font behaves different from default typeface especially in fallback
998                    // font selection.
999                    fontFamily.allowUnsupportedFont();
1000                    fontFamily.freeze();
1001                    final FontFamily[] families = { fontFamily };
1002                    typeface = createFromFamiliesWithDefault(families, RESOLVE_BY_FONT_TABLE,
1003                            RESOLVE_BY_FONT_TABLE);
1004                    sDynamicTypefaceCache.put(key, typeface);
1005                    return typeface;
1006                } else {
1007                    fontFamily.abortCreation();
1008                }
1009            }
1010        }
1011        throw new RuntimeException("Font asset not found " + path);
1012    }
1013
1014    /**
1015     * Creates a unique id for a given font provider and query.
1016     */
1017    private static String createProviderUid(String authority, String query) {
1018        final StringBuilder builder = new StringBuilder();
1019        builder.append("provider:");
1020        builder.append(authority);
1021        builder.append("-");
1022        builder.append(query);
1023        return builder.toString();
1024    }
1025
1026    /**
1027     * Create a new typeface from the specified font file.
1028     *
1029     * @param path The path to the font data.
1030     * @return The new typeface.
1031     */
1032    public static Typeface createFromFile(@Nullable File path) {
1033        // For the compatibility reasons, leaving possible NPE here.
1034        // See android.graphics.cts.TypefaceTest#testCreateFromFileByFileReferenceNull
1035        return createFromFile(path.getAbsolutePath());
1036    }
1037
1038    /**
1039     * Create a new typeface from the specified font file.
1040     *
1041     * @param path The full path to the font data.
1042     * @return The new typeface.
1043     */
1044    public static Typeface createFromFile(@Nullable String path) {
1045        if (sFallbackFonts != null) {
1046            final FontFamily fontFamily = new FontFamily();
1047            if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */,
1048                      RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) {
1049                // Due to backward compatibility, even if the font is not supported by our font
1050                // stack, we need to place the empty font at the first place. The typeface with
1051                // empty font behaves different from default typeface especially in fallback font
1052                // selection.
1053                fontFamily.allowUnsupportedFont();
1054                fontFamily.freeze();
1055                FontFamily[] families = { fontFamily };
1056                return createFromFamiliesWithDefault(families, RESOLVE_BY_FONT_TABLE,
1057                        RESOLVE_BY_FONT_TABLE);
1058            } else {
1059                fontFamily.abortCreation();
1060            }
1061        }
1062        throw new RuntimeException("Font not found " + path);
1063    }
1064
1065    /**
1066     * Create a new typeface from an array of font families.
1067     *
1068     * @param families array of font families
1069     */
1070    private static Typeface createFromFamilies(FontFamily[] families) {
1071        long[] ptrArray = new long[families.length];
1072        for (int i = 0; i < families.length; i++) {
1073            ptrArray[i] = families[i].mNativePtr;
1074        }
1075        return new Typeface(nativeCreateFromArray(
1076                ptrArray, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
1077    }
1078
1079    /**
1080     * Create a new typeface from an array of font families, including
1081     * also the font families in the fallback list.
1082     * @param weight the weight for this family. {@link RESOLVE_BY_FONT_TABLE} can be used. In that
1083     *               case, the table information in the first family's font is used. If the first
1084     *               family has multiple fonts, the closest to the regular weight and upright font
1085     *               is used.
1086     * @param italic the italic information for this family. {@link RESOLVE_BY_FONT_TABLE} can be
1087     *               used. In that case, the table information in the first family's font is used.
1088     *               If the first family has multiple fonts, the closest to the regular weight and
1089     *               upright font is used.
1090     * @param families array of font families
1091     */
1092    private static Typeface createFromFamiliesWithDefault(FontFamily[] families,
1093                int weight, int italic) {
1094        long[] ptrArray = new long[families.length + sFallbackFonts.length];
1095        for (int i = 0; i < families.length; i++) {
1096            ptrArray[i] = families[i].mNativePtr;
1097        }
1098        for (int i = 0; i < sFallbackFonts.length; i++) {
1099            ptrArray[i + families.length] = sFallbackFonts[i].mNativePtr;
1100        }
1101        return new Typeface(nativeCreateFromArray(ptrArray, weight, italic));
1102    }
1103
1104    // don't allow clients to call this directly
1105    private Typeface(long ni) {
1106        if (ni == 0) {
1107            throw new RuntimeException("native typeface cannot be made");
1108        }
1109
1110        native_instance = ni;
1111        mStyle = nativeGetStyle(ni);
1112        mBaseWeight = nativeGetBaseWeight(ni);
1113    }
1114
1115    private static FontFamily makeFamilyFromParsed(FontConfig.Family family,
1116            Map<String, ByteBuffer> bufferForPath) {
1117        FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant());
1118        for (FontConfig.Font font : family.getFonts()) {
1119            String fullPathName = "/system/fonts/" + font.getFontName();
1120            ByteBuffer fontBuffer = bufferForPath.get(fullPathName);
1121            if (fontBuffer == null) {
1122                try (FileInputStream file = new FileInputStream(fullPathName)) {
1123                    FileChannel fileChannel = file.getChannel();
1124                    long fontSize = fileChannel.size();
1125                    fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
1126                    bufferForPath.put(fullPathName, fontBuffer);
1127                } catch (IOException e) {
1128                    Log.e(TAG, "Error mapping font file " + fullPathName);
1129                    continue;
1130                }
1131            }
1132            if (!fontFamily.addFontFromBuffer(fontBuffer, font.getTtcIndex(), font.getAxes(),
1133                    font.getWeight(), font.isItalic() ? STYLE_ITALIC : STYLE_NORMAL)) {
1134                Log.e(TAG, "Error creating font " + fullPathName + "#" + font.getTtcIndex());
1135            }
1136        }
1137        if (!fontFamily.freeze()) {
1138            // Treat as system error since reaching here means that a system pre-installed font
1139            // can't be used by our font stack.
1140            Log.e(TAG, "Unable to load Family: " + family.getName() + ":" + family.getLanguage());
1141        }
1142        return fontFamily;
1143    }
1144
1145    /*
1146     * (non-Javadoc)
1147     *
1148     * This should only be called once, from the static class initializer block.
1149     */
1150    private static void init() {
1151        // Load font config and initialize Minikin state
1152        File systemFontConfigLocation = getSystemFontConfigLocation();
1153        File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG);
1154        try {
1155            FileInputStream fontsIn = new FileInputStream(configFilename);
1156            FontConfig fontConfig = FontListParser.parse(fontsIn);
1157
1158            Map<String, ByteBuffer> bufferForPath = new HashMap<String, ByteBuffer>();
1159
1160            List<FontFamily> familyList = new ArrayList<FontFamily>();
1161            // Note that the default typeface is always present in the fallback list;
1162            // this is an enhancement from pre-Minikin behavior.
1163            for (int i = 0; i < fontConfig.getFamilies().length; i++) {
1164                FontConfig.Family f = fontConfig.getFamilies()[i];
1165                if (i == 0 || f.getName() == null) {
1166                    familyList.add(makeFamilyFromParsed(f, bufferForPath));
1167                }
1168            }
1169            sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]);
1170            setDefault(Typeface.createFromFamilies(sFallbackFonts));
1171
1172            Map<String, Typeface> systemFonts = new HashMap<String, Typeface>();
1173            for (int i = 0; i < fontConfig.getFamilies().length; i++) {
1174                Typeface typeface;
1175                FontConfig.Family f = fontConfig.getFamilies()[i];
1176                if (f.getName() != null) {
1177                    if (i == 0) {
1178                        // The first entry is the default typeface; no sense in
1179                        // duplicating the corresponding FontFamily.
1180                        typeface = sDefaultTypeface;
1181                    } else {
1182                        FontFamily fontFamily = makeFamilyFromParsed(f, bufferForPath);
1183                        FontFamily[] families = { fontFamily };
1184                        typeface = Typeface.createFromFamiliesWithDefault(families,
1185                                RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
1186                    }
1187                    systemFonts.put(f.getName(), typeface);
1188                }
1189            }
1190            for (FontConfig.Alias alias : fontConfig.getAliases()) {
1191                Typeface base = systemFonts.get(alias.getToName());
1192                Typeface newFace = base;
1193                int weight = alias.getWeight();
1194                if (weight != 400) {
1195                    newFace = new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
1196                }
1197                systemFonts.put(alias.getName(), newFace);
1198            }
1199            sSystemFontMap = systemFonts;
1200
1201        } catch (RuntimeException e) {
1202            Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e);
1203            // TODO: normal in non-Minikin case, remove or make error when Minikin-only
1204        } catch (FileNotFoundException e) {
1205            Log.e(TAG, "Error opening " + configFilename, e);
1206        } catch (IOException e) {
1207            Log.e(TAG, "Error reading " + configFilename, e);
1208        } catch (XmlPullParserException e) {
1209            Log.e(TAG, "XML parse exception for " + configFilename, e);
1210        }
1211    }
1212
1213    static {
1214        init();
1215        // Set up defaults and typefaces exposed in public API
1216        DEFAULT         = create((String) null, 0);
1217        DEFAULT_BOLD    = create((String) null, Typeface.BOLD);
1218        SANS_SERIF      = create("sans-serif", 0);
1219        SERIF           = create("serif", 0);
1220        MONOSPACE       = create("monospace", 0);
1221
1222        sDefaults = new Typeface[] {
1223            DEFAULT,
1224            DEFAULT_BOLD,
1225            create((String) null, Typeface.ITALIC),
1226            create((String) null, Typeface.BOLD_ITALIC),
1227        };
1228
1229    }
1230
1231    private static File getSystemFontConfigLocation() {
1232        return new File("/system/etc/");
1233    }
1234
1235    @Override
1236    protected void finalize() throws Throwable {
1237        try {
1238            nativeUnref(native_instance);
1239            native_instance = 0;  // Other finalizers can still call us.
1240        } finally {
1241            super.finalize();
1242        }
1243    }
1244
1245    @Override
1246    public boolean equals(Object o) {
1247        if (this == o) return true;
1248        if (o == null || getClass() != o.getClass()) return false;
1249
1250        Typeface typeface = (Typeface) o;
1251
1252        return mStyle == typeface.mStyle && native_instance == typeface.native_instance;
1253    }
1254
1255    @Override
1256    public int hashCode() {
1257        /*
1258         * Modified method for hashCode with long native_instance derived from
1259         * http://developer.android.com/reference/java/lang/Object.html
1260         */
1261        int result = 17;
1262        result = 31 * result + (int) (native_instance ^ (native_instance >>> 32));
1263        result = 31 * result + mStyle;
1264        return result;
1265    }
1266
1267    /** @hide */
1268    public boolean isSupportedAxes(int axis) {
1269        if (mSupportedAxes == null) {
1270            synchronized (this) {
1271                if (mSupportedAxes == null) {
1272                    mSupportedAxes = nativeGetSupportedAxes(native_instance);
1273                    if (mSupportedAxes == null) {
1274                        mSupportedAxes = EMPTY_AXES;
1275                    }
1276                }
1277            }
1278        }
1279        return Arrays.binarySearch(mSupportedAxes, axis) > 0;
1280    }
1281
1282    private static native long nativeCreateFromTypeface(long native_instance, int style);
1283    private static native long nativeCreateFromTypefaceWithExactStyle(
1284            long native_instance, int weight, boolean italic);
1285    // TODO: clean up: change List<FontVariationAxis> to FontVariationAxis[]
1286    private static native long nativeCreateFromTypefaceWithVariation(
1287            long native_instance, List<FontVariationAxis> axes);
1288    private static native long nativeCreateWeightAlias(long native_instance, int weight);
1289    private static native void nativeUnref(long native_instance);
1290    private static native int  nativeGetStyle(long native_instance);
1291    private static native int  nativeGetBaseWeight(long native_instance);
1292    private static native long nativeCreateFromArray(long[] familyArray, int weight, int italic);
1293    private static native void nativeSetDefault(long native_instance);
1294    private static native int[] nativeGetSupportedAxes(long native_instance);
1295}
1296