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

Commit ad879adb authored by Deepanshu Gupta's avatar Deepanshu Gupta Committed by Android (Google) Code Review
Browse files

Merge "Add Asset management support for fonts." into lmp-dev

parents 98c3461a d51834bd
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
@@ -17,6 +17,7 @@
package com.android.layoutlib.bridge.android;

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;
@@ -47,6 +48,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;
@@ -101,6 +103,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;
@@ -140,6 +143,7 @@ public final class BridgeContext extends Context {
     */
    public BridgeContext(Object projectKey, DisplayMetrics metrics,
            RenderResources renderResources,
            AssetRepository assets,
            IProjectCallback projectCallback,
            Configuration config,
            int targetSdkVersion,
@@ -150,6 +154,8 @@ public final class BridgeContext extends Context {

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

        mApplicationInfo = new ApplicationInfo();
        mApplicationInfo.targetSdkVersion = targetSdkVersion;
@@ -1075,9 +1081,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;
            }