public class

TypefaceCompatApi26Impl

extends androidx.core.graphics.TypefaceCompatApi21Impl

 java.lang.Object

↳androidx.core.graphics.TypefaceCompatBaseImpl

↳androidx.core.graphics.TypefaceCompatApi21Impl

↳androidx.core.graphics.TypefaceCompatApi26Impl

Subclasses:

TypefaceCompatApi28Impl

Gradle dependencies

compile group: 'androidx.core', name: 'core', version: '1.15.0-alpha02'

  • groupId: androidx.core
  • artifactId: core
  • version: 1.15.0-alpha02

Artifact androidx.core:core:1.15.0-alpha02 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.core:core com.android.support:support-compat

Androidx class mapping:

androidx.core.graphics.TypefaceCompatApi26Impl android.support.v4.graphics.TypefaceCompatApi26Impl

Overview

Implementation of the Typeface compat methods for API 26 and above.

Summary

Fields
protected final java.lang.reflect.MethodmAbortCreation

protected final java.lang.reflect.MethodmAddFontFromAssetManager

protected final java.lang.reflect.MethodmAddFontFromBuffer

protected final java.lang.reflect.MethodmCreateFromFamiliesWithDefault

protected final java.lang.Class<java.lang.Object>mFontFamily

protected final java.lang.reflect.Constructor<java.lang.Object>mFontFamilyCtor

protected final java.lang.reflect.MethodmFreeze

Constructors
publicTypefaceCompatApi26Impl()

Methods
protected TypefacecreateFromFamiliesWithDefault(java.lang.Object family)

Call method Typeface#createFromFamiliesWithDefault( FontFamily[] families, int weight, int italic)

public TypefacecreateFromFontFamilyFilesResourceEntry(Context context, FontResourcesParserCompat.FontFamilyFilesResourceEntry entry, Resources resources, int style)

public TypefacecreateFromFontInfo(Context context, CancellationSignal cancellationSignal, FontsContractCompat.FontInfo fonts[], int style)

public TypefacecreateFromResourcesFontFile(Context context, Resources resources, int id, java.lang.String path, int style)

Used by Resources to load a font resource of type font file.

protected java.lang.reflect.MethodobtainAbortCreationMethod(java.lang.Class<java.lang.Object> fontFamily)

protected java.lang.reflect.MethodobtainAddFontFromAssetManagerMethod(java.lang.Class<java.lang.Object> fontFamily)

protected java.lang.reflect.MethodobtainAddFontFromBufferMethod(java.lang.Class<java.lang.Object> fontFamily)

protected java.lang.reflect.MethodobtainCreateFromFamiliesWithDefaultMethod(java.lang.Class<java.lang.Object> fontFamily)

protected java.lang.Class<java.lang.Object>obtainFontFamily()

protected java.lang.reflect.Constructor<java.lang.Object>obtainFontFamilyCtor(java.lang.Class<java.lang.Object> fontFamily)

protected java.lang.reflect.MethodobtainFreezeMethod(java.lang.Class<java.lang.Object> fontFamily)

from androidx.core.graphics.TypefaceCompatBaseImplcreateFromFontInfoWithFallback, createFromInputStream, findBestInfo
from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

protected final java.lang.Class<java.lang.Object> mFontFamily

protected final java.lang.reflect.Constructor<java.lang.Object> mFontFamilyCtor

protected final java.lang.reflect.Method mAddFontFromAssetManager

protected final java.lang.reflect.Method mAddFontFromBuffer

protected final java.lang.reflect.Method mFreeze

protected final java.lang.reflect.Method mAbortCreation

protected final java.lang.reflect.Method mCreateFromFamiliesWithDefault

Constructors

public TypefaceCompatApi26Impl()

Methods

protected Typeface createFromFamiliesWithDefault(java.lang.Object family)

Call method Typeface#createFromFamiliesWithDefault( FontFamily[] families, int weight, int italic)

public Typeface createFromFontFamilyFilesResourceEntry(Context context, FontResourcesParserCompat.FontFamilyFilesResourceEntry entry, Resources resources, int style)

public Typeface createFromFontInfo(Context context, CancellationSignal cancellationSignal, FontsContractCompat.FontInfo fonts[], int style)

public Typeface createFromResourcesFontFile(Context context, Resources resources, int id, java.lang.String path, int style)

Used by Resources to load a font resource of type font file.

protected java.lang.Class<java.lang.Object> obtainFontFamily()

protected java.lang.reflect.Constructor<java.lang.Object> obtainFontFamilyCtor(java.lang.Class<java.lang.Object> fontFamily)

protected java.lang.reflect.Method obtainAddFontFromAssetManagerMethod(java.lang.Class<java.lang.Object> fontFamily)

protected java.lang.reflect.Method obtainAddFontFromBufferMethod(java.lang.Class<java.lang.Object> fontFamily)

protected java.lang.reflect.Method obtainFreezeMethod(java.lang.Class<java.lang.Object> fontFamily)

protected java.lang.reflect.Method obtainAbortCreationMethod(java.lang.Class<java.lang.Object> fontFamily)

protected java.lang.reflect.Method obtainCreateFromFamiliesWithDefaultMethod(java.lang.Class<java.lang.Object> fontFamily)

Source

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.core.graphics;

import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;

import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.graphics.fonts.FontVariationAxis;
import android.net.Uri;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.content.res.FontResourcesParserCompat;
import androidx.core.content.res.FontResourcesParserCompat.FontFileResourceEntry;
import androidx.core.provider.FontsContractCompat;

import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.Map;

/**
 * Implementation of the Typeface compat methods for API 26 and above.
 */
@RestrictTo(LIBRARY_GROUP_PREFIX)
@RequiresApi(26)
public class TypefaceCompatApi26Impl extends TypefaceCompatApi21Impl {
    private static final String TAG = "TypefaceCompatApi26Impl";

    private static final String FONT_FAMILY_CLASS = "android.graphics.FontFamily";
    private static final String ADD_FONT_FROM_ASSET_MANAGER_METHOD = "addFontFromAssetManager";
    private static final String ADD_FONT_FROM_BUFFER_METHOD = "addFontFromBuffer";
    private static final String CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD =
            "createFromFamiliesWithDefault";
    private static final String FREEZE_METHOD = "freeze";
    private static final String ABORT_CREATION_METHOD = "abortCreation";
    private static final int RESOLVE_BY_FONT_TABLE = -1;

    protected final Class<?> mFontFamily;
    protected final Constructor<?> mFontFamilyCtor;
    protected final Method mAddFontFromAssetManager;
    protected final Method mAddFontFromBuffer;
    protected final Method mFreeze;
    protected final Method mAbortCreation;
    protected final Method mCreateFromFamiliesWithDefault;

    public TypefaceCompatApi26Impl() {
        Class<?> fontFamily;
        Constructor<?> fontFamilyCtor;
        Method addFontFromAssetManager;
        Method addFontFromBuffer;
        Method freeze;
        Method abortCreation;
        Method createFromFamiliesWithDefault;
        try {
            fontFamily = obtainFontFamily();
            fontFamilyCtor = obtainFontFamilyCtor(fontFamily);
            addFontFromAssetManager = obtainAddFontFromAssetManagerMethod(fontFamily);
            addFontFromBuffer = obtainAddFontFromBufferMethod(fontFamily);
            freeze = obtainFreezeMethod(fontFamily);
            abortCreation = obtainAbortCreationMethod(fontFamily);
            createFromFamiliesWithDefault = obtainCreateFromFamiliesWithDefaultMethod(fontFamily);
        } catch (ClassNotFoundException | NoSuchMethodException e) {
            Log.e(TAG, "Unable to collect necessary methods for class " + e.getClass().getName(),
                    e);
            fontFamily = null;
            fontFamilyCtor = null;
            addFontFromAssetManager = null;
            addFontFromBuffer = null;
            freeze = null;
            abortCreation = null;
            createFromFamiliesWithDefault = null;
        }
        mFontFamily = fontFamily;
        mFontFamilyCtor = fontFamilyCtor;
        mAddFontFromAssetManager = addFontFromAssetManager;
        mAddFontFromBuffer = addFontFromBuffer;
        mFreeze = freeze;
        mAbortCreation = abortCreation;
        mCreateFromFamiliesWithDefault = createFromFamiliesWithDefault;
    }

    /**
     * Returns true if all the necessary methods were found.
     */
    private boolean isFontFamilyPrivateAPIAvailable() {
        if (mAddFontFromAssetManager == null) {
            Log.w(TAG, "Unable to collect necessary private methods. "
                    + "Fallback to legacy implementation.");
        }
        return mAddFontFromAssetManager != null;
    }

    /**
     * Create a new FontFamily instance
     */
    @Nullable
    private Object newFamily() {
        try {
            return mFontFamilyCtor.newInstance();
        } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
            return null;
        }
    }

    /**
     * Call FontFamily#addFontFromAssetManager(AssetManager mgr, String path, int cookie,
     *      boolean isAsset, int ttcIndex, int weight, int isItalic, FontVariationAxis[] axes)
     */
    private boolean addFontFromAssetManager(Context context, Object family, String fileName,
            int ttcIndex, int weight, int style, @Nullable FontVariationAxis[] axes) {
        try {
            return (Boolean) mAddFontFromAssetManager.invoke(family,
                    context.getAssets(), fileName, 0 /* cookie */, false /* isAsset */, ttcIndex,
                    weight, style, axes);
        } catch (IllegalAccessException | InvocationTargetException e) {
            return false;
        }
    }

    /**
     * Call FontFamily#addFontFromBuffer(ByteBuffer font, int ttcIndex, FontVariationAxis[] axes,
     *      int weight, int italic)
     */
    private boolean addFontFromBuffer(Object family, ByteBuffer buffer,
            int ttcIndex, int weight, int style) {
        try {
            return (Boolean) mAddFontFromBuffer.invoke(family,
                    buffer, ttcIndex, null /* axes */, weight, style);
        } catch (IllegalAccessException | InvocationTargetException e) {
            return false;
        }
    }

    /**
     * Call method Typeface#createFromFamiliesWithDefault(
     *      FontFamily[] families, int weight, int italic)
     */
    @Nullable
    protected Typeface createFromFamiliesWithDefault(Object family) {
        try {
            Object familyArray = Array.newInstance(mFontFamily, 1);
            Array.set(familyArray, 0, family);
            return (Typeface) mCreateFromFamiliesWithDefault.invoke(null /* static method */,
                    familyArray, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
        } catch (IllegalAccessException | InvocationTargetException e) {
            return null;
        }
    }

    /**
     * Call FontFamily#freeze()
     */
    private boolean freeze(Object family) {
        try {
            return (Boolean) mFreeze.invoke(family);
        } catch (IllegalAccessException | InvocationTargetException e) {
            return false;
        }
    }

    /**
     * Call FontFamily#abortCreation()
     */
    private void abortCreation(Object family) {
        try {
            mAbortCreation.invoke(family);
        } catch (IllegalAccessException | InvocationTargetException ignored) { }
    }

    @Override
    @Nullable
    public Typeface createFromFontFamilyFilesResourceEntry(Context context,
            FontResourcesParserCompat.FontFamilyFilesResourceEntry entry, Resources resources,
            int style) {
        if (!isFontFamilyPrivateAPIAvailable()) {
            return super.createFromFontFamilyFilesResourceEntry(context, entry, resources, style);
        }
        Object fontFamily = newFamily();
        if (fontFamily == null) {
            return null;
        }
        for (final FontFileResourceEntry fontFile : entry.getEntries()) {
            if (!addFontFromAssetManager(context, fontFamily, fontFile.getFileName(),
                    fontFile.getTtcIndex(), fontFile.getWeight(), fontFile.isItalic() ? 1 : 0,
                    FontVariationAxis.fromFontVariationSettings(fontFile.getVariationSettings()))) {
                abortCreation(fontFamily);
                return null;
            }
        }
        if (!freeze(fontFamily)) {
            return null;
        }
        return createFromFamiliesWithDefault(fontFamily);
    }

    @Override
    @Nullable
    public Typeface createFromFontInfo(Context context,
            @Nullable CancellationSignal cancellationSignal,
            @NonNull FontsContractCompat.FontInfo[] fonts, int style) {
        if (fonts.length < 1) {
            return null;
        }
        if (!isFontFamilyPrivateAPIAvailable()) {
            // Even if the private API is not avaiable, don't use API 21 implemenation and use
            // public API to create Typeface from file descriptor.
            final FontsContractCompat.FontInfo bestFont = findBestInfo(fonts, style);
            final ContentResolver resolver = context.getContentResolver();
            try (ParcelFileDescriptor pfd =
                    resolver.openFileDescriptor(bestFont.getUri(), "r", cancellationSignal)) {
                if (pfd == null) {
                    return null;
                }
                return new Typeface.Builder(pfd.getFileDescriptor())
                        .setWeight(bestFont.getWeight())
                        .setItalic(bestFont.isItalic())
                        .build();
            } catch (IOException e) {
                return null;
            }
        }
        Map<Uri, ByteBuffer> uriBuffer = TypefaceCompatUtil.readFontInfoIntoByteBuffer(
                context, fonts, cancellationSignal);
        final Object fontFamily = newFamily();
        if (fontFamily == null) {
            return null;
        }
        boolean atLeastOneFont = false;
        for (FontsContractCompat.FontInfo font : fonts) {
            final ByteBuffer fontBuffer = uriBuffer.get(font.getUri());
            if (fontBuffer == null) {
                continue;  // skip
            }
            final boolean success = addFontFromBuffer(fontFamily, fontBuffer,
                    font.getTtcIndex(), font.getWeight(), font.isItalic() ? 1 : 0);
            if (!success) {
                abortCreation(fontFamily);
                return null;
            }
            atLeastOneFont = true;
        }
        if (!atLeastOneFont) {
            abortCreation(fontFamily);
            return null;
        }
        if (!freeze(fontFamily)) {
            return null;
        }
        final Typeface typeface = createFromFamiliesWithDefault(fontFamily);
        if (typeface == null) {
            return null;
        }
        return Typeface.create(typeface, style);
    }

    /**
     * Used by Resources to load a font resource of type font file.
     */
    @Nullable
    @Override
    public Typeface createFromResourcesFontFile(
            Context context, Resources resources, int id, String path, int style) {
        if (!isFontFamilyPrivateAPIAvailable()) {
            return super.createFromResourcesFontFile(context, resources, id, path, style);
        }
        Object fontFamily = newFamily();
        if (fontFamily == null) {
            return null;
        }
        if (!addFontFromAssetManager(context, fontFamily, path,
                0 /* ttcIndex */, RESOLVE_BY_FONT_TABLE /* weight */,
                RESOLVE_BY_FONT_TABLE /* italic */, null /* axes */)) {
            abortCreation(fontFamily);
            return null;
        }
        if (!freeze(fontFamily)) {
            return null;
        }
        return createFromFamiliesWithDefault(fontFamily);
    }

    // The following getters retrieve by reflection the Typeface methods, belonging to the
    // framework code, which will be invoked. Since the definitions of these methods can change
    // across different API versions, inheriting classes should override these getters in order to
    // reflect the method definitions in the API versions they represent.
    //===========================================================================================
    protected Class<?> obtainFontFamily() throws ClassNotFoundException {
        return Class.forName(FONT_FAMILY_CLASS);
    }

    protected Constructor<?> obtainFontFamilyCtor(Class<?> fontFamily)
            throws NoSuchMethodException {
        return fontFamily.getConstructor();
    }

    protected Method obtainAddFontFromAssetManagerMethod(Class<?> fontFamily)
            throws NoSuchMethodException {
        return fontFamily.getMethod(ADD_FONT_FROM_ASSET_MANAGER_METHOD,
                AssetManager.class, String.class, Integer.TYPE, Boolean.TYPE, Integer.TYPE,
                Integer.TYPE, Integer.TYPE, FontVariationAxis[].class);
    }

    protected Method obtainAddFontFromBufferMethod(Class<?> fontFamily)
            throws NoSuchMethodException {
        return fontFamily.getMethod(ADD_FONT_FROM_BUFFER_METHOD,
                ByteBuffer.class, Integer.TYPE, FontVariationAxis[].class, Integer.TYPE,
                Integer.TYPE);
    }

    protected Method obtainFreezeMethod(Class<?> fontFamily) throws NoSuchMethodException {
        return fontFamily.getMethod(FREEZE_METHOD);
    }

    protected Method obtainAbortCreationMethod(Class<?> fontFamily) throws NoSuchMethodException {
        return fontFamily.getMethod(ABORT_CREATION_METHOD);
    }

    protected Method obtainCreateFromFamiliesWithDefaultMethod(Class<?> fontFamily)
            throws NoSuchMethodException {
        Object familyArray = Array.newInstance(fontFamily, 1);
        Method m =  Typeface.class.getDeclaredMethod(CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD,
                familyArray.getClass(), Integer.TYPE, Integer.TYPE);
        m.setAccessible(true);
        return m;
    }

    @NonNull
    @Override
    Typeface createWeightStyle(@NonNull Context context,
            @NonNull Typeface base, int weight, boolean italic) {
        Typeface out = null;
        try {
            out = WeightTypefaceApi26.createWeightStyle(base, weight, italic);
        } catch (RuntimeException fallbackFailed) {
            // ignore, fallback to legacy behavior
        }
        if (out == null) {
            out = super.createWeightStyle(context, base, weight, italic);
        }
        return out;
    }
}