1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.core.graphics;
18
19import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
20
21import android.content.Context;
22import android.content.res.Resources;
23import android.graphics.Typeface;
24import android.os.Build;
25import android.os.CancellationSignal;
26import android.os.Handler;
27
28import androidx.annotation.NonNull;
29import androidx.annotation.Nullable;
30import androidx.annotation.RestrictTo;
31import androidx.collection.LruCache;
32import androidx.core.content.res.FontResourcesParserCompat;
33import androidx.core.content.res.FontResourcesParserCompat.FamilyResourceEntry;
34import androidx.core.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry;
35import androidx.core.content.res.FontResourcesParserCompat.ProviderResourceEntry;
36import androidx.core.content.res.ResourcesCompat;
37import androidx.core.os.BuildCompat;
38import androidx.core.provider.FontsContractCompat;
39import androidx.core.provider.FontsContractCompat.FontInfo;
40/**
41 * Helper for accessing features in {@link Typeface}.
42 * @hide
43 */
44@RestrictTo(LIBRARY_GROUP)
45public class TypefaceCompat {
46    private static final String TAG = "TypefaceCompat";
47
48    private static final TypefaceCompatBaseImpl sTypefaceCompatImpl;
49    static {
50        if (BuildCompat.isAtLeastP()) {
51            sTypefaceCompatImpl = new TypefaceCompatApi28Impl();
52        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
53            sTypefaceCompatImpl = new TypefaceCompatApi26Impl();
54        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
55                && TypefaceCompatApi24Impl.isUsable()) {
56            sTypefaceCompatImpl = new TypefaceCompatApi24Impl();
57        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
58            sTypefaceCompatImpl = new TypefaceCompatApi21Impl();
59        } else {
60            sTypefaceCompatImpl = new TypefaceCompatBaseImpl();
61        }
62    }
63
64    /**
65     * Cache for Typeface objects dynamically loaded from assets.
66     */
67    private static final LruCache<String, Typeface> sTypefaceCache = new LruCache<>(16);
68
69    private TypefaceCompat() {}
70
71    /**
72     * Find from internal cache.
73     *
74     * @return null if not found.
75     */
76    @Nullable
77    public static Typeface findFromCache(@NonNull Resources resources, int id, int style) {
78        return sTypefaceCache.get(createResourceUid(resources, id, style));
79    }
80
81    /**
82     * Create a unique id for a given Resource and id.
83     *
84     * @param resources Resources instance
85     * @param id a resource id
86     * @param style style to be used for this resource, -1 if not available.
87     * @return Unique id for a given resource and id.
88     */
89    private static String createResourceUid(final Resources resources, int id, int style) {
90        return resources.getResourcePackageName(id) + "-" + id + "-" + style;
91    }
92
93    /**
94     * Create Typeface from XML resource which root node is font-family.
95     *
96     * @return null if failed to create.
97     */
98    @Nullable
99    public static Typeface createFromResourcesFamilyXml(
100            @NonNull Context context, @NonNull FamilyResourceEntry entry,
101            @NonNull Resources resources, int id, int style,
102            @Nullable ResourcesCompat.FontCallback fontCallback, @Nullable Handler handler,
103            boolean isRequestFromLayoutInflator) {
104        Typeface typeface;
105        if (entry instanceof ProviderResourceEntry) {
106            ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry;
107            final boolean isBlocking = isRequestFromLayoutInflator
108                    ? providerEntry.getFetchStrategy()
109                    == FontResourcesParserCompat.FETCH_STRATEGY_BLOCKING
110                    : fontCallback == null;
111            final int timeout = isRequestFromLayoutInflator ? providerEntry.getTimeout()
112                    : FontResourcesParserCompat.INFINITE_TIMEOUT_VALUE;
113            typeface = FontsContractCompat.getFontSync(context, providerEntry.getRequest(),
114                    fontCallback, handler, isBlocking, timeout, style);
115        } else {
116            typeface = sTypefaceCompatImpl.createFromFontFamilyFilesResourceEntry(
117                    context, (FontFamilyFilesResourceEntry) entry, resources, style);
118            if (fontCallback != null) {
119                if (typeface != null) {
120                    fontCallback.callbackSuccessAsync(typeface, handler);
121                } else {
122                    fontCallback.callbackFailAsync(
123                            FontsContractCompat.FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR,
124                            handler);
125                }
126            }
127        }
128        if (typeface != null) {
129            sTypefaceCache.put(createResourceUid(resources, id, style), typeface);
130        }
131        return typeface;
132    }
133
134    /**
135     * Used by Resources to load a font resource of type font file.
136     */
137    @Nullable
138    public static Typeface createFromResourcesFontFile(
139            @NonNull Context context, @NonNull Resources resources, int id, String path,
140            int style) {
141        Typeface typeface = sTypefaceCompatImpl.createFromResourcesFontFile(
142                context, resources, id, path, style);
143        if (typeface != null) {
144            final String resourceUid = createResourceUid(resources, id, style);
145            sTypefaceCache.put(resourceUid, typeface);
146        }
147        return typeface;
148    }
149
150    /**
151     * Create a Typeface from a given FontInfo list and a map that matches them to ByteBuffers.
152     */
153    @Nullable
154    public static Typeface createFromFontInfo(@NonNull Context context,
155            @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts, int style) {
156        return sTypefaceCompatImpl.createFromFontInfo(context, cancellationSignal, fonts, style);
157    }
158}
159