Merge "Add guard around getXmlFileParser() call" into mnc-ub-dev
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index d2c21cb..ef681d2 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -72,7 +72,7 @@
  */
 public final class BridgeTypedArray extends TypedArray {
 
-    private final BridgeResources mBridgeResources;
+    private final Resources mBridgeResources;
     private final BridgeContext mContext;
     private final boolean mPlatformFile;
 
@@ -85,7 +85,7 @@
     @Nullable
     private int[] mEmptyIds;
 
-    public BridgeTypedArray(BridgeResources resources, BridgeContext context, int len,
+    public BridgeTypedArray(Resources resources, BridgeContext context, int len,
             boolean platformFile) {
         super(resources, null, null, 0);
         mBridgeResources = resources;
@@ -762,7 +762,8 @@
         if (resVal instanceof ArrayResourceValue) {
             ArrayResourceValue array = (ArrayResourceValue) resVal;
             int count = array.getElementCount();
-            return count >= 0 ? mBridgeResources.fillValues(array, new CharSequence[count]) : null;
+            return count >= 0 ? Resources_Delegate.fillValues(mBridgeResources, array, new CharSequence[count]) :
+                    null;
         }
         int id = getResourceId(index, 0);
         String resIdMessage = id > 0 ? " (resource id 0x" + Integer.toHexString(id) + ')' : "";
@@ -1010,7 +1011,6 @@
     }
 
     static TypedArray obtain(Resources res, int len) {
-        return res instanceof BridgeResources ?
-                new BridgeTypedArray(((BridgeResources) res), null, len, true) : null;
+        return new BridgeTypedArray(res, null, len, true);
     }
 }
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
similarity index 63%
rename from tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
rename to tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
index 0e39243..6e8e42f 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -28,8 +28,10 @@
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
 import com.android.layoutlib.bridge.impl.ParserFactory;
 import com.android.layoutlib.bridge.impl.ResourceHelper;
+import com.android.layoutlib.bridge.util.NinePatchInputStream;
 import com.android.ninepatch.NinePatch;
 import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 import com.android.util.Pair;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -37,6 +39,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.res.Resources.NotFoundException;
+import android.content.res.Resources.Theme;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
@@ -49,131 +53,102 @@
 import java.io.InputStream;
 import java.util.Iterator;
 
-public final class BridgeResources extends Resources {
+@SuppressWarnings("deprecation")
+public class Resources_Delegate {
 
-    private BridgeContext mContext;
-    private LayoutlibCallback mLayoutlibCallback;
-    private boolean[] mPlatformResourceFlag = new boolean[1];
-    private TypedValue mTmpValue = new TypedValue();
+    private static boolean[] mPlatformResourceFlag = new boolean[1];
 
-    /**
-     * Simpler wrapper around FileInputStream. This is used when the input stream represent
-     * not a normal bitmap but a nine patch.
-     * This is useful when the InputStream is created in a method but used in another that needs
-     * to know whether this is 9-patch or not, such as BitmapFactory.
-     */
-    public class NinePatchInputStream extends FileInputStream {
-        private boolean mFakeMarkSupport = true;
-        public NinePatchInputStream(File file) throws FileNotFoundException {
-            super(file);
-        }
-
-        @Override
-        public boolean markSupported() {
-            if (mFakeMarkSupport) {
-                // this is needed so that BitmapFactory doesn't wrap this in a BufferedInputStream.
-                return true;
-            }
-
-            return super.markSupported();
-        }
-
-        public void disableFakeMarkSupport() {
-            // disable fake mark support so that in case codec actually try to use them
-            // we don't lie to them.
-            mFakeMarkSupport = false;
-        }
-    }
-
-    /**
-     * This initializes the static field {@link Resources#mSystem} which is used
-     * by methods who get global resources using {@link Resources#getSystem()}.
-     * <p/>
-     * They will end up using our bridge resources.
-     * <p/>
-     * {@link Bridge} calls this method after setting up a new bridge.
-     */
     public static Resources initSystem(BridgeContext context,
             AssetManager assets,
             DisplayMetrics metrics,
             Configuration config,
             LayoutlibCallback layoutlibCallback) {
-        return Resources.mSystem = new BridgeResources(context,
-                assets,
-                metrics,
-                config,
-                layoutlibCallback);
+        Resources resources = new Resources(assets, metrics, config);
+        resources.mContext = context;
+        resources.mLayoutlibCallback = layoutlibCallback;
+        return Resources.mSystem = resources;
     }
 
     /**
-     * Disposes the static {@link Resources#mSystem} to make sure we don't leave objects
-     * around that would prevent us from unloading the library.
+     * Disposes the static {@link Resources#mSystem} to make sure we don't leave objects around that
+     * would prevent us from unloading the library.
      */
     public static void disposeSystem() {
-        if (Resources.mSystem instanceof BridgeResources) {
-            ((BridgeResources)(Resources.mSystem)).mContext = null;
-            ((BridgeResources)(Resources.mSystem)).mLayoutlibCallback = null;
-        }
+        Resources.mSystem.mContext = null;
+        Resources.mSystem.mLayoutlibCallback = null;
         Resources.mSystem = null;
     }
 
-    private BridgeResources(BridgeContext context, AssetManager assets, DisplayMetrics metrics,
-            Configuration config, LayoutlibCallback layoutlibCallback) {
-        super(assets, metrics, config);
-        mContext = context;
-        mLayoutlibCallback = layoutlibCallback;
+    public static BridgeTypedArray newTypeArray(Resources resources, int numEntries,
+            boolean platformFile) {
+        return new BridgeTypedArray(resources, resources.mContext, numEntries, platformFile);
     }
 
-    public BridgeTypedArray newTypeArray(int numEntries, boolean platformFile) {
-        return new BridgeTypedArray(this, mContext, numEntries, platformFile);
-    }
-
-    private Pair<String, ResourceValue> getResourceValue(int id, boolean[] platformResFlag_out) {
+    private static Pair<String, ResourceValue> getResourceValue(Resources resources, int id,
+            boolean[] platformResFlag_out) {
         // first get the String related to this id in the framework
         Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
 
+        // Set the layoutlib callback and context for resources
+        if (resources != Resources.mSystem && resources.mLayoutlibCallback == null) {
+            resources.mLayoutlibCallback = Resources.mSystem.mLayoutlibCallback;
+            resources.mContext = Resources.mSystem.mContext;
+        }
+
         if (resourceInfo != null) {
             platformResFlag_out[0] = true;
             String attributeName = resourceInfo.getSecond();
 
-            return Pair.of(attributeName, mContext.getRenderResources().getFrameworkResource(
-                    resourceInfo.getFirst(), attributeName));
+            return Pair.of(attributeName,
+                    resources.mContext.getRenderResources().getFrameworkResource(
+                            resourceInfo.getFirst(), attributeName));
         }
 
         // didn't find a match in the framework? look in the project.
-        if (mLayoutlibCallback != null) {
-            resourceInfo = mLayoutlibCallback.resolveResourceId(id);
+        if (resources.mLayoutlibCallback != null) {
+            resourceInfo = resources.mLayoutlibCallback.resolveResourceId(id);
 
             if (resourceInfo != null) {
                 platformResFlag_out[0] = false;
                 String attributeName = resourceInfo.getSecond();
 
-                return Pair.of(attributeName, mContext.getRenderResources().getProjectResource(
-                        resourceInfo.getFirst(), attributeName));
+                return Pair.of(attributeName,
+                        resources.mContext.getRenderResources().getProjectResource(
+                                resourceInfo.getFirst(), attributeName));
             }
         }
 
         return null;
     }
 
-    @Override
-    public Drawable getDrawable(int id, Theme theme) {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static Drawable getDrawable(Resources resources, int id) {
+        return getDrawable(resources, id, null);
+    }
+
+    @LayoutlibDelegate
+    static Drawable getDrawable(Resources resources, int id, Theme theme) {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
-            return ResourceHelper.getDrawable(value.getSecond(), mContext, theme);
+            return ResourceHelper.getDrawable(value.getSecond(), resources.mContext, theme);
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public int getColor(int id, Theme theme) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static int getColor(Resources resources, int id) {
+        return getColor(resources, id, null);
+    }
+
+    @LayoutlibDelegate
+    static int getColor(Resources resources, int id, Theme theme) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             ResourceValue resourceValue = value.getSecond();
@@ -185,7 +160,7 @@
                 String message;
                 if (new File(resourceValue.getValue()).isFile()) {
                     String resource = (resourceValue.isFramework() ? "@android:" : "@") + "color/"
-                      + resourceValue.getName();
+                            + resourceValue.getName();
                     message = "Hexadecimal color expected, found Color State List for " + resource;
                 } else {
                     message = e.getMessage();
@@ -198,31 +173,57 @@
         // Suppress possible NPE. getColorStateList will never return null, it will instead
         // throw an exception, but intelliJ can't figure that out
         //noinspection ConstantConditions
-        return getColorStateList(id, theme).getDefaultColor();
+        return getColorStateList(resources, id, theme).getDefaultColor();
     }
 
-    @Override
-    public ColorStateList getColorStateList(int id, Theme theme) throws NotFoundException {
-        Pair<String, ResourceValue> resValue = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static ColorStateList getColorStateList(Resources resources, int id) throws NotFoundException {
+        return getColorStateList(resources, id, null);
+    }
+
+    @LayoutlibDelegate
+    static ColorStateList getColorStateList(Resources resources, int id, Theme theme)
+            throws NotFoundException {
+        Pair<String, ResourceValue> resValue =
+                getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (resValue != null) {
             ColorStateList stateList = ResourceHelper.getColorStateList(resValue.getSecond(),
-                    mContext);
+                    resources.mContext);
             if (stateList != null) {
                 return stateList.obtainForTheme(theme);
             }
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public CharSequence getText(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static CharSequence getText(Resources resources, int id, CharSequence def) {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+
+        if (value != null) {
+            ResourceValue resValue = value.getSecond();
+
+            assert resValue != null;
+            if (resValue != null) {
+                String v = resValue.getValue();
+                if (v != null) {
+                    return v;
+                }
+            }
+        }
+
+        return def;
+    }
+
+    @LayoutlibDelegate
+    static CharSequence getText(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -237,38 +238,38 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public CharSequence[] getTextArray(int id) throws NotFoundException {
-        ResourceValue resValue = getArrayResourceValue(id);
+    @LayoutlibDelegate
+    static CharSequence[] getTextArray(Resources resources, int id) throws NotFoundException {
+        ResourceValue resValue = getArrayResourceValue(resources, id);
         if (resValue == null) {
             // Error already logged by getArrayResourceValue.
             return new CharSequence[0];
         } else if (!(resValue instanceof ArrayResourceValue)) {
             return new CharSequence[]{
-                    resolveReference(resValue.getValue(), resValue.isFramework())};
+                    resolveReference(resources, resValue.getValue(), resValue.isFramework())};
         }
         ArrayResourceValue arv = ((ArrayResourceValue) resValue);
-        return fillValues(arv, new CharSequence[arv.getElementCount()]);
+        return fillValues(resources, arv, new CharSequence[arv.getElementCount()]);
     }
 
-    @Override
-    public String[] getStringArray(int id) throws NotFoundException {
-        ResourceValue resValue = getArrayResourceValue(id);
+    @LayoutlibDelegate
+    static String[] getStringArray(Resources resources, int id) throws NotFoundException {
+        ResourceValue resValue = getArrayResourceValue(resources, id);
         if (resValue == null) {
             // Error already logged by getArrayResourceValue.
             return new String[0];
         } else if (!(resValue instanceof ArrayResourceValue)) {
             return new String[]{
-                    resolveReference(resValue.getValue(), resValue.isFramework())};
+                    resolveReference(resources, resValue.getValue(), resValue.isFramework())};
         }
         ArrayResourceValue arv = ((ArrayResourceValue) resValue);
-        return fillValues(arv, new String[arv.getElementCount()]);
+        return fillValues(resources, arv, new String[arv.getElementCount()]);
     }
 
     /**
@@ -276,25 +277,26 @@
      * always Strings. The ideal signature for the method should be &lt;T super String&gt;, but java
      * generics don't support it.
      */
-    <T extends CharSequence> T[] fillValues(ArrayResourceValue resValue, T[] values) {
+    static <T extends CharSequence> T[] fillValues(Resources resources, ArrayResourceValue resValue,
+            T[] values) {
         int i = 0;
         for (Iterator<String> iterator = resValue.iterator(); iterator.hasNext(); i++) {
             @SuppressWarnings("unchecked")
-            T s = (T) resolveReference(iterator.next(), resValue.isFramework());
+            T s = (T) resolveReference(resources, iterator.next(), resValue.isFramework());
             values[i] = s;
         }
         return values;
     }
 
-    @Override
-    public int[] getIntArray(int id) throws NotFoundException {
-        ResourceValue rv = getArrayResourceValue(id);
+    @LayoutlibDelegate
+    static int[] getIntArray(Resources resources, int id) throws NotFoundException {
+        ResourceValue rv = getArrayResourceValue(resources, id);
         if (rv == null) {
             // Error already logged by getArrayResourceValue.
             return new int[0];
         } else if (!(rv instanceof ArrayResourceValue)) {
             // This is an older IDE that can only give us the first element of the array.
-            String firstValue = resolveReference(rv.getValue(), rv.isFramework());
+            String firstValue = resolveReference(resources, rv.getValue(), rv.isFramework());
             try {
                 return new int[]{getInt(firstValue)};
             } catch (NumberFormatException e) {
@@ -308,7 +310,7 @@
         int[] values = new int[resValue.getElementCount()];
         int i = 0;
         for (Iterator<String> iterator = resValue.iterator(); iterator.hasNext(); i++) {
-            String element = resolveReference(iterator.next(), resValue.isFramework());
+            String element = resolveReference(resources, iterator.next(), resValue.isFramework());
             try {
                 values[i] = getInt(element);
             } catch (NumberFormatException e) {
@@ -328,11 +330,13 @@
      * method returns the ResourceValue. This happens on older versions of the IDE, which did not
      * parse the array resources properly.
      * <p/>
+     *
      * @throws NotFoundException if no resource if found
      */
     @Nullable
-    private ResourceValue getArrayResourceValue(int id) throws NotFoundException {
-        Pair<String, ResourceValue> v = getResourceValue(id, mPlatformResourceFlag);
+    private static ResourceValue getArrayResourceValue(Resources resources, int id)
+            throws NotFoundException {
+        Pair<String, ResourceValue> v = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (v != null) {
             ResourceValue resValue = v.getSecond();
@@ -358,19 +362,20 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
     @NonNull
-    private String resolveReference(@NonNull String ref, boolean forceFrameworkOnly) {
+    private static String resolveReference(Resources resources, @NonNull String ref,
+            boolean forceFrameworkOnly) {
         if (ref.startsWith(SdkConstants.PREFIX_RESOURCE_REF) || ref.startsWith
                 (SdkConstants.PREFIX_THEME_REF)) {
             ResourceValue rv =
-                    mContext.getRenderResources().findResValue(ref, forceFrameworkOnly);
-            rv = mContext.getRenderResources().resolveResValue(rv);
+                    resources.mContext.getRenderResources().findResValue(ref, forceFrameworkOnly);
+            rv = resources.mContext.getRenderResources().resolveResValue(rv);
             if (rv != null) {
                 return rv.getValue();
             } else {
@@ -382,9 +387,9 @@
         return ref;
     }
 
-    @Override
-    public XmlResourceParser getLayout(int id) throws NotFoundException {
-        Pair<String, ResourceValue> v = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static XmlResourceParser getLayout(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> v = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (v != null) {
             ResourceValue value = v.getSecond();
@@ -392,8 +397,8 @@
 
             try {
                 // check if the current parser can provide us with a custom parser.
-                if (mPlatformResourceFlag[0] == false) {
-                    parser = mLayoutlibCallback.getParser(value);
+                if (!mPlatformResourceFlag[0]) {
+                    parser = resources.mLayoutlibCallback.getParser(value);
                 }
 
                 // create a new one manually if needed.
@@ -407,7 +412,8 @@
                 }
 
                 if (parser != null) {
-                    return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+                    return new BridgeXmlBlockParser(parser, resources.mContext,
+                            mPlatformResourceFlag[0]);
                 }
             } catch (XmlPullParserException e) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
@@ -420,19 +426,19 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public XmlResourceParser getAnimation(int id) throws NotFoundException {
-        Pair<String, ResourceValue> v = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static XmlResourceParser getAnimation(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> v = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (v != null) {
             ResourceValue value = v.getSecond();
-            XmlPullParser parser = null;
+            XmlPullParser parser;
 
             try {
                 File xml = new File(value.getValue());
@@ -441,7 +447,8 @@
                     // give that to our XmlBlockParser
                     parser = ParserFactory.create(xml);
 
-                    return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+                    return new BridgeXmlBlockParser(parser, resources.mContext,
+                            mPlatformResourceFlag[0]);
                 }
             } catch (XmlPullParserException e) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
@@ -454,26 +461,31 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
-        return mContext.obtainStyledAttributes(set, attrs);
+    @LayoutlibDelegate
+    static TypedArray obtainAttributes(Resources resources, AttributeSet set, int[] attrs) {
+        return resources.mContext.obtainStyledAttributes(set, attrs);
     }
 
-    @Override
-    public TypedArray obtainTypedArray(int id) throws NotFoundException {
+    @LayoutlibDelegate
+    static TypedArray obtainAttributes(Resources resources, Resources.Theme theme, AttributeSet
+            set, int[] attrs) {
+        return Resources.obtainAttributes_Original(resources, theme, set, attrs);
+    }
+
+    @LayoutlibDelegate
+    static TypedArray obtainTypedArray(Resources resources, int id) throws NotFoundException {
         throw new UnsupportedOperationException();
     }
 
-
-    @Override
-    public float getDimension(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static float getDimension(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -488,26 +500,26 @@
                     } else if (v.equals(BridgeConstants.WRAP_CONTENT)) {
                         return LayoutParams.WRAP_CONTENT;
                     }
-
+                    TypedValue tmpValue = new TypedValue();
                     if (ResourceHelper.parseFloatAttribute(
-                            value.getFirst(), v, mTmpValue, true /*requireUnit*/) &&
-                            mTmpValue.type == TypedValue.TYPE_DIMENSION) {
-                        return mTmpValue.getDimension(getDisplayMetrics());
+                            value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+                            tmpValue.type == TypedValue.TYPE_DIMENSION) {
+                        return tmpValue.getDimension(resources.getDisplayMetrics());
                     }
                 }
             }
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return 0;
     }
 
-    @Override
-    public int getDimensionPixelOffset(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static int getDimensionPixelOffset(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -516,26 +528,27 @@
             if (resValue != null) {
                 String v = resValue.getValue();
                 if (v != null) {
+                    TypedValue tmpValue = new TypedValue();
                     if (ResourceHelper.parseFloatAttribute(
-                            value.getFirst(), v, mTmpValue, true /*requireUnit*/) &&
-                            mTmpValue.type == TypedValue.TYPE_DIMENSION) {
-                        return TypedValue.complexToDimensionPixelOffset(mTmpValue.data,
-                                getDisplayMetrics());
+                            value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+                            tmpValue.type == TypedValue.TYPE_DIMENSION) {
+                        return TypedValue.complexToDimensionPixelOffset(tmpValue.data,
+                                resources.getDisplayMetrics());
                     }
                 }
             }
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return 0;
     }
 
-    @Override
-    public int getDimensionPixelSize(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static int getDimensionPixelSize(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -544,26 +557,27 @@
             if (resValue != null) {
                 String v = resValue.getValue();
                 if (v != null) {
+                    TypedValue tmpValue = new TypedValue();
                     if (ResourceHelper.parseFloatAttribute(
-                            value.getFirst(), v, mTmpValue, true /*requireUnit*/) &&
-                            mTmpValue.type == TypedValue.TYPE_DIMENSION) {
-                        return TypedValue.complexToDimensionPixelSize(mTmpValue.data,
-                                getDisplayMetrics());
+                            value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+                            tmpValue.type == TypedValue.TYPE_DIMENSION) {
+                        return TypedValue.complexToDimensionPixelSize(tmpValue.data,
+                                resources.getDisplayMetrics());
                     }
                 }
             }
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return 0;
     }
 
-    @Override
-    public int getInteger(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static int getInteger(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -582,15 +596,15 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return 0;
     }
 
-    @Override
-    public boolean getBoolean(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static boolean getBoolean(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -605,61 +619,62 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return false;
     }
 
-    @Override
-    public String getResourceEntryName(int resid) throws NotFoundException {
+    @LayoutlibDelegate
+    static String getResourceEntryName(Resources resources, int resid) throws NotFoundException {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    public String getResourceName(int resid) throws NotFoundException {
+    @LayoutlibDelegate
+    static String getResourceName(Resources resources, int resid) throws NotFoundException {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    public String getResourceTypeName(int resid) throws NotFoundException {
+    @LayoutlibDelegate
+    static String getResourceTypeName(Resources resources, int resid) throws NotFoundException {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    public String getString(int id, Object... formatArgs) throws NotFoundException {
-        String s = getString(id);
+    @LayoutlibDelegate
+    static String getString(Resources resources, int id, Object... formatArgs)
+            throws NotFoundException {
+        String s = getString(resources, id);
         if (s != null) {
             return String.format(s, formatArgs);
 
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public String getString(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static String getString(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null && value.getSecond().getValue() != null) {
             return value.getSecond().getValue();
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public void getValue(int id, TypedValue outValue, boolean resolveRefs)
+    @LayoutlibDelegate
+    static void getValue(Resources resources, int id, TypedValue outValue, boolean resolveRefs)
             throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             ResourceValue resVal = value.getSecond();
@@ -672,7 +687,7 @@
                 }
                 if (resVal instanceof DensityBasedResourceValue) {
                     outValue.density =
-                      ((DensityBasedResourceValue) resVal).getResourceDensity().getDpiValue();
+                            ((DensityBasedResourceValue) resVal).getResourceDensity().getDpiValue();
                 }
 
                 // else it's a string
@@ -683,18 +698,18 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
     }
 
-    @Override
-    public void getValue(String name, TypedValue outValue, boolean resolveRefs)
+    @LayoutlibDelegate
+    static void getValue(Resources resources, String name, TypedValue outValue, boolean resolveRefs)
             throws NotFoundException {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    public XmlResourceParser getXml(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static XmlResourceParser getXml(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             String v = value.getSecond().getValue();
@@ -706,7 +721,8 @@
                     try {
                         XmlPullParser parser = ParserFactory.create(f);
 
-                        return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+                        return new BridgeXmlBlockParser(parser, resources.mContext,
+                                mPlatformResourceFlag[0]);
                     } catch (XmlPullParserException e) {
                         NotFoundException newE = new NotFoundException();
                         newE.initCause(e);
@@ -721,25 +737,31 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public XmlResourceParser loadXmlResourceParser(String file, int id,
+    @LayoutlibDelegate
+    static XmlResourceParser loadXmlResourceParser(Resources resources, int id,
+            String type) throws NotFoundException {
+        return resources.loadXmlResourceParser_Original(id, type);
+    }
+
+    @LayoutlibDelegate
+    static XmlResourceParser loadXmlResourceParser(Resources resources, String file, int id,
             int assetCookie, String type) throws NotFoundException {
         // even though we know the XML file to load directly, we still need to resolve the
         // id so that we can know if it's a platform or project resource.
         // (mPlatformResouceFlag will get the result and will be used later).
-        getResourceValue(id, mPlatformResourceFlag);
+        getResourceValue(resources, id, mPlatformResourceFlag);
 
         File f = new File(file);
         try {
             XmlPullParser parser = ParserFactory.create(f);
 
-            return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+            return new BridgeXmlBlockParser(parser, resources.mContext, mPlatformResourceFlag[0]);
         } catch (XmlPullParserException e) {
             NotFoundException newE = new NotFoundException();
             newE.initCause(e);
@@ -751,9 +773,9 @@
         }
     }
 
-    @Override
-    public InputStream openRawResource(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static InputStream openRawResource(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             String path = value.getSecond().getValue();
@@ -780,15 +802,16 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
-        getValue(id, value, true);
+    @LayoutlibDelegate
+    static InputStream openRawResource(Resources resources, int id, TypedValue value) throws
+            NotFoundException {
+        getValue(resources, id, value, true);
 
         String path = value.string.toString();
 
@@ -812,23 +835,27 @@
         throw new NotFoundException();
     }
 
-    @Override
-    public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
+    @LayoutlibDelegate
+    static AssetFileDescriptor openRawResourceFd(Resources resources, int id) throws
+            NotFoundException {
         throw new UnsupportedOperationException();
     }
 
     /**
-     * Builds and throws a {@link Resources.NotFoundException} based on a resource id and a resource type.
+     * Builds and throws a {@link Resources.NotFoundException} based on a resource id and a resource
+     * type.
+     *
      * @param id the id of the resource
+     *
      * @throws NotFoundException
      */
-    private void throwException(int id) throws NotFoundException {
+    private static void throwException(Resources resources, int id) throws NotFoundException {
         // first get the String related to this id in the framework
         Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
 
         // if the name is unknown in the framework, get it from the custom view loader.
-        if (resourceInfo == null && mLayoutlibCallback != null) {
-            resourceInfo = mLayoutlibCallback.resolveResourceId(id);
+        if (resourceInfo == null && resources.mLayoutlibCallback != null) {
+            resourceInfo = resources.mLayoutlibCallback.resolveResourceId(id);
         }
 
         String message;
@@ -844,7 +871,7 @@
         throw new NotFoundException(message);
     }
 
-    private int getInt(String v) throws NumberFormatException {
+    private static int getInt(String v) throws NumberFormatException {
         int radix = 10;
         if (v.startsWith("0x")) {
             v = v.substring(2);
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
index 8d5863b..8bd2a7a 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
@@ -23,7 +23,7 @@
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.annotation.Nullable;
-import android.content.res.BridgeResources.NinePatchInputStream;
+import com.android.layoutlib.bridge.util.NinePatchInputStream;
 import android.graphics.BitmapFactory.Options;
 import android.graphics.Bitmap_Delegate.BitmapCreateFlags;
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
index d0dd22f..a10ac00 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
@@ -388,21 +388,18 @@
     @LayoutlibDelegate
     /*package*/ static void native_addRoundRect(long nPath, float left, float top, float right,
             float bottom, float[] radii, int dir) {
-        // Java2D doesn't support different rounded corners in each corner, so just use the
-        // first value.
-        native_addRoundRect(nPath, left, top, right, bottom, radii[0], radii[1], dir);
 
-        // there can be a case where this API is used but with similar values for all corners, so
-        // in that case we don't warn.
-        // we only care if 2 corners are different so just compare to the next one.
-        for (int i = 0 ; i < 3 ; i++) {
-            if (radii[i * 2] != radii[(i + 1) * 2] || radii[i * 2 + 1] != radii[(i + 1) * 2 + 1]) {
-                Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                        "Different corner sizes are not supported in Path.addRoundRect.",
-                        null, null /*data*/);
-                break;
-            }
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
         }
+
+        float[] cornerDimensions = new float[radii.length];
+        for (int i = 0; i < radii.length; i++) {
+            cornerDimensions[i] = 2 * radii[i];
+        }
+        pathDelegate.mPath.append(new RoundRectangle(left, top, right - left, bottom - top,
+                cornerDimensions), false);
     }
 
     @LayoutlibDelegate
diff --git a/tools/layoutlib/bridge/src/android/graphics/RoundRectangle.java b/tools/layoutlib/bridge/src/android/graphics/RoundRectangle.java
new file mode 100644
index 0000000..edd36e5
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/RoundRectangle.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2016 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 android.graphics;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RectangularShape;
+import java.awt.geom.RoundRectangle2D;
+import java.util.EnumSet;
+import java.util.NoSuchElementException;
+
+/**
+ * Defines a rectangle with rounded corners, where the sizes of the corners
+ * are potentially different.
+ */
+public class RoundRectangle extends RectangularShape {
+    public double x;
+    public double y;
+    public double width;
+    public double height;
+    public double ulWidth;
+    public double ulHeight;
+    public double urWidth;
+    public double urHeight;
+    public double lrWidth;
+    public double lrHeight;
+    public double llWidth;
+    public double llHeight;
+
+    private enum Zone {
+        CLOSE_OUTSIDE,
+        CLOSE_INSIDE,
+        MIDDLE,
+        FAR_INSIDE,
+        FAR_OUTSIDE
+    }
+
+    private final EnumSet<Zone> close = EnumSet.of(Zone.CLOSE_OUTSIDE, Zone.CLOSE_INSIDE);
+    private final EnumSet<Zone> far = EnumSet.of(Zone.FAR_OUTSIDE, Zone.FAR_INSIDE);
+
+    /**
+     * @param cornerDimensions array of 8 floating-point number corresponding to the width and
+     * the height of each corner in the following order: upper-left, upper-right, lower-right,
+     * lower-left. It assumes for the size the same convention as {@link RoundRectangle2D}, that
+     * is that the width and height of a corner correspond to the total width and height of the
+     * ellipse that corner is a quarter of.
+     */
+    public RoundRectangle(float x, float y, float width, float height, float[] cornerDimensions) {
+        if (cornerDimensions.length != 8) {
+            throw new IllegalArgumentException("The array of corner dimensions must have eight " +
+                    "elements");
+        }
+
+        this.x = x;
+        this.y = y;
+        this.width = width;
+        this.height = height;
+
+        float[] dimensions = cornerDimensions.clone();
+        // If a value is negative, the corresponding corner is squared
+        for (int i = 0; i < dimensions.length; i += 2) {
+            if (dimensions[i] < 0 || dimensions[i + 1] < 0) {
+                dimensions[i] = 0;
+                dimensions[i + 1] = 0;
+            }
+        }
+
+        double topCornerWidth = (dimensions[0] + dimensions[2]) / 2d;
+        double bottomCornerWidth = (dimensions[4] + dimensions[6]) / 2d;
+        double leftCornerHeight = (dimensions[1] + dimensions[7]) / 2d;
+        double rightCornerHeight = (dimensions[3] + dimensions[5]) / 2d;
+
+        // Rescale the corner dimensions if they are bigger than the rectangle
+        double scale = Math.min(1.0, width / topCornerWidth);
+        scale = Math.min(scale, width / bottomCornerWidth);
+        scale = Math.min(scale, height / leftCornerHeight);
+        scale = Math.min(scale, height / rightCornerHeight);
+
+        this.ulWidth = dimensions[0] * scale;
+        this.ulHeight = dimensions[1] * scale;
+        this.urWidth = dimensions[2] * scale;
+        this.urHeight = dimensions[3] * scale;
+        this.lrWidth = dimensions[4] * scale;
+        this.lrHeight = dimensions[5] * scale;
+        this.llWidth = dimensions[6] * scale;
+        this.llHeight = dimensions[7] * scale;
+    }
+
+    @Override
+    public double getX() {
+        return x;
+    }
+
+    @Override
+    public double getY() {
+        return y;
+    }
+
+    @Override
+    public double getWidth() {
+        return width;
+    }
+
+    @Override
+    public double getHeight() {
+        return height;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return (width <= 0d) || (height <= 0d);
+    }
+
+    @Override
+    public void setFrame(double x, double y, double w, double h) {
+        this.x = x;
+        this.y = y;
+        this.width = w;
+        this.height = h;
+    }
+
+    @Override
+    public Rectangle2D getBounds2D() {
+        return new Rectangle2D.Double(x, y, width, height);
+    }
+
+    @Override
+    public boolean contains(double x, double y) {
+        if (isEmpty()) {
+            return false;
+        }
+
+        double x0 = getX();
+        double y0 = getY();
+        double x1 = x0 + getWidth();
+        double y1 = y0 + getHeight();
+        // Check for trivial rejection - point is outside bounding rectangle
+        if (x < x0 || y < y0 || x >= x1 || y >= y1) {
+            return false;
+        }
+
+        double insideTopX0 = x0 + ulWidth / 2d;
+        double insideLeftY0 = y0 + ulHeight / 2d;
+        if (x < insideTopX0 && y < insideLeftY0) {
+            // In the upper-left corner
+            return isInsideCorner(x - insideTopX0, y - insideLeftY0, ulWidth / 2d, ulHeight / 2d);
+        }
+
+        double insideTopX1 = x1 - urWidth / 2d;
+        double insideRightY0 = y0 + urHeight / 2d;
+        if (x > insideTopX1 && y < insideRightY0) {
+            // In the upper-right corner
+            return isInsideCorner(x - insideTopX1, y - insideRightY0, urWidth / 2d, urHeight / 2d);
+        }
+
+        double insideBottomX1 = x1 - lrWidth / 2d;
+        double insideRightY1 = y1 - lrHeight / 2d;
+        if (x > insideBottomX1 && y > insideRightY1) {
+            // In the lower-right corner
+            return isInsideCorner(x - insideBottomX1, y - insideRightY1, lrWidth / 2d,
+                    lrHeight / 2d);
+        }
+
+        double insideBottomX0 = x0 + llWidth / 2d;
+        double insideLeftY1 = y1 - llHeight / 2d;
+        if (x < insideBottomX0 && y > insideLeftY1) {
+            // In the lower-left corner
+            return isInsideCorner(x - insideBottomX0, y - insideLeftY1, llWidth / 2d,
+                    llHeight / 2d);
+        }
+
+        // In the central part of the rectangle
+        return true;
+    }
+
+    private boolean isInsideCorner(double x, double y, double width, double height) {
+        double squareDist = height * height * x * x + width * width * y * y;
+        return squareDist <= width * width * height * height;
+    }
+
+    private Zone classify(double coord, double side1, double arcSize1, double side2,
+            double arcSize2) {
+        if (coord < side1) {
+            return Zone.CLOSE_OUTSIDE;
+        } else if (coord < side1 + arcSize1) {
+            return Zone.CLOSE_INSIDE;
+        } else if (coord < side2 - arcSize2) {
+            return Zone.MIDDLE;
+        } else if (coord < side2) {
+            return Zone.FAR_INSIDE;
+        } else {
+            return Zone.FAR_OUTSIDE;
+        }
+    }
+
+    public boolean intersects(double x, double y, double w, double h) {
+        if (isEmpty() || w <= 0 || h <= 0) {
+            return false;
+        }
+        double x0 = getX();
+        double y0 = getY();
+        double x1 = x0 + getWidth();
+        double y1 = y0 + getHeight();
+        // Check for trivial rejection - bounding rectangles do not intersect
+        if (x + w <= x0 || x >= x1 || y + h <= y0 || y >= y1) {
+            return false;
+        }
+
+        double maxLeftCornerWidth = Math.max(ulWidth, llWidth) / 2d;
+        double maxRightCornerWidth = Math.max(urWidth, lrWidth) / 2d;
+        double maxUpperCornerHeight = Math.max(ulHeight, urHeight) / 2d;
+        double maxLowerCornerHeight = Math.max(llHeight, lrHeight) / 2d;
+        Zone x0class = classify(x, x0, maxLeftCornerWidth, x1, maxRightCornerWidth);
+        Zone x1class = classify(x + w, x0, maxLeftCornerWidth, x1, maxRightCornerWidth);
+        Zone y0class = classify(y, y0, maxUpperCornerHeight, y1, maxLowerCornerHeight);
+        Zone y1class = classify(y + h, y0, maxUpperCornerHeight, y1, maxLowerCornerHeight);
+
+        // Trivially accept if any point is inside inner rectangle
+        if (x0class == Zone.MIDDLE || x1class == Zone.MIDDLE || y0class == Zone.MIDDLE || y1class == Zone.MIDDLE) {
+            return true;
+        }
+        // Trivially accept if either edge spans inner rectangle
+        if ((close.contains(x0class) && far.contains(x1class)) || (close.contains(y0class) &&
+                far.contains(y1class))) {
+            return true;
+        }
+
+        // Since neither edge spans the center, then one of the corners
+        // must be in one of the rounded edges.  We detect this case if
+        // a [xy]0class is 3 or a [xy]1class is 1.  One of those two cases
+        // must be true for each direction.
+        // We now find a "nearest point" to test for being inside a rounded
+        // corner.
+        if (x1class == Zone.CLOSE_INSIDE && y1class == Zone.CLOSE_INSIDE) {
+            // Potentially in upper-left corner
+            x = x + w - x0 - ulWidth / 2d;
+            y = y + h - y0 - ulHeight / 2d;
+            return x > 0 || y > 0 || isInsideCorner(x, y, ulWidth / 2d, ulHeight / 2d);
+        }
+        if (x1class == Zone.CLOSE_INSIDE) {
+            // Potentially in lower-left corner
+            x = x + w - x0 - llWidth / 2d;
+            y = y - y1 + llHeight / 2d;
+            return x > 0 || y < 0 || isInsideCorner(x, y, llWidth / 2d, llHeight / 2d);
+        }
+        if (y1class == Zone.CLOSE_INSIDE) {
+            //Potentially in the upper-right corner
+            x = x - x1 + urWidth / 2d;
+            y = y + h - y0 - urHeight / 2d;
+            return x < 0 || y > 0 || isInsideCorner(x, y, urWidth / 2d, urHeight / 2d);
+        }
+        // Potentially in the lower-right corner
+        x = x - x1 + lrWidth / 2d;
+        y = y - y1 + lrHeight / 2d;
+        return x < 0 || y < 0 || isInsideCorner(x, y, lrWidth / 2d, lrHeight / 2d);
+    }
+
+    @Override
+    public boolean contains(double x, double y, double w, double h) {
+        if (isEmpty() || w <= 0 || h <= 0) {
+            return false;
+        }
+        return (contains(x, y) &&
+                contains(x + w, y) &&
+                contains(x, y + h) &&
+                contains(x + w, y + h));
+    }
+
+    @Override
+    public PathIterator getPathIterator(final AffineTransform at) {
+        return new PathIterator() {
+            int index;
+
+            // ArcIterator.btan(Math.PI/2)
+            public static final double CtrlVal = 0.5522847498307933;
+            private final double ncv = 1.0 - CtrlVal;
+
+            // Coordinates of control points for Bezier curves approximating the straight lines
+            // and corners of the rounded rectangle.
+            private final double[][] ctrlpts = {
+                    {0.0, 0.0, 0.0, ulHeight},
+                    {0.0, 0.0, 1.0, -llHeight},
+                    {0.0, 0.0, 1.0, -llHeight * ncv, 0.0, ncv * llWidth, 1.0, 0.0, 0.0, llWidth,
+                            1.0, 0.0},
+                    {1.0, -lrWidth, 1.0, 0.0},
+                    {1.0, -lrWidth * ncv, 1.0, 0.0, 1.0, 0.0, 1.0, -lrHeight * ncv, 1.0, 0.0, 1.0,
+                            -lrHeight},
+                    {1.0, 0.0, 0.0, urHeight},
+                    {1.0, 0.0, 0.0, ncv * urHeight, 1.0, -urWidth * ncv, 0.0, 0.0, 1.0, -urWidth,
+                            0.0, 0.0},
+                    {0.0, ulWidth, 0.0, 0.0},
+                    {0.0, ncv * ulWidth, 0.0, 0.0, 0.0, 0.0, 0.0, ncv * ulHeight, 0.0, 0.0, 0.0,
+                            ulHeight},
+                    {}
+            };
+            private final int[] types = {
+                    SEG_MOVETO,
+                    SEG_LINETO, SEG_CUBICTO,
+                    SEG_LINETO, SEG_CUBICTO,
+                    SEG_LINETO, SEG_CUBICTO,
+                    SEG_LINETO, SEG_CUBICTO,
+                    SEG_CLOSE,
+            };
+
+            @Override
+            public int getWindingRule() {
+                return WIND_NON_ZERO;
+            }
+
+            @Override
+            public boolean isDone() {
+                return index >= ctrlpts.length;
+            }
+
+            @Override
+            public void next() {
+                index++;
+            }
+
+            @Override
+            public int currentSegment(float[] coords) {
+                if (isDone()) {
+                    throw new NoSuchElementException("roundrect iterator out of bounds");
+                }
+                int nc = 0;
+                double ctrls[] = ctrlpts[index];
+                for (int i = 0; i < ctrls.length; i += 4) {
+                    coords[nc++] = (float) (x + ctrls[i] * width + ctrls[i + 1] / 2d);
+                    coords[nc++] = (float) (y + ctrls[i + 2] * height + ctrls[i + 3] / 2d);
+                }
+                if (at != null) {
+                    at.transform(coords, 0, coords, 0, nc / 2);
+                }
+                return types[index];
+            }
+
+            @Override
+            public int currentSegment(double[] coords) {
+                if (isDone()) {
+                    throw new NoSuchElementException("roundrect iterator out of bounds");
+                }
+                int nc = 0;
+                double ctrls[] = ctrlpts[index];
+                for (int i = 0; i < ctrls.length; i += 4) {
+                    coords[nc++] = x + ctrls[i] * width + ctrls[i + 1] / 2d;
+                    coords[nc++] = y + ctrls[i + 2] * height + ctrls[i + 3] / 2d;
+                }
+                if (at != null) {
+                    at.transform(coords, 0, coords, 0, nc / 2);
+                }
+                return types[index];
+            }
+        };
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java b/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java
index 4901f72..94f3f54 100644
--- a/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java
+++ b/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java
@@ -45,4 +45,10 @@
     public static void dispatchOnPreDraw(View view) {
         view.mAttachInfo.mTreeObserver.dispatchOnPreDraw();
     }
+
+    public static void detachFromWindow(View view) {
+        if (view != null) {
+            view.dispatchDetachedFromWindow();
+        }
+    }
 }
diff --git a/tools/layoutlib/bridge/src/android/view/ViewRootImpl_RunQueue_Delegate.java b/tools/layoutlib/bridge/src/android/view/ViewRootImpl_RunQueue_Delegate.java
new file mode 100644
index 0000000..51b42a6
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/ViewRootImpl_RunQueue_Delegate.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 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 android.view;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of
+ * {@link ViewRootImpl.RunQueue}
+ *
+ * Through the layoutlib_create tool, the original  methods of ViewRootImpl.RunQueue have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ */
+public class ViewRootImpl_RunQueue_Delegate {
+
+    @LayoutlibDelegate
+    /*package*/ static void postDelayed(ViewRootImpl.RunQueue thisQueue, Runnable action, long
+            delayMillis) {
+        // The actual RunQueue is never run and therefore never cleared. This method avoids
+        // runnables to be added to the RunQueue so they do not leak resources.
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/widget/SimpleMonthView_Delegate.java b/tools/layoutlib/bridge/src/android/widget/SimpleMonthView_Delegate.java
deleted file mode 100644
index 8e41e51..0000000
--- a/tools/layoutlib/bridge/src/android/widget/SimpleMonthView_Delegate.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2015 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 android.widget;
-
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.icu.text.SimpleDateFormat;
-import android.text.format.DateFormat;
-
-import java.util.Calendar;
-import java.util.Locale;
-
-/**
- * Delegate that provides implementation for some methods in {@link SimpleMonthView}.
- * <p/>
- * Through the layoutlib_create tool, selected methods of SimpleMonthView have been replaced by
- * calls to methods of the same name in this delegate class.
- * <p/>
- * The main purpose of this class is to use {@link android.icu.text.SimpleDateFormat} instead of
- * {@link java.text.SimpleDateFormat}.
- */
-public class SimpleMonthView_Delegate {
-
-    private static final String DEFAULT_TITLE_FORMAT = "MMMMy";
-    private static final String DAY_OF_WEEK_FORMAT = "EEEEE";
-
-    // Maintain a cache of the last view used, so that the formatters can be reused.
-    @Nullable private static SimpleMonthView sLastView;
-    @Nullable private static SimpleMonthView_Delegate sLastDelegate;
-
-    private SimpleDateFormat mTitleFormatter;
-    private SimpleDateFormat mDayOfWeekFormatter;
-
-    private Locale locale;
-
-    @LayoutlibDelegate
-    /*package*/ static CharSequence getTitle(SimpleMonthView view) {
-        if (view.mTitle == null) {
-            SimpleMonthView_Delegate delegate = getDelegate(view);
-            if (delegate.mTitleFormatter == null) {
-                delegate.mTitleFormatter = new SimpleDateFormat(DateFormat.getBestDateTimePattern(
-                        getLocale(delegate, view), DEFAULT_TITLE_FORMAT));
-            }
-            view.mTitle = delegate.mTitleFormatter.format(view.mCalendar.getTime());
-        }
-        return view.mTitle;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static String getDayOfWeekLabel(SimpleMonthView view, int dayOfWeek) {
-        view.mDayOfWeekLabelCalendar.set(Calendar.DAY_OF_WEEK, dayOfWeek);
-        SimpleMonthView_Delegate delegate = getDelegate(view);
-        if (delegate.mDayOfWeekFormatter == null) {
-            delegate.mDayOfWeekFormatter =
-                    new SimpleDateFormat(DAY_OF_WEEK_FORMAT, getLocale(delegate, view));
-        }
-        return delegate.mDayOfWeekFormatter.format(view.mDayOfWeekLabelCalendar.getTime());
-    }
-
-    private static Locale getLocale(SimpleMonthView_Delegate delegate, SimpleMonthView view) {
-        if (delegate.locale == null) {
-            delegate.locale = view.getContext().getResources().getConfiguration().locale;
-        }
-        return delegate.locale;
-    }
-
-    @NonNull
-    private static SimpleMonthView_Delegate getDelegate(SimpleMonthView view) {
-        if (view == sLastView) {
-            assert sLastDelegate != null;
-            return sLastDelegate;
-        } else {
-            sLastView = view;
-            sLastDelegate = new SimpleMonthView_Delegate();
-            return sLastDelegate;
-        }
-    }
-
-    public static void clearCache() {
-        sLastView = null;
-        sLastDelegate = null;
-    }
-}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
index 2ac212c..fea633e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -208,6 +208,9 @@
 
     @Override
     public void dispose() {
+        if (mSession != null) {
+            mSession.dispose();
+        }
     }
 
     /*package*/ BridgeRenderSession(RenderSessionImpl scene, Result lastResult) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index a32128f..2fd1f18 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -52,11 +52,11 @@
 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;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
+import android.content.res.Resources_Delegate;
 import android.database.DatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
@@ -167,8 +167,8 @@
         RTL_ATTRS.put("?android:attr/paddingRight", "paddingEnd");
         RTL_ATTRS.put("?android:attr/layout_marginLeft", "layout_marginStart");
         RTL_ATTRS.put("?android:attr/layout_marginRight", "layout_marginEnd");
-        RTL_ATTRS.put("?android:attr/layout_toLeft", "layout_toStartOf");
-        RTL_ATTRS.put("?android:attr/layout_toRight", "layout_toEndOf");
+        RTL_ATTRS.put("?android:attr/layout_toLeftOf", "layout_toStartOf");
+        RTL_ATTRS.put("?android:attr/layout_toRightOf", "layout_toEndOf");
         RTL_ATTRS.put("?android:attr/layout_alignParentLeft", "layout_alignParentStart");
         RTL_ATTRS.put("?android:attr/layout_alignParentRight", "layout_alignParentEnd");
         RTL_ATTRS.put("?android:attr/drawableLeft", "drawableStart");
@@ -223,7 +223,7 @@
     public void initResources() {
         AssetManager assetManager = AssetManager.getSystem();
 
-        mSystemResources = BridgeResources.initSystem(
+        mSystemResources = Resources_Delegate.initSystem(
                 this,
                 assetManager,
                 mMetrics,
@@ -236,7 +236,7 @@
      * Disposes the {@link Resources} singleton.
      */
     public void disposeResources() {
-        BridgeResources.disposeSystem();
+        Resources_Delegate.disposeSystem();
     }
 
     public void setBridgeInflater(BridgeInflater inflater) {
@@ -705,8 +705,8 @@
 
         List<Pair<String, Boolean>> attributeList = searchAttrs(attrs);
 
-        BridgeTypedArray ta = ((BridgeResources) mSystemResources).newTypeArray(attrs.length,
-                isPlatformFile);
+        BridgeTypedArray ta =
+                Resources_Delegate.newTypeArray(mSystemResources, attrs.length, isPlatformFile);
 
         // look for a custom style.
         String customStyle = null;
@@ -940,7 +940,7 @@
 
         List<Pair<String, Boolean>> attributes = searchAttrs(attrs);
 
-        BridgeTypedArray ta = ((BridgeResources) mSystemResources).newTypeArray(attrs.length,
+        BridgeTypedArray ta = Resources_Delegate.newTypeArray(mSystemResources, attrs.length,
                 false);
 
         // for each attribute, get its name so that we can search it in the style
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index 6d9e872..4e4fcd0 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -36,7 +36,6 @@
 import android.view.ViewConfiguration_Accessor;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodManager_Accessor;
-import android.widget.SimpleMonthView_Delegate;
 
 import java.util.Locale;
 import java.util.concurrent.TimeUnit;
@@ -278,7 +277,6 @@
             mContext.getRenderResources().setLogger(null);
         }
         ParserFactory.setParserFactory(null);
-        SimpleMonthView_Delegate.clearCache();
     }
 
     public static BridgeContext getCurrentContext() {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index ec50cfe..99af226 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -1396,4 +1396,21 @@
     public RenderSession getSession() {
         return mScene;
     }
+
+    public void dispose() {
+        AttachInfo_Accessor.detachFromWindow(mViewRoot);
+        if (mCanvas != null) {
+            mCanvas.release();
+            mCanvas = null;
+        }
+        if (mViewInfoList != null) {
+            mViewInfoList.clear();
+        }
+        if (mSystemViewInfoList != null) {
+            mSystemViewInfoList.clear();
+        }
+        mImage = null;
+        mViewRoot = null;
+        mContentRoot = null;
+    }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
new file mode 100644
index 0000000..96b795a
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 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 com.android.layoutlib.bridge.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+
+/**
+ * Simpler wrapper around FileInputStream. This is used when the input stream represent
+ * not a normal bitmap but a nine patch.
+ * This is useful when the InputStream is created in a method but used in another that needs
+ * to know whether this is 9-patch or not, such as BitmapFactory.
+ */
+public class NinePatchInputStream extends FileInputStream {
+    private boolean mFakeMarkSupport = true;
+    public NinePatchInputStream(File file) throws FileNotFoundException {
+        super(file);
+    }
+
+    @Override
+    public boolean markSupported() {
+        if (mFakeMarkSupport) {
+            // this is needed so that BitmapFactory doesn't wrap this in a BufferedInputStream.
+            return true;
+        }
+
+        return super.markSupported();
+    }
+
+    public void disableFakeMarkSupport() {
+        // disable fake mark support so that in case codec actually try to use them
+        // we don't lie to them.
+        mFakeMarkSupport = false;
+    }
+}
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index fe16a3e..6b23da7 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -291,7 +291,6 @@
     @Test
     public void testActivity() throws ClassNotFoundException {
         renderAndVerify("activity.xml", "activity.png");
-
     }
 
     /** Test allwidgets.xml */
@@ -431,6 +430,8 @@
             ImageUtils.requireSimilar(goldenImagePath, session.getImage());
         } catch (IOException e) {
             getLogger().error(e, e.getMessage());
+        } finally {
+            session.dispose();
         }
     }
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
index 8f0ad01..414b255 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -367,6 +367,10 @@
 
         ClassVisitor cv = cw;
 
+        // FIXME Generify
+        if ("android/content/res/Resources".equals(className)) {
+            cv = new FieldInjectorAdapter(cv);
+        }
         if (mReplaceMethodCallsClasses.contains(className)) {
             cv = new ReplaceMethodCallsAdapter(cv, className);
         }
@@ -445,4 +449,5 @@
         }
         return buffer.toByteArray();
     }
+
 }
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index cb84a1b..e17f1f8 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -158,6 +158,31 @@
      */
     public final static String[] DELEGATE_METHODS = new String[] {
         "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
+        "android.content.res.Resources#getAnimation",
+        "android.content.res.Resources#getBoolean",
+        "android.content.res.Resources#getColor",
+        "android.content.res.Resources#getColorStateList",
+        "android.content.res.Resources#getDimension",
+        "android.content.res.Resources#getDimensionPixelOffset",
+        "android.content.res.Resources#getDimensionPixelSize",
+        "android.content.res.Resources#getDrawable",
+        "android.content.res.Resources#getIntArray",
+        "android.content.res.Resources#getInteger",
+        "android.content.res.Resources#getLayout",
+        "android.content.res.Resources#getResourceEntryName",
+        "android.content.res.Resources#getResourceName",
+        "android.content.res.Resources#getResourceTypeName",
+        "android.content.res.Resources#getString",
+        "android.content.res.Resources#getStringArray",
+        "android.content.res.Resources#getText",
+        "android.content.res.Resources#getTextArray",
+        "android.content.res.Resources#getValue",
+        "android.content.res.Resources#getXml",
+        "android.content.res.Resources#loadXmlResourceParser",
+        "android.content.res.Resources#obtainAttributes",
+        "android.content.res.Resources#obtainTypedArray",
+        "android.content.res.Resources#openRawResource",
+        "android.content.res.Resources#openRawResourceFd",
         "android.content.res.Resources$Theme#obtainStyledAttributes",
         "android.content.res.Resources$Theme#resolveAttribute",
         "android.content.res.Resources$Theme#resolveAttributes",
@@ -185,6 +210,7 @@
         "android.view.View#getWindowToken",
         "android.view.View#isInEditMode",
         "android.view.ViewRootImpl#isInTouchMode",
+        "android.view.ViewRootImpl$RunQueue#postDelayed",
         "android.view.WindowManagerGlobal#getWindowManagerService",
         "android.view.inputmethod.InputMethodManager#getInstance",
         "android.view.MenuInflater#registerMenu",
@@ -216,8 +242,6 @@
         "android.view.RenderNode#nGetScaleY",
         "android.view.RenderNode#nIsPivotExplicitlySet",
         "android.view.ViewGroup#drawChild",
-        "android.widget.SimpleMonthView#getTitle",
-        "android.widget.SimpleMonthView#getDayOfWeekLabel",
         "android.widget.TimePickerClockDelegate#getAmOrPmKeyCode",
         "com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
         "com.android.internal.util.XmlUtils#convertValueToInt",
@@ -315,6 +339,10 @@
             "java.lang.IntegralToString",                      "com.android.tools.layoutlib.java.IntegralToString",
             "java.lang.UnsafeByteSequence",                    "com.android.tools.layoutlib.java.UnsafeByteSequence",
             "java.nio.charset.StandardCharsets",               "com.android.tools.layoutlib.java.Charsets",
+            // Use android.icu.text versions of DateFormat and SimpleDateFormat since the
+            // original ones do not match the Android implementation
+            "java.text.DateFormat",                            "android.icu.text.DateFormat",
+            "java.text.SimpleDateFormat",                      "android.icu.text.SimpleDateFormat",
         };
 
     private final static String[] EXCLUDED_CLASSES =
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/FieldInjectorAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/FieldInjectorAdapter.java
new file mode 100644
index 0000000..4608a84
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/FieldInjectorAdapter.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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 com.android.tools.layoutlib.create;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Injects fields in a class.
+ * <p>
+ * TODO: Generify
+ */
+public class FieldInjectorAdapter extends ClassVisitor {
+    public FieldInjectorAdapter(ClassVisitor cv) {
+        super(Opcodes.ASM4, cv);
+    }
+
+    @Override
+    public void visitEnd() {
+        super.visitField(Opcodes.ACC_PUBLIC, "mLayoutlibCallback",
+                "Lcom/android/ide/common/rendering/api/LayoutlibCallback;", null, null);
+        super.visitField(Opcodes.ACC_PUBLIC, "mContext",
+                "Lcom/android/layoutlib/bridge/android/BridgeContext;", null, null);
+        super.visitEnd();
+    }
+}