am 0a300b89: am ad879adb: Merge "Add Asset management support for fonts." into lmp-dev automerge: 0684991

* commit '0a300b897711bfa39e0d30e2fa61664f01fb58d8':
  Add Asset management support for fonts.
This commit is contained in:
Deepanshu Gupta
2015-02-17 22:24:17 +00:00
committed by Android Git Automerger
4 changed files with 126 additions and 20 deletions

View File

@@ -16,12 +16,13 @@
package android.content.res; package android.content.res;
import com.android.ide.common.rendering.api.AssetRepository;
import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.Bridge;
import android.content.res.AssetManager;
public class BridgeAssetManager extends AssetManager { public class BridgeAssetManager extends AssetManager {
private AssetRepository mAssetRepository;
/** /**
* This initializes the static field {@link AssetManager#sSystem} which is used * This initializes the static field {@link AssetManager#sSystem} which is used
* by methods who get a global asset manager using {@link AssetManager#getSystem()}. * by methods who get a global asset manager using {@link AssetManager#getSystem()}.
@@ -48,6 +49,14 @@ public class BridgeAssetManager extends AssetManager {
AssetManager.sSystem = null; AssetManager.sSystem = null;
} }
private BridgeAssetManager() { public void setAssetRepository(AssetRepository assetRepository) {
mAssetRepository = assetRepository;
}
public AssetRepository getAssetRepository() {
return mAssetRepository;
}
public BridgeAssetManager() {
} }
} }

View File

@@ -18,21 +18,28 @@ package android.graphics;
import com.android.annotations.NonNull; import com.android.annotations.NonNull;
import com.android.annotations.Nullable; import com.android.annotations.Nullable;
import com.android.ide.common.rendering.api.AssetRepository;
import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.LayoutLog;
import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.content.res.BridgeAssetManager;
import java.awt.Font; import java.awt.Font;
import java.awt.FontFormatException; import java.awt.FontFormatException;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner; import java.util.Scanner;
import java.util.Set; 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_DELTA = 300;
public static final int BOLD_FONT_WEIGHT = 700; 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 FONT_SUFFIX_ITALIC = "Italic.ttf";
private static final String FN_ALL_FONTS_LIST = "fontsInSdk.txt"; 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. * A class associating {@link Font} with its metadata.
@@ -194,7 +219,7 @@ public class FontFamily_Delegate {
try { try {
return Font.createFont(Font.TRUETYPE_FONT, f); return Font.createFont(Font.TRUETYPE_FONT, f);
} catch (Exception e) { } 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. // 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 // We wait for a case where font is being used. Only then we try to log the
// warning. // warning.
@@ -281,8 +306,74 @@ public class FontFamily_Delegate {
@LayoutlibDelegate @LayoutlibDelegate
/*package*/ static boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr, String path) { /*package*/ static boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr, String path) {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, FontFamily_Delegate ffd = sManager.getDelegate(nativeFamily);
"Typeface.createFromAsset is not supported.", null, null); 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,
"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; return false;
} }

View File

@@ -18,6 +18,7 @@ package com.android.layoutlib.bridge.android;
import android.os.IBinder; import android.os.IBinder;
import com.android.annotations.Nullable; 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.ILayoutPullParser;
import com.android.ide.common.rendering.api.IProjectCallback; import com.android.ide.common.rendering.api.IProjectCallback;
import com.android.ide.common.rendering.api.LayoutLog; 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.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.content.res.BridgeAssetManager;
import android.content.res.BridgeResources; import android.content.res.BridgeResources;
import android.content.res.BridgeTypedArray; import android.content.res.BridgeTypedArray;
import android.content.res.Configuration; import android.content.res.Configuration;
@@ -102,6 +104,7 @@ public final class BridgeContext extends Context {
* used to populate the mViewKeyMap. * used to populate the mViewKeyMap.
*/ */
private final HashMap<Object, Object> mViewKeyHelpMap = new HashMap<Object, Object>(); private final HashMap<Object, Object> mViewKeyHelpMap = new HashMap<Object, Object>();
private final BridgeAssetManager mAssets;
private Resources mSystemResources; private Resources mSystemResources;
private final Object mProjectKey; private final Object mProjectKey;
private final DisplayMetrics mMetrics; private final DisplayMetrics mMetrics;
@@ -141,6 +144,7 @@ public final class BridgeContext extends Context {
*/ */
public BridgeContext(Object projectKey, DisplayMetrics metrics, public BridgeContext(Object projectKey, DisplayMetrics metrics,
RenderResources renderResources, RenderResources renderResources,
AssetRepository assets,
IProjectCallback projectCallback, IProjectCallback projectCallback,
Configuration config, Configuration config,
int targetSdkVersion, int targetSdkVersion,
@@ -151,6 +155,8 @@ public final class BridgeContext extends Context {
mRenderResources = renderResources; mRenderResources = renderResources;
mConfig = config; mConfig = config;
mAssets = new BridgeAssetManager();
mAssets.setAssetRepository(assets);
mApplicationInfo = new ApplicationInfo(); mApplicationInfo = new ApplicationInfo();
mApplicationInfo.targetSdkVersion = targetSdkVersion; mApplicationInfo.targetSdkVersion = targetSdkVersion;
@@ -1088,9 +1094,8 @@ public final class BridgeContext extends Context {
} }
@Override @Override
public AssetManager getAssets() { public BridgeAssetManager getAssets() {
// pass return mAssets;
return null;
} }
@Override @Override

View File

@@ -35,7 +35,6 @@ import com.android.resources.ScreenSize;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.HandlerThread_Delegate; import android.os.HandlerThread_Delegate;
import android.os.Looper;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.view.ViewConfiguration_Accessor; import android.view.ViewConfiguration_Accessor;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
@@ -71,7 +70,7 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
/** /**
* Creates a renderAction. * Creates a renderAction.
* <p> * <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)} * call to {@link RenderAction#acquire(long)}
* *
* @param params the RenderParams. This must be a copy that the action can keep * @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 // build the context
mContext = new BridgeContext(mParams.getProjectKey(), metrics, resources, mContext = new BridgeContext(mParams.getProjectKey(), metrics, resources,
mParams.getProjectCallback(), getConfiguration(), mParams.getTargetSdkVersion(), mParams.getAssets(), mParams.getProjectCallback(), getConfiguration(),
mParams.isRtlSupported()); mParams.getTargetSdkVersion(), mParams.isRtlSupported());
setUp(); 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. * 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 * 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 * After scene actions have taken place, only one call to {@link #release()} must be
* done. * 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. * Acquire the lock so that the scene can be acted upon.
* <p> * <p>
* This returns null if the lock was just acquired, otherwise it returns * 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. * instance (see {@link Result#getStatus()}) if an error occurred.
* *
* @param timeout the time to wait if another rendering is happening. * @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) { private Result acquireLock(long timeout) {
ReentrantLock lock = Bridge.getLock(); ReentrantLock lock = Bridge.getLock();
if (lock.isHeldByCurrentThread() == false) { if (!lock.isHeldByCurrentThread()) {
try { try {
boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS); boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
if (acquired == false) { if (!acquired) {
return ERROR_TIMEOUT.createResult(); return ERROR_TIMEOUT.createResult();
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
@@ -308,7 +307,7 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
*/ */
protected void checkLock() { protected void checkLock() {
ReentrantLock lock = Bridge.getLock(); ReentrantLock lock = Bridge.getLock();
if (lock.isHeldByCurrentThread() == false) { if (!lock.isHeldByCurrentThread()) {
throw new IllegalStateException("scene must be acquired first. see #acquire(long)"); throw new IllegalStateException("scene must be acquired first. see #acquire(long)");
} }
if (sCurrentContext != mContext) { if (sCurrentContext != mContext) {
@@ -347,6 +346,7 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
config.screenWidthDp = hardwareConfig.getScreenWidth() / density.getDpiValue(); config.screenWidthDp = hardwareConfig.getScreenWidth() / density.getDpiValue();
config.screenHeightDp = hardwareConfig.getScreenHeight() / density.getDpiValue(); config.screenHeightDp = hardwareConfig.getScreenHeight() / density.getDpiValue();
if (config.screenHeightDp < config.screenWidthDp) { if (config.screenHeightDp < config.screenWidthDp) {
//noinspection SuspiciousNameCombination
config.smallestScreenWidthDp = config.screenHeightDp; config.smallestScreenWidthDp = config.screenHeightDp;
} else { } else {
config.smallestScreenWidthDp = config.screenWidthDp; config.smallestScreenWidthDp = config.screenWidthDp;
@@ -367,6 +367,7 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
config.orientation = Configuration.ORIENTATION_LANDSCAPE; config.orientation = Configuration.ORIENTATION_LANDSCAPE;
break; break;
case SQUARE: case SQUARE:
//noinspection deprecation
config.orientation = Configuration.ORIENTATION_SQUARE; config.orientation = Configuration.ORIENTATION_SQUARE;
break; break;
} }