Loading tools/layoutlib/bridge/src/android/content/res/BridgeAssetManager.java +12 −3 Original line number Diff line number Diff line Loading @@ -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()}. Loading @@ -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() { } } tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java +96 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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. Loading Loading @@ -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. Loading Loading @@ -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; } Loading tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +8 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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, Loading @@ -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; Loading Loading @@ -1093,9 +1099,8 @@ public final class BridgeContext extends Context { } @Override public AssetManager getAssets() { // pass return null; public BridgeAssetManager getAssets() { return mAssets; } @Override Loading tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java +10 −9 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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(); Loading @@ -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. Loading Loading @@ -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. Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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; Loading @@ -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; } Loading Loading
tools/layoutlib/bridge/src/android/content/res/BridgeAssetManager.java +12 −3 Original line number Diff line number Diff line Loading @@ -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()}. Loading @@ -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() { } }
tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java +96 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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. Loading Loading @@ -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. Loading Loading @@ -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; } Loading
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +8 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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, Loading @@ -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; Loading Loading @@ -1093,9 +1099,8 @@ public final class BridgeContext extends Context { } @Override public AssetManager getAssets() { // pass return null; public BridgeAssetManager getAssets() { return mAssets; } @Override Loading
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java +10 −9 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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(); Loading @@ -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. Loading Loading @@ -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. Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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; Loading @@ -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; } Loading