12c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka/*
22c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka * Copyright (C) 2017 The Android Open Source Project
32c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka *
42c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka * Licensed under the Apache License, Version 2.0 (the "License");
52c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka * you may not use this file except in compliance with the License.
62c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka * You may obtain a copy of the License at
72c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka *
82c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka *      http://www.apache.org/licenses/LICENSE-2.0
92c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka *
102c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka * Unless required by applicable law or agreed to in writing, software
112c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka * distributed under the License is distributed on an "AS IS" BASIS,
122c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka * See the License for the specific language governing permissions and
142c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka * limitations under the License.
152c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka */
162c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka
17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.core.graphics;
182c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka
19ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
202c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka
212c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonakaimport android.content.Context;
222c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonakaimport android.content.res.Resources;
232c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonakaimport android.graphics.Typeface;
242c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonakaimport android.net.Uri;
2508df2afeec46f363ef5f17146750bdf011412e56Seigo Nonakaimport android.os.CancellationSignal;
269dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport android.util.Log;
279dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikas
28ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.NonNull;
29ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.Nullable;
30ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.RequiresApi;
31ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.RestrictTo;
329dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.collection.SimpleArrayMap;
33ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry;
34ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.content.res.FontResourcesParserCompat.FontFileResourceEntry;
35ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.provider.FontsContractCompat.FontInfo;
362c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka
372c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonakaimport java.lang.reflect.Array;
382c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonakaimport java.lang.reflect.Constructor;
392c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonakaimport java.lang.reflect.InvocationTargetException;
402c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonakaimport java.lang.reflect.Method;
412c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonakaimport java.nio.ByteBuffer;
422c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonakaimport java.util.List;
432c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka
442c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka
452c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka/**
462c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka * Implementation of the Typeface compat methods for API 24 and above.
472c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka * @hide
482c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka */
492c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka@RestrictTo(LIBRARY_GROUP)
502c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka@RequiresApi(24)
5111e831738feba2bb6c5338358812a373bda9991aClara Bayarriclass TypefaceCompatApi24Impl extends TypefaceCompatBaseImpl {
522c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    private static final String TAG = "TypefaceCompatApi24Impl";
532c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka
542c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    private static final String FONT_FAMILY_CLASS = "android.graphics.FontFamily";
552c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    private static final String ADD_FONT_WEIGHT_STYLE_METHOD = "addFontWeightStyle";
562c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    private static final String CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD =
572c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            "createFromFamiliesWithDefault";
582c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    private static final Class sFontFamily;
592c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    private static final Constructor sFontFamilyCtor;
602c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    private static final Method sAddFontWeightStyle;
612c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    private static final Method sCreateFromFamiliesWithDefault;
622c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka
632c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    static {
642c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        Class fontFamilyClass;
652c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        Constructor fontFamilyCtor;
662c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        Method addFontMethod;
672c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        Method createFromFamiliesWithDefaultMethod;
682c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        try {
692c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            fontFamilyClass = Class.forName(FONT_FAMILY_CLASS);
702c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            fontFamilyCtor = fontFamilyClass.getConstructor();
712c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            addFontMethod = fontFamilyClass.getMethod(ADD_FONT_WEIGHT_STYLE_METHOD,
722c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka                    ByteBuffer.class, Integer.TYPE, List.class, Integer.TYPE, Boolean.TYPE);
732c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            Object familyArray = Array.newInstance(fontFamilyClass, 1);
742c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            createFromFamiliesWithDefaultMethod =
752c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka                    Typeface.class.getMethod(CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD,
762c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka                          familyArray.getClass());
772c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        } catch (ClassNotFoundException | NoSuchMethodException e) {
782c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            Log.e(TAG, e.getClass().getName(), e);
792c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            fontFamilyClass = null;
802c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            fontFamilyCtor = null;
812c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            addFontMethod = null;
822c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            createFromFamiliesWithDefaultMethod = null;
832c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        }
842c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        sFontFamilyCtor = fontFamilyCtor;
852c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        sFontFamily = fontFamilyClass;
862c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        sAddFontWeightStyle = addFontMethod;
872c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        sCreateFromFamiliesWithDefault = createFromFamiliesWithDefaultMethod;
882c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    }
892c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka
902c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    /**
912c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka     * Returns true if API24 implementation is usable.
922c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka     */
932c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    public static boolean isUsable() {
9411e831738feba2bb6c5338358812a373bda9991aClara Bayarri        if (sAddFontWeightStyle == null) {
9511e831738feba2bb6c5338358812a373bda9991aClara Bayarri            Log.w(TAG, "Unable to collect necessary private methods."
9611e831738feba2bb6c5338358812a373bda9991aClara Bayarri                    + "Fallback to legacy implementation.");
9711e831738feba2bb6c5338358812a373bda9991aClara Bayarri        }
982c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        return sAddFontWeightStyle != null;
992c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    }
1002c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka
1012c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    private static Object newFamily() {
1022c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        try {
1032c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            return sFontFamilyCtor.newInstance();
1042c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
1052c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            throw new RuntimeException(e);
1062c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        }
1072c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    }
1082c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka
1092c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    private static boolean addFontWeightStyle(Object family, ByteBuffer buffer, int ttcIndex,
1102c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            int weight, boolean style) {
1112c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        try {
1122c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            final Boolean result = (Boolean) sAddFontWeightStyle.invoke(
1132c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka                    family, buffer, ttcIndex, null /* variation axis */, weight, style);
1142c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            return result.booleanValue();
1152c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        } catch (IllegalAccessException | InvocationTargetException e) {
1162c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            throw new RuntimeException(e);
1172c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        }
1182c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    }
1192c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka
1202c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    private static Typeface createFromFamiliesWithDefault(Object family) {
1212c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        try {
1222c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            Object familyArray = Array.newInstance(sFontFamily, 1);
1232c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            Array.set(familyArray, 0, family);
1242c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            return (Typeface) sCreateFromFamiliesWithDefault.invoke(
1252c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka                    null /* static method */, familyArray);
1262c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        } catch (IllegalAccessException | InvocationTargetException e) {
1272c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            throw new RuntimeException(e);
1282c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        }
1292c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    }
1302c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka
1312c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    @Override
13208df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka    public Typeface createFromFontInfo(Context context,
13308df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka            @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts, int style) {
1342c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        Object family = newFamily();
13508df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka        SimpleArrayMap<Uri, ByteBuffer> bufferCache = new SimpleArrayMap<>();
13608df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka
1372c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        for (final FontInfo font : fonts) {
13808df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka            final Uri uri = font.getUri();
13908df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka            ByteBuffer buffer = bufferCache.get(uri);
14008df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka            if (buffer == null) {
14108df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka                buffer = TypefaceCompatUtil.mmap(context, cancellationSignal, uri);
14208df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka                bufferCache.put(uri, buffer);
14308df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka            }
14408df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka            if (!addFontWeightStyle(family, buffer, font.getTtcIndex(), font.getWeight(),
14508df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka                    font.isItalic())) {
1462c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka                return null;
1472c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            }
1482c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        }
149758621d23659e4b0da3b2ad00a84a5624efb1be8Clara Bayarri        final Typeface typeface = createFromFamiliesWithDefault(family);
150758621d23659e4b0da3b2ad00a84a5624efb1be8Clara Bayarri        return Typeface.create(typeface, style);
1512c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    }
1522c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka
1532c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    @Override
1542c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    public Typeface createFromFontFamilyFilesResourceEntry(Context context,
1552c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            FontFamilyFilesResourceEntry entry, Resources resources, int style) {
1562c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        Object family = newFamily();
1572c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        for (final FontFileResourceEntry e : entry.getEntries()) {
1582c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            final ByteBuffer buffer =
1592c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka                    TypefaceCompatUtil.copyToDirectBuffer(context, resources, e.getResourceId());
1602113e0613ddf90a83874ff9f13970899f1553bbbSeigo Nonaka            if (buffer == null) {
1612113e0613ddf90a83874ff9f13970899f1553bbbSeigo Nonaka                return null;
1622113e0613ddf90a83874ff9f13970899f1553bbbSeigo Nonaka            }
163d017d547e411e03ac271ce36600afe2412f35a87Mihai Popa            if (!addFontWeightStyle(family, buffer, e.getTtcIndex(), e.getWeight(), e.isItalic())) {
1642c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka                return null;
1652c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka            }
1662c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        }
1672c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka        return createFromFamiliesWithDefault(family);
1682c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka    }
1692c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka}
170