Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1ddb76ff authored by Deepanshu Gupta's avatar Deepanshu Gupta Committed by Ricardo Cerqueira
Browse files

Add Asset management support for fonts.

Change-Id: I10ca67dcffe244667d4ae0bda65dbc1aa6691d50
parent b9bad644
Loading
Loading
Loading
Loading
+12 −3
Original line number Diff line number Diff line
@@ -16,12 +16,13 @@

package android.content.res;

import com.android.ide.common.rendering.api.AssetRepository;
import com.android.layoutlib.bridge.Bridge;

import android.content.res.AssetManager;

public class BridgeAssetManager extends AssetManager {

    private AssetRepository mAssetRepository;

    /**
     * This initializes the static field {@link AssetManager#sSystem} which is used
     * by methods who get a global asset manager using {@link AssetManager#getSystem()}.
@@ -48,6 +49,14 @@ public class BridgeAssetManager extends AssetManager {
        AssetManager.sSystem = null;
    }

    private BridgeAssetManager() {
    public void setAssetRepository(AssetRepository assetRepository) {
        mAssetRepository = assetRepository;
    }

    public AssetRepository getAssetRepository() {
        return mAssetRepository;
    }

    public BridgeAssetManager() {
    }
}
+96 −5
Original line number Diff line number Diff line
@@ -18,21 +18,28 @@ package android.graphics;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ide.common.rendering.api.AssetRepository;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;

import android.content.res.AssetManager;
import android.content.res.BridgeAssetManager;

import java.awt.Font;
import java.awt.FontFormatException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
import java.util.Set;

@@ -56,10 +63,28 @@ public class FontFamily_Delegate {
    public static final int BOLD_FONT_WEIGHT_DELTA = 300;
    public static final int BOLD_FONT_WEIGHT = 700;

    // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked
    // separately.
    private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
    private static final String FN_ALL_FONTS_LIST = "fontsInSdk.txt";
    private static final String EXTENSION_OTF = ".otf";

    private static final int CACHE_SIZE = 10;
    // The cache has a drawback that if the font file changed after the font object was created,
    // we will not update it.
    private static final Map<String, FontInfo> sCache =
            new LinkedHashMap<String, FontInfo>(CACHE_SIZE) {
        @Override
        protected boolean removeEldestEntry(Entry<String, FontInfo> eldest) {
            return size() > CACHE_SIZE;
        }

        @Override
        public FontInfo put(String key, FontInfo value) {
            // renew this entry.
            FontInfo removed = remove(key);
            super.put(key, value);
            return removed;
        }
    };

    /**
     * A class associating {@link Font} with its metadata.
@@ -194,7 +219,7 @@ public class FontFamily_Delegate {
            try {
                return Font.createFont(Font.TRUETYPE_FONT, f);
            } catch (Exception e) {
                if (path.endsWith(".otf") && e instanceof FontFormatException) {
                if (path.endsWith(EXTENSION_OTF) && e instanceof FontFormatException) {
                    // If we aren't able to load an Open Type font, don't log a warning just yet.
                    // We wait for a case where font is being used. Only then we try to log the
                    // warning.
@@ -281,8 +306,74 @@ public class FontFamily_Delegate {

    @LayoutlibDelegate
    /*package*/ static boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr, String path) {
        FontFamily_Delegate ffd = sManager.getDelegate(nativeFamily);
        ffd.mValid = true;
        if (mgr == null) {
            return false;
        }
        if (mgr instanceof BridgeAssetManager) {
            InputStream fontStream = null;
            try {
                AssetRepository assetRepository = ((BridgeAssetManager) mgr).getAssetRepository();
                if (assetRepository == null) {
                    Bridge.getLog().error(LayoutLog.TAG_MISSING_ASSET, "Asset not found: " + path,
                            null);
                    return false;
                }
                if (!assetRepository.isSupported()) {
                    // Don't log any warnings on unsupported IDEs.
                    return false;
                }
                // Check cache
                FontInfo fontInfo = sCache.get(path);
                if (fontInfo != null) {
                    // renew the font's lease.
                    sCache.put(path, fontInfo);
                    ffd.addFont(fontInfo);
                    return true;
                }
                fontStream = assetRepository.openAsset(path, AssetManager.ACCESS_STREAMING);
                if (fontStream == null) {
                    Bridge.getLog().error(LayoutLog.TAG_MISSING_ASSET, "Asset not found: " + path,
                            path);
                    return false;
                }
                Font font = Font.createFont(Font.TRUETYPE_FONT, fontStream);
                fontInfo = new FontInfo();
                fontInfo.mFont = font;
                fontInfo.mWeight = font.isBold() ? BOLD_FONT_WEIGHT : DEFAULT_FONT_WEIGHT;
                fontInfo.mIsItalic = font.isItalic();
                ffd.addFont(fontInfo);
                return true;
            } catch (IOException e) {
                Bridge.getLog().error(LayoutLog.TAG_MISSING_ASSET, "Unable to load font " + path, e,
                        path);
            } catch (FontFormatException e) {
                if (path.endsWith(EXTENSION_OTF)) {
                    // otf fonts are not supported on the user's config (JRE version + OS)
                    Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                "Typeface.createFromAsset is not supported.", null, null);
                            "OpenType fonts are not supported yet: " + path, null, path);
                } else {
                    Bridge.getLog().error(LayoutLog.TAG_BROKEN,
                            "Unable to load font " + path, e, path);
                }
            } finally {
                if (fontStream != null) {
                    try {
                        fontStream.close();
                    } catch (IOException ignored) {
                    }
                }
            }
            return false;
        }
        // This should never happen. AssetManager is a final class (from user's perspective), and
        // we've replaced every creation of AssetManager with our implementation. We create an
        // exception and log it, but continue with rest of the rendering, without loading this font.
        Bridge.getLog().error(LayoutLog.TAG_BROKEN,
                "You have found a bug in the rendering library. Please file a bug at b.android.com.",
                new RuntimeException("Asset Manager is not an instance of BridgeAssetManager"),
                null);
        return false;
    }

+8 −3
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.layoutlib.bridge.android;

import android.os.IBinder;
import com.android.annotations.Nullable;
import com.android.ide.common.rendering.api.AssetRepository;
import com.android.ide.common.rendering.api.ILayoutPullParser;
import com.android.ide.common.rendering.api.IProjectCallback;
import com.android.ide.common.rendering.api.LayoutLog;
@@ -48,6 +49,7 @@ import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.BridgeAssetManager;
import android.content.res.BridgeResources;
import android.content.res.BridgeTypedArray;
import android.content.res.Configuration;
@@ -102,6 +104,7 @@ public final class BridgeContext extends Context {
     * used to populate the mViewKeyMap.
     */
    private final HashMap<Object, Object> mViewKeyHelpMap = new HashMap<Object, Object>();
    private final BridgeAssetManager mAssets;
    private Resources mSystemResources;
    private final Object mProjectKey;
    private final DisplayMetrics mMetrics;
@@ -141,6 +144,7 @@ public final class BridgeContext extends Context {
     */
    public BridgeContext(Object projectKey, DisplayMetrics metrics,
            RenderResources renderResources,
            AssetRepository assets,
            IProjectCallback projectCallback,
            Configuration config,
            int targetSdkVersion,
@@ -151,6 +155,8 @@ public final class BridgeContext extends Context {

        mRenderResources = renderResources;
        mConfig = config;
        mAssets = new BridgeAssetManager();
        mAssets.setAssetRepository(assets);

        mApplicationInfo = new ApplicationInfo();
        mApplicationInfo.targetSdkVersion = targetSdkVersion;
@@ -1093,9 +1099,8 @@ public final class BridgeContext extends Context {
    }

    @Override
    public AssetManager getAssets() {
        // pass
        return null;
    public BridgeAssetManager getAssets() {
        return mAssets;
    }

    @Override
+10 −9
Original line number Diff line number Diff line
@@ -35,7 +35,6 @@ import com.android.resources.ScreenSize;

import android.content.res.Configuration;
import android.os.HandlerThread_Delegate;
import android.os.Looper;
import android.util.DisplayMetrics;
import android.view.ViewConfiguration_Accessor;
import android.view.inputmethod.InputMethodManager;
@@ -71,7 +70,7 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
    /**
     * Creates a renderAction.
     * <p>
     * This <b>must</b> be followed by a call to {@link RenderAction#init()}, which act as a
     * This <b>must</b> be followed by a call to {@link RenderAction#init(long)}, which act as a
     * call to {@link RenderAction#acquire(long)}
     *
     * @param params the RenderParams. This must be a copy that the action can keep
@@ -121,8 +120,8 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso

        // build the context
        mContext = new BridgeContext(mParams.getProjectKey(), metrics, resources,
                mParams.getProjectCallback(), getConfiguration(), mParams.getTargetSdkVersion(),
                mParams.isRtlSupported());
                mParams.getAssets(), mParams.getProjectCallback(), getConfiguration(),
                mParams.getTargetSdkVersion(), mParams.isRtlSupported());

        setUp();

@@ -139,7 +138,7 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
     * The preparation can fail if another rendering took too long and the timeout was elapsed.
     *
     * More than one call to this from the same thread will have no effect and will return
     * {@link Result#SUCCESS}.
     * {@link Result.Status#SUCCESS}.
     *
     * After scene actions have taken place, only one call to {@link #release()} must be
     * done.
@@ -173,7 +172,7 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
     * Acquire the lock so that the scene can be acted upon.
     * <p>
     * This returns null if the lock was just acquired, otherwise it returns
     * {@link Result#SUCCESS} if the lock already belonged to that thread, or another
     * {@link Result.Status#SUCCESS} if the lock already belonged to that thread, or another
     * instance (see {@link Result#getStatus()}) if an error occurred.
     *
     * @param timeout the time to wait if another rendering is happening.
@@ -184,11 +183,11 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
     */
    private Result acquireLock(long timeout) {
        ReentrantLock lock = Bridge.getLock();
        if (lock.isHeldByCurrentThread() == false) {
        if (!lock.isHeldByCurrentThread()) {
            try {
                boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);

                if (acquired == false) {
                if (!acquired) {
                    return ERROR_TIMEOUT.createResult();
                }
            } catch (InterruptedException e) {
@@ -308,7 +307,7 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
     */
    protected void checkLock() {
        ReentrantLock lock = Bridge.getLock();
        if (lock.isHeldByCurrentThread() == false) {
        if (!lock.isHeldByCurrentThread()) {
            throw new IllegalStateException("scene must be acquired first. see #acquire(long)");
        }
        if (sCurrentContext != mContext) {
@@ -347,6 +346,7 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
        config.screenWidthDp = hardwareConfig.getScreenWidth() / density.getDpiValue();
        config.screenHeightDp = hardwareConfig.getScreenHeight() / density.getDpiValue();
        if (config.screenHeightDp < config.screenWidthDp) {
            //noinspection SuspiciousNameCombination
            config.smallestScreenWidthDp = config.screenHeightDp;
        } else {
            config.smallestScreenWidthDp = config.screenWidthDp;
@@ -367,6 +367,7 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
                config.orientation = Configuration.ORIENTATION_LANDSCAPE;
                break;
            case SQUARE:
                //noinspection deprecation
                config.orientation = Configuration.ORIENTATION_SQUARE;
                break;
            }