LayoutLib: Original import of Honeycomb's layoutlib. do not merge.

frameworks/base.git @ f0a53435f14d23d9555fc46014352ee6a7baa647

Change-Id: Ibc215751693dc7650683b61bb458f7c8beaf8060
diff --git a/tools/layoutlib/README b/tools/layoutlib/README
new file mode 100644
index 0000000..0fea9bd
--- /dev/null
+++ b/tools/layoutlib/README
@@ -0,0 +1,4 @@
+Layoutlib is a custom version of the android View framework designed to run inside Eclipse.
+The goal of the library is to provide layout rendering in Eclipse that are very very close to their rendering on devices.
+
+None of the com.android.* or android.* classes in layoutlib run on devices.
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/.classpath b/tools/layoutlib/bridge/.classpath
index 175a98b..5cfda29 100644
--- a/tools/layoutlib/bridge/.classpath
+++ b/tools/layoutlib/bridge/.classpath
@@ -4,9 +4,10 @@
 	<classpathentry kind="src" path="tests"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/layoutlib_api"/>
-	<classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_SRC/dalvik/libcore/xml/src/main/java"/>
-	<classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/layoutlib.jar" sourcepath="/ANDROID_SRC/frameworks/base/core/java"/>
-	<classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/ninepatch.jar" sourcepath="/ANDROID_SRC/development/tools/ninepatch/src"/>
+	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/layoutlib_api/layoutlib_api-prebuilt.jar"/>
+	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/>
+	<classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/>
+	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/ninepatch/ninepatch-prebuilt.jar"/>
+	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/tools-common/tools-common-prebuilt.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/tools/layoutlib/bridge/Android.mk b/tools/layoutlib/bridge/Android.mk
index b2010d5..7e70f33 100644
--- a/tools/layoutlib/bridge/Android.mk
+++ b/tools/layoutlib/bridge/Android.mk
@@ -17,13 +17,17 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_JAVA_RESOURCE_DIRS := resources
+
 
 LOCAL_JAVA_LIBRARIES := \
 	kxml2-2.3.0 \
-	layoutlib_api \
-	ninepatch
+	layoutlib_api-prebuilt \
+	tools-common-prebuilt
 
-LOCAL_STATIC_JAVA_LIBRARIES := temp_layoutlib
+LOCAL_STATIC_JAVA_LIBRARIES := \
+	temp_layoutlib \
+	ninepatch-prebuilt
 
 LOCAL_MODULE := layoutlib
 
diff --git a/tools/layoutlib/bridge/resources/bars/action_bar.xml b/tools/layoutlib/bridge/resources/bars/action_bar.xml
new file mode 100644
index 0000000..51983f2
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/action_bar.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+	<ImageView
+			android:layout_height="wrap_content"
+			android:layout_width="wrap_content"/>
+	<TextView
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"/>
+</merge>
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_wifi_signal_4_fully.png
new file mode 100644
index 0000000..bd44b52
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_wifi_signal_4_fully.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/status_bar_background.9.png b/tools/layoutlib/bridge/resources/bars/hdpi/status_bar_background.9.png
new file mode 100644
index 0000000..a4be298
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/hdpi/status_bar_background.9.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png
new file mode 100644
index 0000000..4bcd2be
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png
new file mode 100644
index 0000000..cfeba3e
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png
new file mode 100644
index 0000000..1d97e05
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_wifi_signal_4_fully.png
new file mode 100644
index 0000000..c629387
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_wifi_signal_4_fully.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/status_bar_background.9.png b/tools/layoutlib/bridge/resources/bars/mdpi/status_bar_background.9.png
new file mode 100644
index 0000000..eb7c1a4
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/mdpi/status_bar_background.9.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml b/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml
new file mode 100644
index 0000000..d3c492e
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+	<TextView
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_weight="1"/>
+	<ImageView
+			android:layout_height="wrap_content"
+			android:layout_width="wrap_content"/>
+	<ImageView
+			android:layout_height="wrap_content"
+			android:layout_width="wrap_content"
+			android:layout_marginLeft="3dip"
+			android:layout_marginRight="5dip"/>
+</merge>
diff --git a/tools/layoutlib/bridge/resources/bars/tablet_system_bar.xml b/tools/layoutlib/bridge/resources/bars/tablet_system_bar.xml
new file mode 100644
index 0000000..c5acddb
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/tablet_system_bar.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+	<ImageView
+			android:layout_height="wrap_content"
+			android:layout_width="wrap_content"/>
+	<ImageView
+			android:layout_height="wrap_content"
+			android:layout_width="wrap_content"/>
+	<ImageView
+			android:layout_height="wrap_content"
+			android:layout_width="wrap_content"/>
+	<TextView
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_weight="1"/>
+	<ImageView
+			android:layout_height="wrap_content"
+			android:layout_width="wrap_content"/>
+	<ImageView
+			android:layout_height="wrap_content"
+			android:layout_width="wrap_content"
+			android:layout_marginLeft="3dip"
+			android:layout_marginRight="15dip"/>
+</merge>
diff --git a/tools/layoutlib/bridge/resources/bars/title_bar.xml b/tools/layoutlib/bridge/resources/bars/title_bar.xml
new file mode 100644
index 0000000..76d78d9
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/title_bar.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+	<TextView
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"/>
+</merge>
diff --git a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
new file mode 100644
index 0000000..7b444aa
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 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.animation;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate implementing the native methods of android.animation.PropertyValuesHolder
+ *
+ * Through the layoutlib_create tool, the original native methods of PropertyValuesHolder have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
+ * around to map int to instance of the delegate.
+ *
+ * The main goal of this class' methods are to provide a native way to access setters and getters
+ * on some object. In this case we want to default to using Java reflection instead so the native
+ * methods do nothing.
+ *
+ */
+/*package*/ class PropertyValuesHolder_Delegate {
+
+    @LayoutlibDelegate
+    /*package*/ static int nGetIntMethod(Class<?> targetClass, String methodName) {
+        // return 0 to force PropertyValuesHolder to use Java reflection.
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nGetFloatMethod(Class<?> targetClass, String methodName) {
+        // return 0 to force PropertyValuesHolder to use Java reflection.
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nCallIntMethod(Object target, int methodID, int arg) {
+        // do nothing
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nCallFloatMethod(Object target, int methodID, float arg) {
+        // do nothing
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java b/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java
new file mode 100644
index 0000000..aabd3f1
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2010 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.app;
+
+import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.content.Context;
+import android.os.Bundle;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link Fragment}
+ *
+ * Through the layoutlib_create tool, the original  methods of Fragment have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * The methods being re-implemented are the ones responsible for instantiating Fragment objects.
+ * Because the classes of these objects are found in the project, these methods need access to
+ * {@link IProjectCallback} object. They are however static methods, so the callback is set
+ * before the inflation through {@link #setProjectCallback(IProjectCallback)}.
+ */
+public class Fragment_Delegate {
+
+    private static IProjectCallback sProjectCallback;
+
+    /**
+     * Sets the current {@link IProjectCallback} to be used to instantiate classes coming
+     * from the project being rendered.
+     */
+    public static void setProjectCallback(IProjectCallback projectCallback) {
+        sProjectCallback = projectCallback;
+    }
+
+    /**
+     * Like {@link #instantiate(Context, String, Bundle)} but with a null
+     * argument Bundle.
+     */
+    @LayoutlibDelegate
+    /*package*/ static Fragment instantiate(Context context, String fname) {
+        return instantiate(context, fname, null);
+    }
+
+    /**
+     * Create a new instance of a Fragment with the given class name.  This is
+     * the same as calling its empty constructor.
+     *
+     * @param context The calling context being used to instantiate the fragment.
+     * This is currently just used to get its ClassLoader.
+     * @param fname The class name of the fragment to instantiate.
+     * @param args Bundle of arguments to supply to the fragment, which it
+     * can retrieve with {@link #getArguments()}.  May be null.
+     * @return Returns a new fragment instance.
+     * @throws InstantiationException If there is a failure in instantiating
+     * the given fragment class.  This is a runtime exception; it is not
+     * normally expected to happen.
+     */
+    @LayoutlibDelegate
+    /*package*/ static Fragment instantiate(Context context, String fname, Bundle args) {
+        try {
+            if (sProjectCallback != null) {
+                Fragment f = (Fragment) sProjectCallback.loadView(fname,
+                        new Class[0], new Object[0]);
+
+                if (args != null) {
+                    args.setClassLoader(f.getClass().getClassLoader());
+                    f.mArguments = args;
+                }
+                return f;
+            }
+
+            return null;
+        } catch (ClassNotFoundException e) {
+            throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname
+                    + ": make sure class name exists, is public, and has an"
+                    + " empty constructor that is public", e);
+        } catch (java.lang.InstantiationException e) {
+            throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname
+                    + ": make sure class name exists, is public, and has an"
+                    + " empty constructor that is public", e);
+        } catch (IllegalAccessException e) {
+            throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname
+                    + ": make sure class name exists, is public, and has an"
+                    + " empty constructor that is public", e);
+        } catch (Exception e) {
+            throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname
+                    + ": make sure class name exists, is public, and has an"
+                    + " empty constructor that is public", e);
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
new file mode 100644
index 0000000..413894b
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2011 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.content.res;
+
+import com.android.layoutlib.bridge.impl.RenderSessionImpl;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.content.res.Resources.NotFoundException;
+import android.content.res.Resources.Theme;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link Theme}
+ *
+ * Through the layoutlib_create tool, the original  methods of Theme have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ */
+public class Resources_Theme_Delegate {
+
+    @LayoutlibDelegate
+    /*package*/ static TypedArray obtainStyledAttributes(
+            Resources thisResources, Theme thisTheme,
+            int[] attrs) {
+        return RenderSessionImpl.getCurrentContext().obtainStyledAttributes(attrs);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static TypedArray obtainStyledAttributes(
+            Resources thisResources, Theme thisTheme,
+            int resid, int[] attrs)
+            throws NotFoundException {
+        return RenderSessionImpl.getCurrentContext().obtainStyledAttributes(resid, attrs);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static TypedArray obtainStyledAttributes(
+            Resources thisResources, Theme thisTheme,
+            AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) {
+        return RenderSessionImpl.getCurrentContext().obtainStyledAttributes(
+                set, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean resolveAttribute(
+            Resources thisResources, Theme thisTheme,
+            int resid, TypedValue outValue,
+            boolean resolveRefs) {
+        return RenderSessionImpl.getCurrentContext().resolveThemeAttribute(
+                resid, outValue, resolveRefs);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/AvoidXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/AvoidXfermode_Delegate.java
new file mode 100644
index 0000000..a50a2bd
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/AvoidXfermode_Delegate.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.awt.Composite;
+
+/**
+ * Delegate implementing the native methods of android.graphics.AvoidXfermode
+ *
+ * Through the layoutlib_create tool, the original native methods of AvoidXfermode have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original AvoidXfermode class.
+ *
+ * Because this extends {@link Xfermode_Delegate}, there's no need to use a
+ * {@link DelegateManager}, as all the PathEffect classes will be added to the manager owned by
+ * {@link Xfermode_Delegate}.
+ *
+ */
+public class AvoidXfermode_Delegate extends Xfermode_Delegate {
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public Composite getComposite(int alpha) {
+        // FIXME
+        return null;
+    }
+
+    @Override
+    public boolean isSupported() {
+        return false;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        return "Avoid Xfermodes are not supported in Layout Preview mode.";
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreate(int opColor, int tolerance, int nativeMode) {
+        AvoidXfermode_Delegate newDelegate = new AvoidXfermode_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
deleted file mode 100644
index 35f022e..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright (C) 2008 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.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.imageio.ImageIO;
-
-public final class Bitmap extends _Original_Bitmap {
-
-    private BufferedImage mImage;
-
-    public Bitmap(File input) throws IOException {
-        super(1, true, null, -1);
-
-        mImage = ImageIO.read(input);
-    }
-
-    public Bitmap(InputStream is) throws IOException {
-        super(1, true, null, -1);
-
-        mImage = ImageIO.read(is);
-    }
-
-    Bitmap(BufferedImage image) {
-        super(1, true, null, -1);
-        mImage = image;
-    }
-
-    public BufferedImage getImage() {
-        return mImage;
-    }
-
-    // ----- overriden methods
-
-    public enum Config {
-        // these native values must match up with the enum in SkBitmap.h
-        ALPHA_8     (2),
-        RGB_565     (4),
-        ARGB_4444   (5),
-        ARGB_8888   (6);
-
-        Config(int ni) {
-            this.nativeInt = ni;
-        }
-        final int nativeInt;
-
-        /* package */ static Config nativeToConfig(int ni) {
-            return sConfigs[ni];
-        }
-
-        private static Config sConfigs[] = {
-            null, null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888
-        };
-    }
-
-    @Override
-    public int getWidth() {
-        return mImage.getWidth();
-    }
-
-    @Override
-    public int getHeight() {
-        return mImage.getHeight();
-    }
-
-    /**
-     * Returns an immutable bitmap from the source bitmap. The new bitmap may
-     * be the same object as source, or a copy may have been made.
-     */
-    public static Bitmap createBitmap(Bitmap src) {
-        return createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), null, false);
-    }
-
-    /**
-     * Returns an immutable bitmap from the specified subset of the source
-     * bitmap. The new bitmap may be the same object as source, or a copy may
-     * have been made.
-     *
-     * @param source   The bitmap we are subsetting
-     * @param x        The x coordinate of the first pixel in source
-     * @param y        The y coordinate of the first pixel in source
-     * @param width    The number of pixels in each row
-     * @param height   The number of rows
-     */
-    public static Bitmap createBitmap(Bitmap source, int x, int y,
-                                      int width, int height) {
-        return new Bitmap(source.mImage.getSubimage(x, y, width, height));
-    }
-
-    /**
-     * Returns an immutable bitmap from subset of the source bitmap,
-     * transformed by the optional matrix.
-     *
-     * @param source   The bitmap we are subsetting
-     * @param x        The x coordinate of the first pixel in source
-     * @param y        The y coordinate of the first pixel in source
-     * @param width    The number of pixels in each row
-     * @param height   The number of rows
-     * @param m        Option matrix to be applied to the pixels
-     * @param filter   true if the source should be filtered.
-     *                   Only applies if the matrix contains more than just
-     *                   translation.
-     * @return A bitmap that represents the specified subset of source
-     * @throws IllegalArgumentException if the x, y, width, height values are
-     *         outside of the dimensions of the source bitmap.
-     */
-    public static Bitmap createBitmap(Bitmap source, int x, int y, int width,
-                                      int height, Matrix m, boolean filter) {
-        checkXYSign(x, y);
-        checkWidthHeight(width, height);
-        if (x + width > source.getWidth()) {
-            throw new IllegalArgumentException(
-                    "x + width must be <= bitmap.width()");
-        }
-        if (y + height > source.getHeight()) {
-            throw new IllegalArgumentException(
-                    "y + height must be <= bitmap.height()");
-        }
-
-        // check if we can just return our argument unchanged
-        if (!source.isMutable() && x == 0 && y == 0
-                && width == source.getWidth() && height == source.getHeight()
-                && (m == null || m.isIdentity())) {
-            return source;
-        }
-
-        if (m == null || m.isIdentity()) {
-            return new Bitmap(source.mImage.getSubimage(x, y, width, height));
-        }
-
-        int neww = width;
-        int newh = height;
-        Paint paint;
-
-        Rect srcR = new Rect(x, y, x + width, y + height);
-        RectF dstR = new RectF(0, 0, width, height);
-
-        /*  the dst should have alpha if the src does, or if our matrix
-            doesn't preserve rectness
-        */
-        boolean hasAlpha = source.hasAlpha() || !m.rectStaysRect();
-        RectF deviceR = new RectF();
-        m.mapRect(deviceR, dstR);
-        neww = Math.round(deviceR.width());
-        newh = Math.round(deviceR.height());
-
-        Canvas canvas = new Canvas(neww, newh);
-
-        canvas.translate(-deviceR.left, -deviceR.top);
-        canvas.concat(m);
-        paint = new Paint();
-        paint.setFilterBitmap(filter);
-        if (!m.rectStaysRect()) {
-            paint.setAntiAlias(true);
-        }
-
-        canvas.drawBitmap(source, srcR, dstR, paint);
-
-        return new Bitmap(canvas.getImage());
-    }
-
-    /**
-     * Returns a mutable bitmap with the specified width and height.
-     *
-     * @param width    The width of the bitmap
-     * @param height   The height of the bitmap
-     * @param config   The bitmap config to create.
-     * @throws IllegalArgumentException if the width or height are <= 0
-     */
-    public static Bitmap createBitmap(int width, int height, Config config) {
-        return new Bitmap(new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB));
-    }
-
-    /**
-     * Returns a immutable bitmap with the specified width and height, with each
-     * pixel value set to the corresponding value in the colors array.
-     *
-     * @param colors   Array of {@link Color} used to initialize the pixels.
-     * @param offset   Number of values to skip before the first color in the
-     *                 array of colors.
-     * @param stride   Number of colors in the array between rows (must be >=
-     *                 width or <= -width).
-     * @param width    The width of the bitmap
-     * @param height   The height of the bitmap
-     * @param config   The bitmap config to create. If the config does not
-     *                 support per-pixel alpha (e.g. RGB_565), then the alpha
-     *                 bytes in the colors[] will be ignored (assumed to be FF)
-     * @throws IllegalArgumentException if the width or height are <= 0, or if
-     *         the color array's length is less than the number of pixels.
-     */
-    public static Bitmap createBitmap(int colors[], int offset, int stride,
-                                      int width, int height, Config config) {
-        checkWidthHeight(width, height);
-        if (Math.abs(stride) < width) {
-            throw new IllegalArgumentException("abs(stride) must be >= width");
-        }
-        int lastScanline = offset + (height - 1) * stride;
-        int length = colors.length;
-        if (offset < 0 || (offset + width > length)
-            || lastScanline < 0
-            || (lastScanline + width > length)) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-
-        // TODO: create an immutable bitmap...
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * Returns a immutable bitmap with the specified width and height, with each
-     * pixel value set to the corresponding value in the colors array.
-     *
-     * @param colors   Array of {@link Color} used to initialize the pixels.
-     *                 This array must be at least as large as width * height.
-     * @param width    The width of the bitmap
-     * @param height   The height of the bitmap
-     * @param config   The bitmap config to create. If the config does not
-     *                 support per-pixel alpha (e.g. RGB_565), then the alpha
-     *                 bytes in the colors[] will be ignored (assumed to be FF)
-     * @throws IllegalArgumentException if the width or height are <= 0, or if
-     *         the color array's length is less than the number of pixels.
-     */
-    public static Bitmap createBitmap(int colors[], int width, int height,
-                                      Config config) {
-        return createBitmap(colors, 0, width, width, height, config);
-    }
-
-    public static Bitmap createScaledBitmap(Bitmap src, int dstWidth,
-            int dstHeight, boolean filter) {
-        Matrix m;
-        synchronized (Bitmap.class) {
-            // small pool of just 1 matrix
-            m = sScaleMatrix;
-            sScaleMatrix = null;
-        }
-
-        if (m == null) {
-            m = new Matrix();
-        }
-
-        final int width = src.getWidth();
-        final int height = src.getHeight();
-        final float sx = dstWidth  / (float)width;
-        final float sy = dstHeight / (float)height;
-        m.setScale(sx, sy);
-        Bitmap b = Bitmap.createBitmap(src, 0, 0, width, height, m, filter);
-
-        synchronized (Bitmap.class) {
-            // do we need to check for null? why not just assign everytime?
-            if (sScaleMatrix == null) {
-                sScaleMatrix = m;
-            }
-        }
-
-        return b;
-    }
-
-
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
deleted file mode 100644
index e978fe8..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
+++ /dev/null
@@ -1,566 +0,0 @@
-/*
- * Copyright (C) 2010 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 android.content.res.AssetManager;
-import android.content.res.Resources;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-
-import java.io.BufferedInputStream;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Creates Bitmap objects from various sources, including files, streams,
- * and byte-arrays.
- */
-public class BitmapFactory {
-    public static class Options {
-        /**
-         * Create a default Options object, which if left unchanged will give
-         * the same result from the decoder as if null were passed.
-         */
-        public Options() {
-            inDither = true;
-            inScaled = true;
-        }
-
-        /**
-         * If set to true, the decoder will return null (no bitmap), but
-         * the out... fields will still be set, allowing the caller to query
-         * the bitmap without having to allocate the memory for its pixels.
-         */
-        public boolean inJustDecodeBounds;
-
-        /**
-         * If set to a value > 1, requests the decoder to subsample the original
-         * image, returning a smaller image to save memory. The sample size is
-         * the number of pixels in either dimension that correspond to a single
-         * pixel in the decoded bitmap. For example, inSampleSize == 4 returns
-         * an image that is 1/4 the width/height of the original, and 1/16 the
-         * number of pixels. Any value <= 1 is treated the same as 1. Note: the
-         * decoder will try to fulfill this request, but the resulting bitmap
-         * may have different dimensions that precisely what has been requested.
-         * Also, powers of 2 are often faster/easier for the decoder to honor.
-         */
-        public int inSampleSize;
-
-        /**
-         * If this is non-null, the decoder will try to decode into this
-         * internal configuration. If it is null, or the request cannot be met,
-         * the decoder will try to pick the best matching config based on the
-         * system's screen depth, and characteristics of the original image such
-         * as if it has per-pixel alpha (requiring a config that also does).
-         */
-        public Bitmap.Config inPreferredConfig;
-
-        /**
-         * If dither is true, the decoder will attempt to dither the decoded
-         * image.
-         */
-        public boolean inDither;
-
-        /**
-         * The pixel density to use for the bitmap.  This will always result
-         * in the returned bitmap having a density set for it (see
-         * {@link Bitmap#setDensity(int) Bitmap.setDensity(int)).  In addition,
-         * if {@link #inScaled} is set (which it is by default} and this
-         * density does not match {@link #inTargetDensity}, then the bitmap
-         * will be scaled to the target density before being returned.
-         *
-         * <p>If this is 0,
-         * {@link BitmapFactory#decodeResource(Resources, int)},
-         * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
-         * and {@link BitmapFactory#decodeResourceStream}
-         * will fill in the density associated with the resource.  The other
-         * functions will leave it as-is and no density will be applied.
-         *
-         * @see #inTargetDensity
-         * @see #inScreenDensity
-         * @see #inScaled
-         * @see Bitmap#setDensity(int)
-         * @see android.util.DisplayMetrics#densityDpi
-         */
-        public int inDensity;
-
-        /**
-         * The pixel density of the destination this bitmap will be drawn to.
-         * This is used in conjunction with {@link #inDensity} and
-         * {@link #inScaled} to determine if and how to scale the bitmap before
-         * returning it.
-         *
-         * <p>If this is 0,
-         * {@link BitmapFactory#decodeResource(Resources, int)},
-         * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
-         * and {@link BitmapFactory#decodeResourceStream}
-         * will fill in the density associated the Resources object's
-         * DisplayMetrics.  The other
-         * functions will leave it as-is and no scaling for density will be
-         * performed.
-         *
-         * @see #inDensity
-         * @see #inScreenDensity
-         * @see #inScaled
-         * @see android.util.DisplayMetrics#densityDpi
-         */
-        public int inTargetDensity;
-
-        /**
-         * The pixel density of the actual screen that is being used.  This is
-         * purely for applications running in density compatibility code, where
-         * {@link #inTargetDensity} is actually the density the application
-         * sees rather than the real screen density.
-         *
-         * <p>By setting this, you
-         * allow the loading code to avoid scaling a bitmap that is currently
-         * in the screen density up/down to the compatibility density.  Instead,
-         * if {@link #inDensity} is the same as {@link #inScreenDensity}, the
-         * bitmap will be left as-is.  Anything using the resulting bitmap
-         * must also used {@link Bitmap#getScaledWidth(int)
-         * Bitmap.getScaledWidth} and {@link Bitmap#getScaledHeight
-         * Bitmap.getScaledHeight} to account for any different between the
-         * bitmap's density and the target's density.
-         *
-         * <p>This is never set automatically for the caller by
-         * {@link BitmapFactory} itself.  It must be explicitly set, since the
-         * caller must deal with the resulting bitmap in a density-aware way.
-         *
-         * @see #inDensity
-         * @see #inTargetDensity
-         * @see #inScaled
-         * @see android.util.DisplayMetrics#densityDpi
-         */
-        public int inScreenDensity;
-
-        /**
-         * When this flag is set, if {@link #inDensity} and
-         * {@link #inTargetDensity} are not 0, the
-         * bitmap will be scaled to match {@link #inTargetDensity} when loaded,
-         * rather than relying on the graphics system scaling it each time it
-         * is drawn to a Canvas.
-         *
-         * <p>This flag is turned on by default and should be turned off if you need
-         * a non-scaled version of the bitmap.  Nine-patch bitmaps ignore this
-         * flag and are always scaled.
-         */
-        public boolean inScaled;
-
-        /**
-         * If this is set to true, then the resulting bitmap will allocate its
-         * pixels such that they can be purged if the system needs to reclaim
-         * memory. In that instance, when the pixels need to be accessed again
-         * (e.g. the bitmap is drawn, getPixels() is called), they will be
-         * automatically re-decoded.
-         *
-         * For the re-decode to happen, the bitmap must have access to the
-         * encoded data, either by sharing a reference to the input
-         * or by making a copy of it. This distinction is controlled by
-         * inInputShareable. If this is true, then the bitmap may keep a shallow
-         * reference to the input. If this is false, then the bitmap will
-         * explicitly make a copy of the input data, and keep that. Even if
-         * sharing is allowed, the implementation may still decide to make a
-         * deep copy of the input data.
-         */
-        public boolean inPurgeable;
-
-        /**
-         * This field works in conjuction with inPurgeable. If inPurgeable is
-         * false, then this field is ignored. If inPurgeable is true, then this
-         * field determines whether the bitmap can share a reference to the
-         * input data (inputstream, array, etc.) or if it must make a deep copy.
-         */
-        public boolean inInputShareable;
-
-        /**
-         * Normally bitmap allocations count against the dalvik heap, which
-         * means they help trigger GCs when a lot have been allocated. However,
-         * in rare cases, the caller may want to allocate the bitmap outside of
-         * that heap. To request that, set inNativeAlloc to true. In these
-         * rare instances, it is solely up to the caller to ensure that OOM is
-         * managed explicitly by calling bitmap.recycle() as soon as such a
-         * bitmap is no longer needed.
-         *
-         * @hide pending API council approval
-         */
-        public boolean inNativeAlloc;
-
-        /**
-         * The resulting width of the bitmap, set independent of the state of
-         * inJustDecodeBounds. However, if there is an error trying to decode,
-         * outWidth will be set to -1.
-         */
-        public int outWidth;
-
-        /**
-         * The resulting height of the bitmap, set independent of the state of
-         * inJustDecodeBounds. However, if there is an error trying to decode,
-         * outHeight will be set to -1.
-         */
-        public int outHeight;
-
-        /**
-         * If known, this string is set to the mimetype of the decoded image.
-         * If not know, or there is an error, it is set to null.
-         */
-        public String outMimeType;
-
-        /**
-         * Temp storage to use for decoding.  Suggest 16K or so.
-         */
-        public byte[] inTempStorage;
-
-        private native void requestCancel();
-
-        /**
-         * Flag to indicate that cancel has been called on this object.  This
-         * is useful if there's an intermediary that wants to first decode the
-         * bounds and then decode the image.  In that case the intermediary
-         * can check, inbetween the bounds decode and the image decode, to see
-         * if the operation is canceled.
-         */
-        public boolean mCancel;
-
-        /**
-         *  This can be called from another thread while this options object is
-         *  inside a decode... call. Calling this will notify the decoder that
-         *  it should cancel its operation. This is not guaranteed to cancel
-         *  the decode, but if it does, the decoder... operation will return
-         *  null, or if inJustDecodeBounds is true, will set outWidth/outHeight
-         *  to -1
-         */
-        public void requestCancelDecode() {
-            mCancel = true;
-            requestCancel();
-        }
-    }
-
-    /**
-     * Decode a file path into a bitmap. If the specified file name is null,
-     * or cannot be decoded into a bitmap, the function returns null.
-     *
-     * @param pathName complete path name for the file to be decoded.
-     * @param opts null-ok; Options that control downsampling and whether the
-     *             image should be completely decoded, or just is size returned.
-     * @return The decoded bitmap, or null if the image data could not be
-     *         decoded, or, if opts is non-null, if opts requested only the
-     *         size be returned (in opts.outWidth and opts.outHeight)
-     */
-    public static Bitmap decodeFile(String pathName, Options opts) {
-        Bitmap bm = null;
-        InputStream stream = null;
-        try {
-            stream = new FileInputStream(pathName);
-            bm = decodeStream(stream, null, opts);
-        } catch (Exception e) {
-            /*  do nothing.
-                If the exception happened on open, bm will be null.
-            */
-        } finally {
-            if (stream != null) {
-                try {
-                    stream.close();
-                } catch (IOException e) {
-                    // do nothing here
-                }
-            }
-        }
-        return bm;
-    }
-
-    /**
-     * Decode a file path into a bitmap. If the specified file name is null,
-     * or cannot be decoded into a bitmap, the function returns null.
-     *
-     * @param pathName complete path name for the file to be decoded.
-     * @return the resulting decoded bitmap, or null if it could not be decoded.
-     */
-    public static Bitmap decodeFile(String pathName) {
-        return decodeFile(pathName, null);
-    }
-
-    /**
-     * Decode a new Bitmap from an InputStream. This InputStream was obtained from
-     * resources, which we pass to be able to scale the bitmap accordingly.
-     */
-    public static Bitmap decodeResourceStream(Resources res, TypedValue value,
-            InputStream is, Rect pad, Options opts) {
-
-        if (opts == null) {
-            opts = new Options();
-        }
-
-        if (opts.inDensity == 0 && value != null) {
-            final int density = value.density;
-            if (density == TypedValue.DENSITY_DEFAULT) {
-                opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
-            } else if (density != TypedValue.DENSITY_NONE) {
-                opts.inDensity = density;
-            }
-        }
-
-        if (opts.inTargetDensity == 0 && res != null) {
-            opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
-        }
-
-        return decodeStream(is, pad, opts);
-    }
-
-    /**
-     * Synonym for opening the given resource and calling
-     * {@link #decodeResourceStream}.
-     *
-     * @param res   The resources object containing the image data
-     * @param id The resource id of the image data
-     * @param opts null-ok; Options that control downsampling and whether the
-     *             image should be completely decoded, or just is size returned.
-     * @return The decoded bitmap, or null if the image data could not be
-     *         decoded, or, if opts is non-null, if opts requested only the
-     *         size be returned (in opts.outWidth and opts.outHeight)
-     */
-    public static Bitmap decodeResource(Resources res, int id, Options opts) {
-        Bitmap bm = null;
-        InputStream is = null;
-
-        try {
-            final TypedValue value = new TypedValue();
-            is = res.openRawResource(id, value);
-
-            bm = decodeResourceStream(res, value, is, null, opts);
-        } catch (Exception e) {
-            /*  do nothing.
-                If the exception happened on open, bm will be null.
-                If it happened on close, bm is still valid.
-            */
-        } finally {
-            try {
-                if (is != null) is.close();
-            } catch (IOException e) {
-                // Ignore
-            }
-        }
-
-        return bm;
-    }
-
-    /**
-     * Synonym for {@link #decodeResource(Resources, int, android.graphics.BitmapFactory.Options)}
-     * will null Options.
-     *
-     * @param res The resources object containing the image data
-     * @param id The resource id of the image data
-     * @return The decoded bitmap, or null if the image could not be decode.
-     */
-    public static Bitmap decodeResource(Resources res, int id) {
-        return decodeResource(res, id, null);
-    }
-
-    /**
-     * Decode an immutable bitmap from the specified byte array.
-     *
-     * @param data byte array of compressed image data
-     * @param offset offset into imageData for where the decoder should begin
-     *               parsing.
-     * @param length the number of bytes, beginning at offset, to parse
-     * @param opts null-ok; Options that control downsampling and whether the
-     *             image should be completely decoded, or just is size returned.
-     * @return The decoded bitmap, or null if the image data could not be
-     *         decoded, or, if opts is non-null, if opts requested only the
-     *         size be returned (in opts.outWidth and opts.outHeight)
-     */
-    public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) {
-        if ((offset | length) < 0 || data.length < offset + length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-
-        // FIXME: implement as needed, but it's unlikely that this is needed in the context of the bridge.
-        return null;
-        //return nativeDecodeByteArray(data, offset, length, opts);
-    }
-
-    /**
-     * Decode an immutable bitmap from the specified byte array.
-     *
-     * @param data byte array of compressed image data
-     * @param offset offset into imageData for where the decoder should begin
-     *               parsing.
-     * @param length the number of bytes, beginning at offset, to parse
-     * @return The decoded bitmap, or null if the image could not be decode.
-     */
-    public static Bitmap decodeByteArray(byte[] data, int offset, int length) {
-        return decodeByteArray(data, offset, length, null);
-    }
-
-    /**
-     * Decode an input stream into a bitmap. If the input stream is null, or
-     * cannot be used to decode a bitmap, the function returns null.
-     * The stream's position will be where ever it was after the encoded data
-     * was read.
-     *
-     * @param is The input stream that holds the raw data to be decoded into a
-     *           bitmap.
-     * @param outPadding If not null, return the padding rect for the bitmap if
-     *                   it exists, otherwise set padding to [-1,-1,-1,-1]. If
-     *                   no bitmap is returned (null) then padding is
-     *                   unchanged.
-     * @param opts null-ok; Options that control downsampling and whether the
-     *             image should be completely decoded, or just is size returned.
-     * @return The decoded bitmap, or null if the image data could not be
-     *         decoded, or, if opts is non-null, if opts requested only the
-     *         size be returned (in opts.outWidth and opts.outHeight)
-     */
-    public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
-        // we don't throw in this case, thus allowing the caller to only check
-        // the cache, and not force the image to be decoded.
-        if (is == null) {
-            return null;
-        }
-
-        // we need mark/reset to work properly
-
-        if (!is.markSupported()) {
-            is = new BufferedInputStream(is, 16 * 1024);
-        }
-
-        // so we can call reset() if a given codec gives up after reading up to
-        // this many bytes. FIXME: need to find out from the codecs what this
-        // value should be.
-        is.mark(1024);
-
-        Bitmap  bm;
-
-        if (is instanceof AssetManager.AssetInputStream) {
-            // FIXME: log this.
-            return null;
-        } else {
-            // pass some temp storage down to the native code. 1024 is made up,
-            // but should be large enough to avoid too many small calls back
-            // into is.read(...) This number is not related to the value passed
-            // to mark(...) above.
-            try {
-                bm = new Bitmap(is);
-            } catch (IOException e) {
-                return null;
-            }
-        }
-
-        return finishDecode(bm, outPadding, opts);
-    }
-
-    private static Bitmap finishDecode(Bitmap bm, Rect outPadding, Options opts) {
-        if (bm == null || opts == null) {
-            return bm;
-        }
-
-        final int density = opts.inDensity;
-        if (density == 0) {
-            return bm;
-        }
-
-        bm.setDensity(density);
-        final int targetDensity = opts.inTargetDensity;
-        if (targetDensity == 0 || density == targetDensity
-                || density == opts.inScreenDensity) {
-            return bm;
-        }
-
-        byte[] np = bm.getNinePatchChunk();
-        final boolean isNinePatch = false; //np != null && NinePatch.isNinePatchChunk(np);
-        if (opts.inScaled || isNinePatch) {
-            float scale = targetDensity / (float)density;
-            // TODO: This is very inefficient and should be done in native by Skia
-            final Bitmap oldBitmap = bm;
-            bm = Bitmap.createScaledBitmap(oldBitmap, (int) (bm.getWidth() * scale + 0.5f),
-                    (int) (bm.getHeight() * scale + 0.5f), true);
-            oldBitmap.recycle();
-
-            if (isNinePatch) {
-                //np = nativeScaleNinePatch(np, scale, outPadding);
-                bm.setNinePatchChunk(np);
-            }
-            bm.setDensity(targetDensity);
-        }
-
-        return bm;
-    }
-
-    /**
-     * Decode an input stream into a bitmap. If the input stream is null, or
-     * cannot be used to decode a bitmap, the function returns null.
-     * The stream's position will be where ever it was after the encoded data
-     * was read.
-     *
-     * @param is The input stream that holds the raw data to be decoded into a
-     *           bitmap.
-     * @return The decoded bitmap, or null if the image data could not be
-     *         decoded, or, if opts is non-null, if opts requested only the
-     *         size be returned (in opts.outWidth and opts.outHeight)
-     */
-    public static Bitmap decodeStream(InputStream is) {
-        return decodeStream(is, null, null);
-    }
-
-    /**
-     * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
-     * return null. The position within the descriptor will not be changed when
-     * this returns, so the descriptor can be used again as-is.
-     *
-     * @param fd The file descriptor containing the bitmap data to decode
-     * @param outPadding If not null, return the padding rect for the bitmap if
-     *                   it exists, otherwise set padding to [-1,-1,-1,-1]. If
-     *                   no bitmap is returned (null) then padding is
-     *                   unchanged.
-     * @param opts null-ok; Options that control downsampling and whether the
-     *             image should be completely decoded, or just is size returned.
-     * @return the decoded bitmap, or null
-     */
-    public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
-        return null;
-
-        /* FIXME: implement as needed
-        try {
-            if (MemoryFile.isMemoryFile(fd)) {
-                int mappedlength = MemoryFile.getMappedSize(fd);
-                MemoryFile file = new MemoryFile(fd, mappedlength, "r");
-                InputStream is = file.getInputStream();
-                Bitmap bm = decodeStream(is, outPadding, opts);
-                return finishDecode(bm, outPadding, opts);
-            }
-        } catch (IOException ex) {
-            // invalid filedescriptor, no need to call nativeDecodeFileDescriptor()
-            return null;
-        }
-        //Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
-        //return finishDecode(bm, outPadding, opts);
-        */
-    }
-
-    /**
-     * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
-     * return null. The position within the descriptor will not be changed when
-     * this returns, so the descriptor can be used again as is.
-     *
-     * @param fd The file descriptor containing the bitmap data to decode
-     * @return the decoded bitmap, or null
-     */
-    public static Bitmap decodeFileDescriptor(FileDescriptor fd) {
-        return decodeFileDescriptor(fd, null, null);
-    }
-}
-
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
new file mode 100644
index 0000000..080b85f
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2011 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 com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeResources.NinePatchInputStream;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.ninepatch.NinePatchChunk;
+import com.android.resources.Density;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.graphics.BitmapFactory.Options;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Delegate implementing the native methods of android.graphics.BitmapFactory
+ *
+ * Through the layoutlib_create tool, the original native methods of BitmapFactory have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
+ * around to map int to instance of the delegate.
+ *
+ */
+/*package*/ class BitmapFactory_Delegate {
+
+    // ------ Java delegates ------
+
+    @LayoutlibDelegate
+    /*package*/ static Bitmap finishDecode(Bitmap bm, Rect outPadding, Options opts) {
+        if (bm == null || opts == null) {
+            return bm;
+        }
+
+        final int density = opts.inDensity;
+        if (density == 0) {
+            return bm;
+        }
+
+        bm.setDensity(density);
+        final int targetDensity = opts.inTargetDensity;
+        if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
+            return bm;
+        }
+
+        byte[] np = bm.getNinePatchChunk();
+        final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
+        // DELEGATE CHANGE: never scale 9-patch
+        if (opts.inScaled && isNinePatch == false) {
+            float scale = targetDensity / (float)density;
+            // TODO: This is very inefficient and should be done in native by Skia
+            final Bitmap oldBitmap = bm;
+            bm = Bitmap.createScaledBitmap(oldBitmap, (int) (bm.getWidth() * scale + 0.5f),
+                    (int) (bm.getHeight() * scale + 0.5f), true);
+            oldBitmap.recycle();
+
+            if (isNinePatch) {
+                np = nativeScaleNinePatch(np, scale, outPadding);
+                bm.setNinePatchChunk(np);
+            }
+            bm.setDensity(targetDensity);
+        }
+
+        return bm;
+    }
+
+
+    // ------ Native Delegates ------
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeSetDefaultConfig(int nativeConfig) {
+        // pass
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static Bitmap nativeDecodeStream(InputStream is, byte[] storage,
+            Rect padding, Options opts) {
+        Bitmap bm = null;
+
+        Density density = Density.MEDIUM;
+        if (opts != null) {
+            density = Density.getEnum(opts.inDensity);
+        }
+
+        try {
+            if (is instanceof NinePatchInputStream) {
+                NinePatchInputStream npis = (NinePatchInputStream) is;
+                npis.disableFakeMarkSupport();
+
+                // load the bitmap as a nine patch
+                com.android.ninepatch.NinePatch ninePatch = com.android.ninepatch.NinePatch.load(
+                        npis, true /*is9Patch*/, false /*convert*/);
+
+                // get the bitmap and chunk objects.
+                bm = Bitmap_Delegate.createBitmap(ninePatch.getImage(), true /*isMutable*/,
+                        density);
+                NinePatchChunk chunk = ninePatch.getChunk();
+
+                // put the chunk in the bitmap
+                bm.setNinePatchChunk(NinePatch_Delegate.serialize(chunk));
+
+                // read the padding
+                int[] paddingarray = chunk.getPadding();
+                padding.left = paddingarray[0];
+                padding.top = paddingarray[1];
+                padding.right = paddingarray[2];
+                padding.bottom = paddingarray[3];
+            } else {
+                // load the bitmap directly.
+                bm = Bitmap_Delegate.createBitmap(is, true, density);
+            }
+        } catch (IOException e) {
+            Bridge.getLog().error(null,"Failed to load image" , e, null);
+        }
+
+        return bm;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
+            Rect padding, Options opts) {
+        opts.inBitmap = null;
+        return null;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts) {
+        opts.inBitmap = null;
+        return null;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static Bitmap nativeDecodeByteArray(byte[] data, int offset,
+            int length, Options opts) {
+        opts.inBitmap = null;
+        return null;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad) {
+        // don't scale for now. This should not be called anyway since we re-implement
+        // BitmapFactory.finishDecode();
+        return chunk;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nativeIsSeekable(FileDescriptor fd) {
+        return true;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java
deleted file mode 100644
index ad3974c..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2008 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.Paint;
-
-public class BitmapShader extends Shader {
-
-    // we hold on just for the GC, since our native counterpart is using it
-    private final Bitmap mBitmap;
-
-    /**
-     * Call this to create a new shader that will draw with a bitmap.
-     *
-     * @param bitmap            The bitmap to use inside the shader
-     * @param tileX             The tiling mode for x to draw the bitmap in.
-     * @param tileY             The tiling mode for y to draw the bitmap in.
-     */
-    public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY) {
-        mBitmap = bitmap;
-    }
-
-    //---------- Custom methods
-
-    public Bitmap getBitmap() {
-        return mBitmap;
-    }
-
-    @Override
-    Paint getJavaPaint() {
-        return null;
-    }
-}
-
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
new file mode 100644
index 0000000..9a8cf04
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2010 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 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.graphics.Shader.TileMode;
+
+/**
+ * Delegate implementing the native methods of android.graphics.BitmapShader
+ *
+ * Through the layoutlib_create tool, the original native methods of BitmapShader have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original BitmapShader class.
+ *
+ * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
+ * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
+ *
+ * @see Shader_Delegate
+ *
+ */
+public class BitmapShader_Delegate extends Shader_Delegate {
+
+    // ---- delegate data ----
+    private java.awt.Paint mJavaPaint;
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public java.awt.Paint getJavaPaint() {
+        return mJavaPaint;
+    }
+
+    @Override
+    public boolean isSupported() {
+        return true;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        // no message since isSupported returns true;
+        return null;
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreate(int native_bitmap, int shaderTileModeX,
+            int shaderTileModeY) {
+        Bitmap_Delegate bitmap = Bitmap_Delegate.getDelegate(native_bitmap);
+        if (bitmap == null) {
+            return 0;
+        }
+
+        BitmapShader_Delegate newDelegate = new BitmapShader_Delegate(
+                bitmap.getImage(),
+                Shader_Delegate.getTileMode(shaderTileModeX),
+                Shader_Delegate.getTileMode(shaderTileModeY));
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativePostCreate(int native_shader, int native_bitmap,
+            int shaderTileModeX, int shaderTileModeY) {
+        // pass, not needed.
+        return 0;
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    private BitmapShader_Delegate(java.awt.image.BufferedImage image,
+            TileMode tileModeX, TileMode tileModeY) {
+        mJavaPaint = new BitmapShaderPaint(image, tileModeX, tileModeY);
+    }
+
+    private class BitmapShaderPaint implements java.awt.Paint {
+        private final java.awt.image.BufferedImage mImage;
+        private final TileMode mTileModeX;
+        private final TileMode mTileModeY;
+
+        BitmapShaderPaint(java.awt.image.BufferedImage image,
+                TileMode tileModeX, TileMode tileModeY) {
+            mImage = image;
+            mTileModeX = tileModeX;
+            mTileModeY = tileModeY;
+        }
+
+        public java.awt.PaintContext createContext(
+                java.awt.image.ColorModel      colorModel,
+                java.awt.Rectangle             deviceBounds,
+                java.awt.geom.Rectangle2D      userBounds,
+                java.awt.geom.AffineTransform  xform,
+                java.awt.RenderingHints        hints) {
+
+            java.awt.geom.AffineTransform canvasMatrix;
+            try {
+                canvasMatrix = xform.createInverse();
+            } catch (java.awt.geom.NoninvertibleTransformException e) {
+                Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
+                        "Unable to inverse matrix in BitmapShader", e, null /*data*/);
+                canvasMatrix = new java.awt.geom.AffineTransform();
+            }
+
+            java.awt.geom.AffineTransform localMatrix = getLocalMatrix();
+            try {
+                localMatrix = localMatrix.createInverse();
+            } catch (java.awt.geom.NoninvertibleTransformException e) {
+                Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
+                        "Unable to inverse matrix in BitmapShader", e, null /*data*/);
+                localMatrix = new java.awt.geom.AffineTransform();
+            }
+
+            return new BitmapShaderContext(canvasMatrix, localMatrix, colorModel);
+        }
+
+        private class BitmapShaderContext implements java.awt.PaintContext {
+
+            private final java.awt.geom.AffineTransform mCanvasMatrix;
+            private final java.awt.geom.AffineTransform mLocalMatrix;
+            private final java.awt.image.ColorModel mColorModel;
+
+            public BitmapShaderContext(
+                    java.awt.geom.AffineTransform canvasMatrix,
+                    java.awt.geom.AffineTransform localMatrix,
+                    java.awt.image.ColorModel colorModel) {
+                mCanvasMatrix = canvasMatrix;
+                mLocalMatrix = localMatrix;
+                mColorModel = colorModel;
+            }
+
+            public void dispose() {
+            }
+
+            public java.awt.image.ColorModel getColorModel() {
+                return mColorModel;
+            }
+
+            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
+                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
+                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
+
+                int[] data = new int[w*h];
+
+                int index = 0;
+                float[] pt1 = new float[2];
+                float[] pt2 = new float[2];
+                for (int iy = 0 ; iy < h ; iy++) {
+                    for (int ix = 0 ; ix < w ; ix++) {
+                        // handle the canvas transform
+                        pt1[0] = x + ix;
+                        pt1[1] = y + iy;
+                        mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
+
+                        // handle the local matrix.
+                        pt1[0] = pt2[0];
+                        pt1[1] = pt2[1];
+                        mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
+
+                        data[index++] = getColor(pt2[0], pt2[1]);
+                    }
+                }
+
+                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
+
+                return image.getRaster();
+            }
+        }
+
+        /**
+         * Returns a color for an arbitrary point.
+         */
+        private int getColor(float fx, float fy) {
+            int x = getCoordinate(Math.round(fx), mImage.getWidth(), mTileModeX);
+            int y = getCoordinate(Math.round(fy), mImage.getHeight(), mTileModeY);
+
+            return mImage.getRGB(x, y);
+        }
+
+        private int getCoordinate(int i, int size, TileMode mode) {
+            if (i < 0) {
+                switch (mode) {
+                    case CLAMP:
+                        i = 0;
+                        break;
+                    case REPEAT:
+                        i = size - 1 - (-i % size);
+                        break;
+                    case MIRROR:
+                        // this is the same as the positive side, just make the value positive
+                        // first.
+                        i = -i;
+                        int count = i / size;
+                        i = i % size;
+
+                        if ((count % 2) == 1) {
+                            i = size - 1 - i;
+                        }
+                        break;
+                }
+            } else if (i >= size) {
+                switch (mode) {
+                    case CLAMP:
+                        i = size - 1;
+                        break;
+                    case REPEAT:
+                        i = i % size;
+                        break;
+                    case MIRROR:
+                        int count = i / size;
+                        i = i % size;
+
+                        if ((count % 2) == 1) {
+                            i = size - 1 - i;
+                        }
+                        break;
+                }
+            }
+
+            return i;
+        }
+
+
+        public int getTransparency() {
+            return java.awt.Paint.TRANSLUCENT;
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
new file mode 100644
index 0000000..66e59d8
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2010 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 com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.resources.Density;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.os.Parcel;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.Buffer;
+import java.util.Arrays;
+
+import javax.imageio.ImageIO;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Bitmap
+ *
+ * Through the layoutlib_create tool, the original native methods of Bitmap have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Bitmap class.
+ *
+ * @see DelegateManager
+ *
+ */
+public final class Bitmap_Delegate {
+
+    // ---- delegate manager ----
+    private static final DelegateManager<Bitmap_Delegate> sManager =
+            new DelegateManager<Bitmap_Delegate>(Bitmap_Delegate.class);
+
+    // ---- delegate helper data ----
+
+    // ---- delegate data ----
+    private final Config mConfig;
+    private BufferedImage mImage;
+    private boolean mHasAlpha = true;
+    private int mGenerationId = 0;
+
+
+    // ---- Public Helper methods ----
+
+    /**
+     * Returns the native delegate associated to a given {@link Bitmap_Delegate} object.
+     */
+    public static Bitmap_Delegate getDelegate(Bitmap bitmap) {
+        return sManager.getDelegate(bitmap.mNativeBitmap);
+    }
+
+    /**
+     * Returns the native delegate associated to a given an int referencing a {@link Bitmap} object.
+     */
+    public static Bitmap_Delegate getDelegate(int native_bitmap) {
+        return sManager.getDelegate(native_bitmap);
+    }
+
+    /**
+     * Creates and returns a {@link Bitmap} initialized with the given file content.
+     *
+     * @param input the file from which to read the bitmap content
+     * @param isMutable whether the bitmap is mutable
+     * @param density the density associated with the bitmap
+     *
+     * @see Bitmap#isMutable()
+     * @see Bitmap#getDensity()
+     */
+    public static Bitmap createBitmap(File input, boolean isMutable, Density density)
+            throws IOException {
+        // create a delegate with the content of the file.
+        Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input), Config.ARGB_8888);
+
+        return createBitmap(delegate, isMutable, density.getDpiValue());
+    }
+
+    /**
+     * Creates and returns a {@link Bitmap} initialized with the given stream content.
+     *
+     * @param input the stream from which to read the bitmap content
+     * @param isMutable whether the bitmap is mutable
+     * @param density the density associated with the bitmap
+     *
+     * @see Bitmap#isMutable()
+     * @see Bitmap#getDensity()
+     */
+    public static Bitmap createBitmap(InputStream input, boolean isMutable, Density density)
+            throws IOException {
+        // create a delegate with the content of the stream.
+        Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input), Config.ARGB_8888);
+
+        return createBitmap(delegate, isMutable, density.getDpiValue());
+    }
+
+    /**
+     * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
+     *
+     * @param image the bitmap content
+     * @param isMutable whether the bitmap is mutable
+     * @param density the density associated with the bitmap
+     *
+     * @see Bitmap#isMutable()
+     * @see Bitmap#getDensity()
+     */
+    public static Bitmap createBitmap(BufferedImage image, boolean isMutable,
+            Density density) throws IOException {
+        // create a delegate with the given image.
+        Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888);
+
+        return createBitmap(delegate, isMutable, density.getDpiValue());
+    }
+
+    /**
+     * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}.
+     */
+    public static BufferedImage getImage(Bitmap bitmap) {
+        // get the delegate from the native int.
+        Bitmap_Delegate delegate = sManager.getDelegate(bitmap.mNativeBitmap);
+        if (delegate == null) {
+            return null;
+        }
+
+        return delegate.mImage;
+    }
+
+    public static int getBufferedImageType(int nativeBitmapConfig) {
+        switch (Config.sConfigs[nativeBitmapConfig]) {
+            case ALPHA_8:
+                return BufferedImage.TYPE_INT_ARGB;
+            case RGB_565:
+                return BufferedImage.TYPE_INT_ARGB;
+            case ARGB_4444:
+                return BufferedImage.TYPE_INT_ARGB;
+            case ARGB_8888:
+                return BufferedImage.TYPE_INT_ARGB;
+        }
+
+        return BufferedImage.TYPE_INT_ARGB;
+    }
+
+    /**
+     * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}.
+     */
+    public BufferedImage getImage() {
+        return mImage;
+    }
+
+    /**
+     * Returns the Android bitmap config. Note that this not the config of the underlying
+     * Java2D bitmap.
+     */
+    public Config getConfig() {
+        return mConfig;
+    }
+
+    /**
+     * Returns the hasAlpha rendering hint
+     * @return true if the bitmap alpha should be used at render time
+     */
+    public boolean hasAlpha() {
+        return mHasAlpha && mConfig != Config.RGB_565;
+    }
+
+    /**
+     * Update the generationId.
+     *
+     * @see Bitmap#getGenerationId()
+     */
+    public void change() {
+        mGenerationId++;
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width,
+            int height, int nativeConfig, boolean mutable) {
+        int imageType = getBufferedImageType(nativeConfig);
+
+        // create the image
+        BufferedImage image = new BufferedImage(width, height, imageType);
+
+        if (colors != null) {
+            image.setRGB(0, 0, width, height, colors, offset, stride);
+        }
+
+        // create a delegate with the content of the stream.
+        Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.sConfigs[nativeConfig]);
+
+        return createBitmap(delegate, mutable, Bitmap.getDefaultDensity());
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static Bitmap nativeCopy(int srcBitmap, int nativeConfig, boolean isMutable) {
+        Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(srcBitmap);
+        if (srcBmpDelegate == null) {
+            return null;
+        }
+
+        BufferedImage srcImage = srcBmpDelegate.getImage();
+
+        int width = srcImage.getWidth();
+        int height = srcImage.getHeight();
+
+        int imageType = getBufferedImageType(nativeConfig);
+
+        // create the image
+        BufferedImage image = new BufferedImage(width, height, imageType);
+
+        // copy the source image into the image.
+        int[] argb = new int[width * height];
+        srcImage.getRGB(0, 0, width, height, argb, 0, width);
+        image.setRGB(0, 0, width, height, argb, 0, width);
+
+        // create a delegate with the content of the stream.
+        Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.sConfigs[nativeConfig]);
+
+        return createBitmap(delegate, isMutable, Bitmap.getDefaultDensity());
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeDestructor(int nativeBitmap) {
+        sManager.removeJavaReferenceFor(nativeBitmap);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeRecycle(int nativeBitmap) {
+        sManager.removeJavaReferenceFor(nativeBitmap);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nativeCompress(int nativeBitmap, int format, int quality,
+            OutputStream stream, byte[] tempStorage) {
+        Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
+                "Bitmap.compress() is not supported", null /*data*/);
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeErase(int nativeBitmap, int color) {
+        // get the delegate from the native int.
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            return;
+        }
+
+        BufferedImage image = delegate.mImage;
+
+        Graphics2D g = image.createGraphics();
+        try {
+            g.setColor(new java.awt.Color(color, true));
+
+            g.fillRect(0, 0, image.getWidth(), image.getHeight());
+        } finally {
+            g.dispose();
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeWidth(int nativeBitmap) {
+        // get the delegate from the native int.
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            return 0;
+        }
+
+        return delegate.mImage.getWidth();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeHeight(int nativeBitmap) {
+        // get the delegate from the native int.
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            return 0;
+        }
+
+        return delegate.mImage.getHeight();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeRowBytes(int nativeBitmap) {
+        // get the delegate from the native int.
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            return 0;
+        }
+
+        return delegate.mImage.getWidth();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeConfig(int nativeBitmap) {
+        // get the delegate from the native int.
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            return 0;
+        }
+
+        return delegate.mConfig.nativeInt;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nativeHasAlpha(int nativeBitmap) {
+        // get the delegate from the native int.
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            return true;
+        }
+
+        return delegate.mHasAlpha;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeGetPixel(int nativeBitmap, int x, int y) {
+        // get the delegate from the native int.
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            return 0;
+        }
+
+        return delegate.mImage.getRGB(x, y);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeGetPixels(int nativeBitmap, int[] pixels, int offset,
+            int stride, int x, int y, int width, int height) {
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.getImage().getRGB(x, y, width, height, pixels, offset, stride);
+    }
+
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeSetPixel(int nativeBitmap, int x, int y, int color) {
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.getImage().setRGB(x, y, color);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeSetPixels(int nativeBitmap, int[] colors, int offset,
+            int stride, int x, int y, int width, int height) {
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.getImage().setRGB(x, y, width, height, colors, offset, stride);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeCopyPixelsToBuffer(int nativeBitmap, Buffer dst) {
+        // FIXME implement native delegate
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Bitmap.copyPixelsToBuffer is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeCopyPixelsFromBuffer(int nb, Buffer src) {
+        // FIXME implement native delegate
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Bitmap.copyPixelsFromBuffer is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeGenerationId(int nativeBitmap) {
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            return 0;
+        }
+
+        return delegate.mGenerationId;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static Bitmap nativeCreateFromParcel(Parcel p) {
+        // This is only called by Bitmap.CREATOR (Parcelable.Creator<Bitmap>), which is only
+        // used during aidl call so really this should not be called.
+        Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
+                "AIDL is not suppored, and therefore Bitmaps cannot be created from parcels.",
+                null /*data*/);
+        return null;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nativeWriteToParcel(int nativeBitmap, boolean isMutable,
+            int density, Parcel p) {
+        // This is only called when sending a bitmap through aidl, so really this should not
+        // be called.
+        Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
+                "AIDL is not suppored, and therefore Bitmaps cannot be written to parcels.",
+                null /*data*/);
+        return false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static Bitmap nativeExtractAlpha(int nativeBitmap, int nativePaint,
+            int[] offsetXY) {
+        Bitmap_Delegate bitmap = sManager.getDelegate(nativeBitmap);
+        if (bitmap == null) {
+            return null;
+        }
+
+        // get the paint which can be null if nativePaint is 0.
+        Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint);
+
+        if (paint != null && paint.getMaskFilter() != null) {
+            Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
+                    "MaskFilter not supported in Bitmap.extractAlpha",
+                    null, null /*data*/);
+        }
+
+        int alpha = paint != null ? paint.getAlpha() : 0xFF;
+        BufferedImage image = createCopy(bitmap.getImage(), BufferedImage.TYPE_INT_ARGB, alpha);
+
+        // create the delegate. The actual Bitmap config is only an alpha channel
+        Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ALPHA_8);
+
+        // the density doesn't matter, it's set by the Java method.
+        return createBitmap(delegate, false /*isMutable*/,
+                Density.DEFAULT_DENSITY /*density*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativePrepareToDraw(int nativeBitmap) {
+        // nothing to be done here.
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeSetHasAlpha(int nativeBitmap, boolean hasAlpha) {
+        // get the delegate from the native int.
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.mHasAlpha = hasAlpha;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nativeSameAs(int nb0, int nb1) {
+        Bitmap_Delegate delegate1 = sManager.getDelegate(nb0);
+        if (delegate1 == null) {
+            return false;
+        }
+
+        Bitmap_Delegate delegate2 = sManager.getDelegate(nb1);
+        if (delegate2 == null) {
+            return false;
+        }
+
+        BufferedImage image1 = delegate1.getImage();
+        BufferedImage image2 = delegate2.getImage();
+        if (delegate1.mConfig != delegate2.mConfig ||
+                image1.getWidth() != image2.getWidth() ||
+                image1.getHeight() != image2.getHeight()) {
+            return false;
+        }
+
+        // get the internal data
+        int w = image1.getWidth();
+        int h = image2.getHeight();
+        int[] argb1 = new int[w*h];
+        int[] argb2 = new int[w*h];
+
+        image1.getRGB(0, 0, w, h, argb1, 0, w);
+        image2.getRGB(0, 0, w, h, argb2, 0, w);
+
+        // compares
+        if (delegate1.mConfig == Config.ALPHA_8) {
+            // in this case we have to manually compare the alpha channel as the rest is garbage.
+            final int length = w*h;
+            for (int i = 0 ; i < length ; i++) {
+                if ((argb1[i] & 0xFF000000) != (argb2[i] & 0xFF000000)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        return Arrays.equals(argb1, argb2);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    private Bitmap_Delegate(BufferedImage image, Config config) {
+        mImage = image;
+        mConfig = config;
+    }
+
+    private static Bitmap createBitmap(Bitmap_Delegate delegate, boolean isMutable, int density) {
+        // get its native_int
+        int nativeInt = sManager.addNewDelegate(delegate);
+
+        // and create/return a new Bitmap with it
+        return new Bitmap(nativeInt, null /* buffer */, isMutable, null /*ninePatchChunk*/, density);
+    }
+
+    /**
+     * Creates and returns a copy of a given BufferedImage.
+     * <p/>
+     * if alpha is different than 255, then it is applied to the alpha channel of each pixel.
+     *
+     * @param image the image to copy
+     * @param imageType the type of the new image
+     * @param alpha an optional alpha modifier
+     * @return a new BufferedImage
+     */
+    /*package*/ static BufferedImage createCopy(BufferedImage image, int imageType, int alpha) {
+        int w = image.getWidth();
+        int h = image.getHeight();
+
+        BufferedImage result = new BufferedImage(w, h, imageType);
+
+        int[] argb = new int[w * h];
+        image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
+
+        if (alpha != 255) {
+            final int length = argb.length;
+            for (int i = 0 ; i < length; i++) {
+                int a = (argb[i] >>> 24 * alpha) / 255;
+                argb[i] = (a << 24) | (argb[i] & 0x00FFFFFF);
+            }
+        }
+
+        result.setRGB(0, 0, w, h, argb, 0, w);
+
+        return result;
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/BlurMaskFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BlurMaskFilter_Delegate.java
new file mode 100644
index 0000000..4becba1
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/BlurMaskFilter_Delegate.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate implementing the native methods of android.graphics.BlurMaskFilter
+ *
+ * Through the layoutlib_create tool, the original native methods of BlurMaskFilter have
+ * been replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original BlurMaskFilter class.
+ *
+ * Because this extends {@link MaskFilter_Delegate}, there's no need to use a
+ * {@link DelegateManager}, as all the Shader classes will be added to the manager
+ * owned by {@link MaskFilter_Delegate}.
+ *
+ * @see MaskFilter_Delegate
+ *
+ */
+public class BlurMaskFilter_Delegate extends MaskFilter_Delegate {
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public boolean isSupported() {
+        return false;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        return "Blur Mask Filters are not supported.";
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeConstructor(float radius, int style) {
+        BlurMaskFilter_Delegate newDelegate = new BlurMaskFilter_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
deleted file mode 100644
index d5d315e..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java
+++ /dev/null
@@ -1,1279 +0,0 @@
-/*
- * Copyright (C) 2008 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 com.android.layoutlib.api.ILayoutLog;
-
-import android.graphics.DrawFilter;
-import android.graphics.Picture;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
-import android.graphics.Xfermode;
-import android.graphics.Paint.Align;
-import android.graphics.Paint.FontInfo;
-import android.graphics.Paint.Style;
-import android.graphics.Region.Op;
-
-import java.awt.AlphaComposite;
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Composite;
-import java.awt.Graphics2D;
-import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.geom.AffineTransform;
-import java.awt.image.BufferedImage;
-import java.util.List;
-import java.util.Stack;
-
-import javax.microedition.khronos.opengles.GL;
-
-/**
- * Re-implementation of the Canvas, 100% in java on top of a BufferedImage.
- */
-public class Canvas extends _Original_Canvas {
-
-    private BufferedImage mBufferedImage;
-    private final Stack<Graphics2D> mGraphicsStack = new Stack<Graphics2D>();
-    private final ILayoutLog mLogger;
-
-    public Canvas() {
-        mLogger = null;
-        // the mBufferedImage will be taken from a bitmap in #setBitmap()
-    }
-
-    public Canvas(Bitmap bitmap) {
-        mLogger = null;
-        mBufferedImage = bitmap.getImage();
-        mGraphicsStack.push(mBufferedImage.createGraphics());
-    }
-
-    public Canvas(int nativeCanvas) {
-        mLogger = null;
-        throw new UnsupportedOperationException("Can't create Canvas(int)");
-    }
-
-    public Canvas(javax.microedition.khronos.opengles.GL gl) {
-        mLogger = null;
-        throw new UnsupportedOperationException("Can't create Canvas(javax.microedition.khronos.opengles.GL)");
-    }
-
-    // custom constructors for our use.
-    public Canvas(int width, int height, ILayoutLog logger) {
-        mLogger = logger;
-        mBufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
-        mGraphicsStack.push(mBufferedImage.createGraphics());
-    }
-
-    public Canvas(int width, int height) {
-        this(width, height, null /* logger*/);
-    }
-
-    // custom mehtods
-    public BufferedImage getImage() {
-        return mBufferedImage;
-    }
-
-    public Graphics2D getGraphics2d() {
-        return mGraphicsStack.peek();
-    }
-
-    public void dispose() {
-        while (mGraphicsStack.size() > 0) {
-            mGraphicsStack.pop().dispose();
-        }
-    }
-
-    /**
-     * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
-     * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
-     */
-    private Graphics2D getCustomGraphics(Paint paint) {
-        // make new one
-        Graphics2D g = getGraphics2d();
-        g = (Graphics2D)g.create();
-
-        // configure it
-        g.setColor(new Color(paint.getColor()));
-        int alpha = paint.getAlpha();
-        float falpha = alpha / 255.f;
-
-        Style style = paint.getStyle();
-        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-            PathEffect e = paint.getPathEffect();
-            if (e instanceof DashPathEffect) {
-                DashPathEffect dpe = (DashPathEffect)e;
-                g.setStroke(new BasicStroke(
-                        paint.getStrokeWidth(),
-                        paint.getStrokeCap().getJavaCap(),
-                        paint.getStrokeJoin().getJavaJoin(),
-                        paint.getStrokeMiter(),
-                        dpe.getIntervals(),
-                        dpe.getPhase()));
-            } else {
-                g.setStroke(new BasicStroke(
-                        paint.getStrokeWidth(),
-                        paint.getStrokeCap().getJavaCap(),
-                        paint.getStrokeJoin().getJavaJoin(),
-                        paint.getStrokeMiter()));
-            }
-        }
-
-        Xfermode xfermode = paint.getXfermode();
-        if (xfermode instanceof PorterDuffXfermode) {
-            PorterDuff.Mode mode = ((PorterDuffXfermode)xfermode).getMode();
-
-            setModeInGraphics(mode, g, falpha);
-        } else {
-            if (mLogger != null && xfermode != null) {
-                mLogger.warning(String.format(
-                        "Xfermode '%1$s' is not supported in the Layout Editor.",
-                        xfermode.getClass().getCanonicalName()));
-            }
-            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
-        }
-
-        Shader shader = paint.getShader();
-        if (shader != null) {
-            java.awt.Paint shaderPaint = shader.getJavaPaint();
-            if (shaderPaint != null) {
-                g.setPaint(shaderPaint);
-            } else {
-                if (mLogger != null) {
-                    mLogger.warning(String.format(
-                            "Shader '%1$s' is not supported in the Layout Editor.",
-                            shader.getClass().getCanonicalName()));
-                }
-            }
-        }
-
-        return g;
-    }
-
-    private void setModeInGraphics(PorterDuff.Mode mode, Graphics2D g, float falpha) {
-        switch (mode) {
-            case CLEAR:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, falpha));
-                break;
-            case DARKEN:
-                break;
-            case DST:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST, falpha));
-                break;
-            case DST_ATOP:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, falpha));
-                break;
-            case DST_IN:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN, falpha));
-                break;
-            case DST_OUT:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OUT, falpha));
-                break;
-            case DST_OVER:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, falpha));
-                break;
-            case LIGHTEN:
-                break;
-            case MULTIPLY:
-                break;
-            case SCREEN:
-                break;
-            case SRC:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, falpha));
-                break;
-            case SRC_ATOP:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, falpha));
-                break;
-            case SRC_IN:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, falpha));
-                break;
-            case SRC_OUT:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT, falpha));
-                break;
-            case SRC_OVER:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
-                break;
-            case XOR:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR, falpha));
-                break;
-        }
-    }
-
-
-    // --------------------
-    // OVERRIDEN ENUMS
-    // This is needed since we rename Canvas into _Original_Canvas
-    // --------------------
-
-    public enum EdgeType {
-        BW(0),  //!< treat edges by just rounding to nearest pixel boundary
-        AA(1);  //!< treat edges by rounding-out, since they may be antialiased
-
-        EdgeType(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-    }
-
-
-    // --------------------
-    // OVERRIDEN METHODS
-    // --------------------
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#setBitmap(android.graphics.Bitmap)
-     */
-    @Override
-    public void setBitmap(Bitmap bitmap) {
-        mBufferedImage = bitmap.getImage();
-        mGraphicsStack.push(mBufferedImage.createGraphics());
-    }
-
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#translate(float, float)
-     */
-    @Override
-    public void translate(float dx, float dy) {
-        getGraphics2d().translate(dx, dy);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#save()
-     */
-    @Override
-    public int save() {
-        // get the current save count
-        int count = mGraphicsStack.size();
-
-        // create a new graphics and add it to the stack
-        Graphics2D g = (Graphics2D)getGraphics2d().create();
-        mGraphicsStack.push(g);
-
-        // return the old save count
-        return count;
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#save(int)
-     */
-    @Override
-    public int save(int saveFlags) {
-        // For now we ignore saveFlags
-        return save();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#restore()
-     */
-    @Override
-    public void restore() {
-        mGraphicsStack.pop();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#restoreToCount(int)
-     */
-    @Override
-    public void restoreToCount(int saveCount) {
-        while (mGraphicsStack.size() > saveCount) {
-            mGraphicsStack.pop();
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getSaveCount()
-     */
-    @Override
-    public int getSaveCount() {
-        return mGraphicsStack.size();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(float, float, float, float, android.graphics.Region.Op)
-     */
-    @Override
-    public boolean clipRect(float left, float top, float right, float bottom, Op op) {
-        return clipRect(left, top, right, bottom);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(float, float, float, float)
-     */
-    @Override
-    public boolean clipRect(float left, float top, float right, float bottom) {
-        getGraphics2d().clipRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
-        return true;
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(int, int, int, int)
-     */
-    @Override
-    public boolean clipRect(int left, int top, int right, int bottom) {
-        getGraphics2d().clipRect(left, top, right-left, bottom-top);
-        return true;
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(android.graphics.Rect, android.graphics.Region.Op)
-     */
-    @Override
-    public boolean clipRect(Rect rect, Op op) {
-        return clipRect(rect.left, rect.top, rect.right, rect.bottom);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(android.graphics.Rect)
-     */
-    @Override
-    public boolean clipRect(Rect rect) {
-        return clipRect(rect.left, rect.top, rect.right, rect.bottom);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(android.graphics.RectF, android.graphics.Region.Op)
-     */
-    @Override
-    public boolean clipRect(RectF rect, Op op) {
-        return clipRect(rect.left, rect.top, rect.right, rect.bottom);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(android.graphics.RectF)
-     */
-    @Override
-    public boolean clipRect(RectF rect) {
-        return clipRect(rect.left, rect.top, rect.right, rect.bottom);
-    }
-
-    public boolean quickReject(RectF rect, EdgeType type) {
-        return false;
-    }
-
-    @Override
-    public boolean quickReject(RectF rect, _Original_Canvas.EdgeType type) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public boolean quickReject(Path path, EdgeType type) {
-        return false;
-    }
-
-    @Override
-    public boolean quickReject(Path path, _Original_Canvas.EdgeType type) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public boolean quickReject(float left, float top, float right, float bottom,
-                               EdgeType type) {
-        return false;
-    }
-
-    @Override
-    public boolean quickReject(float left, float top, float right, float bottom,
-                               _Original_Canvas.EdgeType type) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /**
-     * Retrieve the clip bounds, returning true if they are non-empty.
-     *
-     * @param bounds Return the clip bounds here. If it is null, ignore it but
-     *               still return true if the current clip is non-empty.
-     * @return true if the current clip is non-empty.
-     */
-    @Override
-    public boolean getClipBounds(Rect bounds) {
-        Rectangle rect = getGraphics2d().getClipBounds();
-        if (rect != null) {
-            bounds.left = rect.x;
-            bounds.top = rect.y;
-            bounds.right = rect.x + rect.width;
-            bounds.bottom = rect.y + rect.height;
-            return true;
-        }
-        return false;
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawColor(int, android.graphics.PorterDuff.Mode)
-     */
-    @Override
-    public void drawColor(int color, PorterDuff.Mode mode) {
-        Graphics2D g = getGraphics2d();
-
-        // save old color
-        Color c = g.getColor();
-
-        Composite composite = g.getComposite();
-
-        // get the alpha from the color
-        int alpha = color >>> 24;
-        float falpha = alpha / 255.f;
-
-        setModeInGraphics(mode, g, falpha);
-
-        g.setColor(new Color(color));
-
-        g.fillRect(0, 0, getWidth(), getHeight());
-
-        g.setComposite(composite);
-
-        // restore color
-        g.setColor(c);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawColor(int)
-     */
-    @Override
-    public void drawColor(int color) {
-        drawColor(color, PorterDuff.Mode.SRC_OVER);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawARGB(int, int, int, int)
-     */
-    @Override
-    public void drawARGB(int a, int r, int g, int b) {
-        drawColor(a << 24 | r << 16 | g << 8 | b, PorterDuff.Mode.SRC_OVER);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawRGB(int, int, int)
-     */
-    @Override
-    public void drawRGB(int r, int g, int b) {
-        drawColor(0xFF << 24 | r << 16 | g << 8 | b, PorterDuff.Mode.SRC_OVER);
-    }
-
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getWidth()
-     */
-    @Override
-    public int getWidth() {
-        return mBufferedImage.getWidth();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getHeight()
-     */
-    @Override
-    public int getHeight() {
-        return mBufferedImage.getHeight();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPaint(android.graphics.Paint)
-     */
-    @Override
-    public void drawPaint(Paint paint) {
-        drawColor(paint.getColor());
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
-        drawBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
-                (int)left, (int)top,
-                (int)left+bitmap.getWidth(), (int)top+bitmap.getHeight(), paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, android.graphics.Matrix, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
-        boolean needsRestore = false;
-        if (matrix.isIdentity() == false) {
-            // create a new graphics and apply the matrix to it
-            save(); // this creates a new Graphics2D, and stores it for children call to use
-            needsRestore = true;
-            Graphics2D g = getGraphics2d(); // get the newly create Graphics2D
-
-            // get the Graphics2D current matrix
-            AffineTransform currentTx = g.getTransform();
-            // get the AffineTransform from the matrix
-            AffineTransform matrixTx = matrix.getTransform();
-
-            // combine them so that the matrix is applied after.
-            currentTx.preConcatenate(matrixTx);
-
-            // give it to the graphics as a new matrix replacing all previous transform
-            g.setTransform(currentTx);
-        }
-
-        // draw the bitmap
-        drawBitmap(bitmap, 0, 0, paint);
-
-        if (needsRestore) {
-            // remove the new graphics
-            restore();
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.Rect, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
-        if (src == null) {
-            drawBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
-                    dst.left, dst.top, dst.right, dst.bottom, paint);
-        } else {
-            drawBitmap(bitmap, src.left, src.top, src.width(), src.height(),
-                    dst.left, dst.top, dst.right, dst.bottom, paint);
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.RectF, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
-        if (src == null) {
-            drawBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
-                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom, paint);
-        } else {
-            drawBitmap(bitmap, src.left, src.top, src.width(), src.height(),
-                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom, paint);
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmap(int[], int, int, int, int, int, int, boolean, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmap(int[] colors, int offset, int stride, int x, int y, int width,
-            int height, boolean hasAlpha, Paint paint) {
-        throw new UnsupportedOperationException();
-    }
-
-    private void drawBitmap(Bitmap bitmap, int sleft, int stop, int sright, int sbottom, int dleft,
-            int dtop, int dright, int dbottom, Paint paint) {
-        BufferedImage image = bitmap.getImage();
-
-        Graphics2D g = getGraphics2d();
-
-        Composite c = null;
-
-        if (paint != null) {
-            if (paint.isFilterBitmap()) {
-                g = (Graphics2D)g.create();
-                g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
-                        RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-            }
-
-            if (paint.getAlpha() != 0xFF) {
-                c = g.getComposite();
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
-                        paint.getAlpha()/255.f));
-            }
-        }
-
-        g.drawImage(image, dleft, dtop, dright, dbottom,
-                sleft, stop, sright, sbottom, null);
-
-        if (paint != null) {
-            if (paint.isFilterBitmap()) {
-                g.dispose();
-            }
-            if (c != null) {
-                g.setComposite(c);
-            }
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#rotate(float, float, float)
-     */
-    @Override
-    public void rotate(float degrees, float px, float py) {
-        if (degrees != 0) {
-            Graphics2D g = getGraphics2d();
-            g.translate(px, py);
-            g.rotate(Math.toRadians(degrees));
-            g.translate(-px, -py);
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#rotate(float)
-     */
-    @Override
-    public void rotate(float degrees) {
-        getGraphics2d().rotate(Math.toRadians(degrees));
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#scale(float, float, float, float)
-     */
-    @Override
-    public void scale(float sx, float sy, float px, float py) {
-        Graphics2D g = getGraphics2d();
-        g.translate(px, py);
-        g.scale(sx, sy);
-        g.translate(-px, -py);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#scale(float, float)
-     */
-    @Override
-    public void scale(float sx, float sy) {
-        getGraphics2d().scale(sx, sy);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawText(char[], int, int, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawText(char[] text, int index, int count, float x, float y, Paint paint) {
-        // WARNING: the logic in this method is similar to Paint.measureText.
-        // Any change to this method should be reflected in Paint.measureText
-        Graphics2D g = getGraphics2d();
-
-        g = (Graphics2D)g.create();
-        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
-
-        // set the color. because this only handles RGB, the alpha channel is handled
-        // as a composite.
-        g.setColor(new Color(paint.getColor()));
-        int alpha = paint.getAlpha();
-        float falpha = alpha / 255.f;
-        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
-
-
-        // Paint.TextAlign indicates how the text is positioned relative to X.
-        // LEFT is the default and there's nothing to do.
-        if (paint.getTextAlign() != Align.LEFT) {
-            float m = paint.measureText(text, index, count);
-            if (paint.getTextAlign() == Align.CENTER) {
-                x -= m / 2;
-            } else if (paint.getTextAlign() == Align.RIGHT) {
-                x -= m;
-            }
-        }
-
-        List<FontInfo> fonts = paint.getFonts();
-        try {
-            if (fonts.size() > 0) {
-                FontInfo mainFont = fonts.get(0);
-                int i = index;
-                int lastIndex = index + count;
-                while (i < lastIndex) {
-                    // always start with the main font.
-                    int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
-                    if (upTo == -1) {
-                        // draw all the rest and exit.
-                        g.setFont(mainFont.mFont);
-                        g.drawChars(text, i, lastIndex - i, (int)x, (int)y);
-                        return;
-                    } else if (upTo > 0) {
-                        // draw what's possible
-                        g.setFont(mainFont.mFont);
-                        g.drawChars(text, i, upTo - i, (int)x, (int)y);
-
-                        // compute the width that was drawn to increase x
-                        x += mainFont.mMetrics.charsWidth(text, i, upTo - i);
-
-                        // move index to the first non displayed char.
-                        i = upTo;
-
-                        // don't call continue at this point. Since it is certain the main font
-                        // cannot display the font a index upTo (now ==i), we move on to the
-                        // fallback fonts directly.
-                    }
-
-                    // no char supported, attempt to read the next char(s) with the
-                    // fallback font. In this case we only test the first character
-                    // and then go back to test with the main font.
-                    // Special test for 2-char characters.
-                    boolean foundFont = false;
-                    for (int f = 1 ; f < fonts.size() ; f++) {
-                        FontInfo fontInfo = fonts.get(f);
-
-                        // need to check that the font can display the character. We test
-                        // differently if the char is a high surrogate.
-                        int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
-                        upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
-                        if (upTo == -1) {
-                            // draw that char
-                            g.setFont(fontInfo.mFont);
-                            g.drawChars(text, i, charCount, (int)x, (int)y);
-
-                            // update x
-                            x += fontInfo.mMetrics.charsWidth(text, i, charCount);
-
-                            // update the index in the text, and move on
-                            i += charCount;
-                            foundFont = true;
-                            break;
-
-                        }
-                    }
-
-                    // in case no font can display the char, display it with the main font.
-                    // (it'll put a square probably)
-                    if (foundFont == false) {
-                        int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
-
-                        g.setFont(mainFont.mFont);
-                        g.drawChars(text, i, charCount, (int)x, (int)y);
-
-                        // measure it to advance x
-                        x += mainFont.mMetrics.charsWidth(text, i, charCount);
-
-                        // and move to the next chars.
-                        i += charCount;
-                    }
-                }
-            }
-        } finally {
-            g.dispose();
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
-        drawText(text.toString().toCharArray(), start, end - start, x, y, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawText(java.lang.String, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawText(String text, float x, float y, Paint paint) {
-        drawText(text.toCharArray(), 0, text.length(), x, y, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawText(java.lang.String, int, int, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawText(String text, int start, int end, float x, float y, Paint paint) {
-        drawText(text.toCharArray(), start, end - start, x, y, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawRect(android.graphics.RectF, android.graphics.Paint)
-     */
-    @Override
-    public void drawRect(RectF rect, Paint paint) {
-        doDrawRect((int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawRect(float, float, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawRect(float left, float top, float right, float bottom, Paint paint) {
-        doDrawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top), paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawRect(android.graphics.Rect, android.graphics.Paint)
-     */
-    @Override
-    public void drawRect(Rect r, Paint paint) {
-        doDrawRect(r.left, r.top, r.width(), r.height(), paint);
-    }
-
-    private final void doDrawRect(int left, int top, int width, int height, Paint paint) {
-        if (width > 0 && height > 0) {
-            // get a Graphics2D object configured with the drawing parameters.
-            Graphics2D g = getCustomGraphics(paint);
-
-            Style style = paint.getStyle();
-
-            // draw
-            if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
-                g.fillRect(left, top, width, height);
-            }
-
-            if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-                g.drawRect(left, top, width, height);
-            }
-
-            // dispose Graphics2D object
-            g.dispose();
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawRoundRect(android.graphics.RectF, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
-        if (rect.width() > 0 && rect.height() > 0) {
-            // get a Graphics2D object configured with the drawing parameters.
-            Graphics2D g = getCustomGraphics(paint);
-
-            Style style = paint.getStyle();
-
-            // draw
-
-            int arcWidth = (int)(rx * 2);
-            int arcHeight = (int)(ry * 2);
-
-            if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
-                g.fillRoundRect((int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(),
-                        arcWidth, arcHeight);
-            }
-
-            if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-                g.drawRoundRect((int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(),
-                        arcWidth, arcHeight);
-            }
-
-            // dispose Graphics2D object
-            g.dispose();
-        }
-    }
-
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawLine(float, float, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
-        // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D g = getCustomGraphics(paint);
-
-        g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
-
-        // dispose Graphics2D object
-        g.dispose();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawLines(float[], int, int, android.graphics.Paint)
-     */
-    @Override
-    public void drawLines(float[] pts, int offset, int count, Paint paint) {
-        // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D g = getCustomGraphics(paint);
-
-        for (int i = 0 ; i < count ; i += 4) {
-            g.drawLine((int)pts[i + offset], (int)pts[i + offset + 1],
-                    (int)pts[i + offset + 2], (int)pts[i + offset + 3]);
-        }
-
-        // dispose Graphics2D object
-        g.dispose();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawLines(float[], android.graphics.Paint)
-     */
-    @Override
-    public void drawLines(float[] pts, Paint paint) {
-        drawLines(pts, 0, pts.length, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawCircle(float, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawCircle(float cx, float cy, float radius, Paint paint) {
-        // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D g = getCustomGraphics(paint);
-
-        Style style = paint.getStyle();
-
-        int size = (int)(radius * 2);
-
-        // draw
-        if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
-            g.fillOval((int)(cx - radius), (int)(cy - radius), size, size);
-        }
-
-        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-            g.drawOval((int)(cx - radius), (int)(cy - radius), size, size);
-        }
-
-        // dispose Graphics2D object
-        g.dispose();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawOval(android.graphics.RectF, android.graphics.Paint)
-     */
-    @Override
-    public void drawOval(RectF oval, Paint paint) {
-        // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D g = getCustomGraphics(paint);
-
-        Style style = paint.getStyle();
-
-        // draw
-        if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
-            g.fillOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height());
-        }
-
-        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-            g.drawOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height());
-        }
-
-        // dispose Graphics2D object
-        g.dispose();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPath(android.graphics.Path, android.graphics.Paint)
-     */
-    @Override
-    public void drawPath(Path path, Paint paint) {
-        // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D g = getCustomGraphics(paint);
-
-        Style style = paint.getStyle();
-
-        // draw
-        if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
-            g.fill(path.getAwtShape());
-        }
-
-        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-            g.draw(path.getAwtShape());
-        }
-
-        // dispose Graphics2D object
-        g.dispose();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#setMatrix(android.graphics.Matrix)
-     */
-    @Override
-    public void setMatrix(Matrix matrix) {
-        // get the new current graphics
-        Graphics2D g = getGraphics2d();
-
-        // and apply the matrix
-        g.setTransform(matrix.getTransform());
-
-        if (mLogger != null && matrix.hasPerspective()) {
-            mLogger.warning("android.graphics.Canvas#setMatrix(android.graphics.Matrix) only supports affine transformations in the Layout Editor.");
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#concat(android.graphics.Matrix)
-     */
-    @Override
-    public void concat(Matrix matrix) {
-        // get the current top graphics2D object.
-        Graphics2D g = getGraphics2d();
-
-        // get its current matrix
-        AffineTransform currentTx = g.getTransform();
-        // get the AffineTransform of the given matrix
-        AffineTransform matrixTx = matrix.getTransform();
-
-        // combine them so that the given matrix is applied after.
-        currentTx.preConcatenate(matrixTx);
-
-        // give it to the graphics2D as a new matrix replacing all previous transform
-        g.setTransform(currentTx);
-    }
-
-
-    // --------------------
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipPath(android.graphics.Path, android.graphics.Region.Op)
-     */
-    @Override
-    public boolean clipPath(Path path, Op op) {
-        // TODO Auto-generated method stub
-        return super.clipPath(path, op);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipPath(android.graphics.Path)
-     */
-    @Override
-    public boolean clipPath(Path path) {
-        // TODO Auto-generated method stub
-        return super.clipPath(path);
-    }
-
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRegion(android.graphics.Region, android.graphics.Region.Op)
-     */
-    @Override
-    public boolean clipRegion(Region region, Op op) {
-        // TODO Auto-generated method stub
-        return super.clipRegion(region, op);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRegion(android.graphics.Region)
-     */
-    @Override
-    public boolean clipRegion(Region region) {
-        // TODO Auto-generated method stub
-        return super.clipRegion(region);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint)
-     */
-    @Override
-    public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
-            Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawArc(oval, startAngle, sweepAngle, useCenter, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmapMesh(android.graphics.Bitmap, int, int, float[], int, int[], int, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,
-            int vertOffset, int[] colors, int colorOffset, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset, colors, colorOffset, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPicture(android.graphics.Picture, android.graphics.Rect)
-     */
-    @Override
-    public void drawPicture(Picture picture, Rect dst) {
-        // TODO Auto-generated method stub
-        super.drawPicture(picture, dst);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPicture(android.graphics.Picture, android.graphics.RectF)
-     */
-    @Override
-    public void drawPicture(Picture picture, RectF dst) {
-        // TODO Auto-generated method stub
-        super.drawPicture(picture, dst);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPicture(android.graphics.Picture)
-     */
-    @Override
-    public void drawPicture(Picture picture) {
-        // TODO Auto-generated method stub
-        super.drawPicture(picture);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPoint(float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawPoint(float x, float y, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawPoint(x, y, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPoints(float[], int, int, android.graphics.Paint)
-     */
-    @Override
-    public void drawPoints(float[] pts, int offset, int count, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawPoints(pts, offset, count, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPoints(float[], android.graphics.Paint)
-     */
-    @Override
-    public void drawPoints(float[] pts, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawPoints(pts, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPosText(char[], int, int, float[], android.graphics.Paint)
-     */
-    @Override
-    public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawPosText(text, index, count, pos, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPosText(java.lang.String, float[], android.graphics.Paint)
-     */
-    @Override
-    public void drawPosText(String text, float[] pos, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawPosText(text, pos, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawTextOnPath(char[], int, int, android.graphics.Path, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawTextOnPath(char[] text, int index, int count, Path path, float offset,
-            float offset2, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawTextOnPath(text, index, count, path, offset, offset2, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawTextOnPath(java.lang.String, android.graphics.Path, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawTextOnPath(String text, Path path, float offset, float offset2, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawTextOnPath(text, path, offset, offset2, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawVertices(android.graphics.Canvas.VertexMode, int, float[], int, float[], int, int[], int, short[], int, int, android.graphics.Paint)
-     */
-    @Override
-    public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
-            float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices,
-            int indexOffset, int indexCount, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawVertices(mode, vertexCount, verts, vertOffset, texs, texOffset, colors, colorOffset,
-                indices, indexOffset, indexCount, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getDrawFilter()
-     */
-    @Override
-    public DrawFilter getDrawFilter() {
-        // TODO Auto-generated method stub
-        return super.getDrawFilter();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getGL()
-     */
-    @Override
-    public GL getGL() {
-        // TODO Auto-generated method stub
-        return super.getGL();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getMatrix()
-     */
-    @Override
-    public Matrix getMatrix() {
-        // TODO Auto-generated method stub
-        return super.getMatrix();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getMatrix(android.graphics.Matrix)
-     */
-    @Override
-    public void getMatrix(Matrix ctm) {
-        // TODO Auto-generated method stub
-        super.getMatrix(ctm);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#isOpaque()
-     */
-    @Override
-    public boolean isOpaque() {
-        // TODO Auto-generated method stub
-        return super.isOpaque();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#saveLayer(float, float, float, float, android.graphics.Paint, int)
-     */
-    @Override
-    public int saveLayer(float left, float top, float right, float bottom, Paint paint,
-            int saveFlags) {
-        // TODO Auto-generated method stub
-        return super.saveLayer(left, top, right, bottom, paint, saveFlags);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#saveLayer(android.graphics.RectF, android.graphics.Paint, int)
-     */
-    @Override
-    public int saveLayer(RectF bounds, Paint paint, int saveFlags) {
-        // TODO Auto-generated method stub
-        return super.saveLayer(bounds, paint, saveFlags);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#saveLayerAlpha(float, float, float, float, int, int)
-     */
-    @Override
-    public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
-            int saveFlags) {
-        // TODO Auto-generated method stub
-        return super.saveLayerAlpha(left, top, right, bottom, alpha, saveFlags);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#saveLayerAlpha(android.graphics.RectF, int, int)
-     */
-    @Override
-    public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags) {
-        // TODO Auto-generated method stub
-        return super.saveLayerAlpha(bounds, alpha, saveFlags);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#setDrawFilter(android.graphics.DrawFilter)
-     */
-    @Override
-    public void setDrawFilter(DrawFilter filter) {
-        // TODO Auto-generated method stub
-        super.setDrawFilter(filter);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#setViewport(int, int)
-     */
-    @Override
-    public void setViewport(int width, int height) {
-        // TODO Auto-generated method stub
-        super.setViewport(width, height);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#skew(float, float)
-     */
-    @Override
-    public void skew(float sx, float sy) {
-        // TODO Auto-generated method stub
-        super.skew(sx, sy);
-    }
-
-
-
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
new file mode 100644
index 0000000..4decd1a
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -0,0 +1,1334 @@
+/*
+ * Copyright (C) 2010 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 com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.layoutlib.bridge.impl.GcSnapshot;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.graphics.Bitmap.Config;
+import android.graphics.Paint_Delegate.FontInfo;
+import android.text.TextUtils;
+
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.util.List;
+
+
+/**
+ * Delegate implementing the native methods of android.graphics.Canvas
+ *
+ * Through the layoutlib_create tool, the original native methods of Canvas have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Canvas class.
+ *
+ * @see DelegateManager
+ *
+ */
+public final class Canvas_Delegate {
+
+    // ---- delegate manager ----
+    private static final DelegateManager<Canvas_Delegate> sManager =
+            new DelegateManager<Canvas_Delegate>(Canvas_Delegate.class);
+
+    // ---- delegate helper data ----
+
+    private final static boolean[] sBoolOut = new boolean[1];
+
+    // ---- delegate data ----
+    private Bitmap_Delegate mBitmap;
+    private GcSnapshot mSnapshot;
+
+    private DrawFilter_Delegate mDrawFilter = null;
+
+    // ---- Public Helper methods ----
+
+    /**
+     * Returns the native delegate associated to a given {@link Canvas} object.
+     */
+    public static Canvas_Delegate getDelegate(Canvas canvas) {
+        return sManager.getDelegate(canvas.mNativeCanvas);
+    }
+
+    /**
+     * Returns the native delegate associated to a given an int referencing a {@link Canvas} object.
+     */
+    public static Canvas_Delegate getDelegate(int native_canvas) {
+        return sManager.getDelegate(native_canvas);
+    }
+
+    /**
+     * Returns the current {@link Graphics2D} used to draw.
+     */
+    public GcSnapshot getSnapshot() {
+        return mSnapshot;
+    }
+
+    /**
+     * Returns the {@link DrawFilter} delegate or null if none have been set.
+     *
+     * @return the delegate or null.
+     */
+    public DrawFilter_Delegate getDrawFilter() {
+        return mDrawFilter;
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static boolean isOpaque(Canvas thisCanvas) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            return false;
+        }
+
+        return canvasDelegate.mBitmap.getConfig() == Config.RGB_565;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int getWidth(Canvas thisCanvas) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            return 0;
+        }
+
+        return canvasDelegate.mBitmap.getImage().getWidth();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int getHeight(Canvas thisCanvas) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            return 0;
+        }
+
+        return canvasDelegate.mBitmap.getImage().getHeight();
+    }
+
+    @LayoutlibDelegate
+   /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        canvasDelegate.getSnapshot().translate(dx, dy);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void rotate(Canvas thisCanvas, float degrees) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees));
+    }
+
+    @LayoutlibDelegate
+   /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        canvasDelegate.getSnapshot().scale(sx, sy);
+    }
+
+    @LayoutlibDelegate
+   /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        // get the current top graphics2D object.
+        GcSnapshot g = canvasDelegate.getSnapshot();
+
+        // get its current matrix
+        AffineTransform currentTx = g.getTransform();
+        // get the AffineTransform for the given skew.
+        float[] mtx = Matrix_Delegate.getSkew(kx, ky);
+        AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx);
+
+        // combine them so that the given matrix is applied after.
+        currentTx.preConcatenate(matrixTx);
+
+        // give it to the graphics2D as a new matrix replacing all previous transform
+        g.setTransform(currentTx);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean clipRect(Canvas thisCanvas, RectF rect) {
+        return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean clipRect(Canvas thisCanvas, Rect rect) {
+        return clipRect(thisCanvas, (float) rect.left, (float) rect.top,
+                (float) rect.right, (float) rect.bottom);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right,
+            float bottom) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            return false;
+        }
+
+        return canvasDelegate.clipRect(left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right,
+            int bottom) {
+
+        return clipRect(thisCanvas, (float) left, (float) top, (float) right, (float) bottom);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int save(Canvas thisCanvas) {
+        return save(thisCanvas, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int save(Canvas thisCanvas, int saveFlags) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            return 0;
+        }
+
+        return canvasDelegate.save(saveFlags);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void restore(Canvas thisCanvas) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        canvasDelegate.restore();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int getSaveCount(Canvas thisCanvas) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            return 0;
+        }
+
+        return canvasDelegate.getSnapshot().size();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        canvasDelegate.restoreTo(saveCount);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count,
+            Paint paint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPoint is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void drawPoint(Canvas thisCanvas, float x, float y, Paint paint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPoint is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void drawLines(Canvas thisCanvas,
+            final float[] pts, final int offset, final int count,
+            Paint paint) {
+        draw(thisCanvas.mNativeCanvas, paint.mNativePaint, false /*compositeOnly*/,
+                false /*forceSrcMode*/, new GcSnapshot.Drawable() {
+                    public void draw(Graphics2D graphics, Paint_Delegate paint) {
+                        for (int i = 0 ; i < count ; i += 4) {
+                            graphics.drawLine((int)pts[i + offset], (int)pts[i + offset + 1],
+                                    (int)pts[i + offset + 2], (int)pts[i + offset + 3]);
+                        }
+                    }
+                });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void freeCaches() {
+        // nothing to be done here.
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int initRaster(int nativeBitmapOrZero) {
+        if (nativeBitmapOrZero > 0) {
+            // get the Bitmap from the int
+            Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero);
+
+            // create a new Canvas_Delegate with the given bitmap and return its new native int.
+            Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate);
+
+            return sManager.addNewDelegate(newDelegate);
+        } else {
+            // create a new Canvas_Delegate and return its new native int.
+            Canvas_Delegate newDelegate = new Canvas_Delegate();
+
+            return sManager.addNewDelegate(newDelegate);
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setBitmap(int nativeCanvas, int bitmap) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        // get the delegate from the native int.
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        if (bitmapDelegate == null) {
+            return;
+        }
+
+        canvasDelegate.setBitmap(bitmapDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_saveLayer(int nativeCanvas, RectF bounds,
+                                               int paint, int layerFlags) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return 0;
+        }
+
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
+        if (paintDelegate == null) {
+            return 0;
+        }
+
+        return canvasDelegate.saveLayer(bounds, paintDelegate, layerFlags);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_saveLayer(int nativeCanvas, float l,
+                                               float t, float r, float b,
+                                               int paint, int layerFlags) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return 0;
+        }
+
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
+        if (paintDelegate == null) {
+            return 0;
+        }
+
+        return canvasDelegate.saveLayer(new RectF(l, t, r, b),
+                paintDelegate, layerFlags);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_saveLayerAlpha(int nativeCanvas,
+                                                    RectF bounds, int alpha,
+                                                    int layerFlags) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return 0;
+        }
+
+        return canvasDelegate.saveLayerAlpha(bounds, alpha, layerFlags);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_saveLayerAlpha(int nativeCanvas, float l,
+                                                    float t, float r, float b,
+                                                    int alpha, int layerFlags) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return 0;
+        }
+
+        return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags);
+    }
+
+
+    @LayoutlibDelegate
+    /*package*/ static void native_concat(int nCanvas, int nMatrix) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
+        if (matrixDelegate == null) {
+            return;
+        }
+
+        // get the current top graphics2D object.
+        GcSnapshot snapshot = canvasDelegate.getSnapshot();
+
+        // get its current matrix
+        AffineTransform currentTx = snapshot.getTransform();
+        // get the AffineTransform of the given matrix
+        AffineTransform matrixTx = matrixDelegate.getAffineTransform();
+
+        // combine them so that the given matrix is applied after.
+        currentTx.preConcatenate(matrixTx);
+
+        // give it to the graphics2D as a new matrix replacing all previous transform
+        snapshot.setTransform(currentTx);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setMatrix(int nCanvas, int nMatrix) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
+        if (matrixDelegate == null) {
+            return;
+        }
+
+        // get the current top graphics2D object.
+        GcSnapshot snapshot = canvasDelegate.getSnapshot();
+
+        // get the AffineTransform of the given matrix
+        AffineTransform matrixTx = matrixDelegate.getAffineTransform();
+
+        // give it to the graphics2D as a new matrix replacing all previous transform
+        snapshot.setTransform(matrixTx);
+
+        if (matrixDelegate.hasPerspective()) {
+            assert false;
+            Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_AFFINE,
+                    "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " +
+                    "supports affine transformations.", null, null /*data*/);
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_clipRect(int nCanvas,
+                                                  float left, float top,
+                                                  float right, float bottom,
+                                                  int regionOp) {
+
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        if (canvasDelegate == null) {
+            return false;
+        }
+
+        return canvasDelegate.clipRect(left, top, right, bottom, regionOp);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_clipPath(int nativeCanvas,
+                                                  int nativePath,
+                                                  int regionOp) {
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return true;
+        }
+
+        Path_Delegate pathDelegate = Path_Delegate.getDelegate(nativePath);
+        if (pathDelegate == null) {
+            return true;
+        }
+
+        return canvasDelegate.mSnapshot.clip(pathDelegate.getJavaShape(), regionOp);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_clipRegion(int nativeCanvas,
+                                                    int nativeRegion,
+                                                    int regionOp) {
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return true;
+        }
+
+        Region_Delegate region = Region_Delegate.getDelegate(nativeRegion);
+        if (region == null) {
+            return true;
+        }
+
+        return canvasDelegate.mSnapshot.clip(region.getJavaArea(), regionOp);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeSetDrawFilter(int nativeCanvas, int nativeFilter) {
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        canvasDelegate.mDrawFilter = DrawFilter_Delegate.getDelegate(nativeFilter);
+
+        if (canvasDelegate.mDrawFilter != null &&
+                canvasDelegate.mDrawFilter.isSupported() == false) {
+            Bridge.getLog().fidelityWarning(LayoutLog.TAG_DRAWFILTER,
+                    canvasDelegate.mDrawFilter.getSupportMessage(), null, null /*data*/);
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_getClipBounds(int nativeCanvas,
+                                                       Rect bounds) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return false;
+        }
+
+        Rectangle rect = canvasDelegate.getSnapshot().getClip().getBounds();
+        if (rect != null && rect.isEmpty() == false) {
+            bounds.left = rect.x;
+            bounds.top = rect.y;
+            bounds.right = rect.x + rect.width;
+            bounds.bottom = rect.y + rect.height;
+            return true;
+        }
+
+        return false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_getCTM(int canvas, int matrix) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix);
+        if (matrixDelegate == null) {
+            return;
+        }
+
+        AffineTransform transform = canvasDelegate.getSnapshot().getTransform();
+        matrixDelegate.set(Matrix_Delegate.makeValues(transform));
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_quickReject(int nativeCanvas,
+                                                     RectF rect,
+                                                     int native_edgeType) {
+        // FIXME properly implement quickReject
+        return false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_quickReject(int nativeCanvas,
+                                                     int path,
+                                                     int native_edgeType) {
+        // FIXME properly implement quickReject
+        return false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_quickReject(int nativeCanvas,
+                                                     float left, float top,
+                                                     float right, float bottom,
+                                                     int native_edgeType) {
+        // FIXME properly implement quickReject
+        return false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawRGB(int nativeCanvas, int r, int g, int b) {
+        native_drawColor(nativeCanvas, 0xFF000000 | r << 16 | (g&0xFF) << 8 | (b&0xFF),
+                PorterDuff.Mode.SRC_OVER.nativeInt);
+
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawARGB(int nativeCanvas, int a, int r, int g, int b) {
+        native_drawColor(nativeCanvas, a << 24 | (r&0xFF) << 16 | (g&0xFF) << 8 | (b&0xFF),
+                PorterDuff.Mode.SRC_OVER.nativeInt);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawColor(int nativeCanvas, int color) {
+        native_drawColor(nativeCanvas, color, PorterDuff.Mode.SRC_OVER.nativeInt);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawColor(int nativeCanvas, final int color, final int mode) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        final int w = canvasDelegate.mBitmap.getImage().getWidth();
+        final int h = canvasDelegate.mBitmap.getImage().getHeight();
+        draw(nativeCanvas, new GcSnapshot.Drawable() {
+
+            public void draw(Graphics2D graphics, Paint_Delegate paint) {
+                // reset its transform just in case
+                graphics.setTransform(new AffineTransform());
+
+                // set the color
+                graphics.setColor(new Color(color, true /*alpha*/));
+
+                Composite composite = PorterDuffXfermode_Delegate.getComposite(
+                        PorterDuffXfermode_Delegate.getPorterDuffMode(mode), 0xFF);
+                if (composite != null) {
+                    graphics.setComposite(composite);
+                }
+
+                graphics.fillRect(0, 0, w, h);
+            }
+        });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawPaint(int nativeCanvas, int paint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPaint is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawLine(int nativeCanvas,
+            final float startX, final float startY, final float stopX, final float stopY,
+            int paint) {
+
+        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                new GcSnapshot.Drawable() {
+                    public void draw(Graphics2D graphics, Paint_Delegate paint) {
+                        graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
+                    }
+        });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawRect(int nativeCanvas, RectF rect,
+                                               int paint) {
+        native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawRect(int nativeCanvas,
+            final float left, final float top, final float right, final float bottom, int paint) {
+
+        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                new GcSnapshot.Drawable() {
+                    public void draw(Graphics2D graphics, Paint_Delegate paint) {
+                        int style = paint.getStyle();
+
+                        // draw
+                        if (style == Paint.Style.FILL.nativeInt ||
+                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                            graphics.fillRect((int)left, (int)top,
+                                    (int)(right-left), (int)(bottom-top));
+                        }
+
+                        if (style == Paint.Style.STROKE.nativeInt ||
+                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                            graphics.drawRect((int)left, (int)top,
+                                    (int)(right-left), (int)(bottom-top));
+                        }
+                    }
+        });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawOval(int nativeCanvas, final RectF oval, int paint) {
+        if (oval.right > oval.left && oval.bottom > oval.top) {
+            draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                    new GcSnapshot.Drawable() {
+                        public void draw(Graphics2D graphics, Paint_Delegate paint) {
+                            int style = paint.getStyle();
+
+                            // draw
+                            if (style == Paint.Style.FILL.nativeInt ||
+                                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                                graphics.fillOval((int)oval.left, (int)oval.top,
+                                        (int)oval.width(), (int)oval.height());
+                            }
+
+                            if (style == Paint.Style.STROKE.nativeInt ||
+                                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                                graphics.drawOval((int)oval.left, (int)oval.top,
+                                        (int)oval.width(), (int)oval.height());
+                            }
+                        }
+            });
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawCircle(int nativeCanvas,
+            float cx, float cy, float radius, int paint) {
+        native_drawOval(nativeCanvas,
+                new RectF(cx - radius, cy - radius, radius*2, radius*2),
+                paint);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawArc(int nativeCanvas,
+            RectF oval, float startAngle, float sweep, boolean useCenter, int paint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawArc is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawRoundRect(int nativeCanvas,
+            final RectF rect, final float rx, final float ry, int paint) {
+
+        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                new GcSnapshot.Drawable() {
+                    public void draw(Graphics2D graphics, Paint_Delegate paint) {
+                        int style = paint.getStyle();
+
+                        // draw
+                        if (style == Paint.Style.FILL.nativeInt ||
+                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                            graphics.fillRoundRect(
+                                    (int)rect.left, (int)rect.top,
+                                    (int)rect.width(), (int)rect.height(),
+                                    (int)rx, (int)ry);
+                        }
+
+                        if (style == Paint.Style.STROKE.nativeInt ||
+                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                            graphics.drawRoundRect(
+                                    (int)rect.left, (int)rect.top,
+                                    (int)rect.width(), (int)rect.height(),
+                                    (int)rx, (int)ry);
+                        }
+                    }
+        });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawPath(int nativeCanvas, int path, int paint) {
+        final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                new GcSnapshot.Drawable() {
+                    public void draw(Graphics2D graphics, Paint_Delegate paint) {
+                        Shape shape = pathDelegate.getJavaShape();
+                        int style = paint.getStyle();
+
+                        if (style == Paint.Style.FILL.nativeInt ||
+                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                            graphics.fill(shape);
+                        }
+
+                        if (style == Paint.Style.STROKE.nativeInt ||
+                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                            graphics.draw(shape);
+                        }
+                    }
+        });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
+                                                 float left, float top,
+                                                 int nativePaintOrZero,
+                                                 int canvasDensity,
+                                                 int screenDensity,
+                                                 int bitmapDensity) {
+        // get the delegate from the native int.
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        if (bitmapDelegate == null) {
+            return;
+        }
+
+        BufferedImage image = bitmapDelegate.getImage();
+        float right = left + image.getWidth();
+        float bottom = top + image.getHeight();
+
+        drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
+                0, 0, image.getWidth(), image.getHeight(),
+                (int)left, (int)top, (int)right, (int)bottom);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
+                                                 Rect src, RectF dst,
+                                                 int nativePaintOrZero,
+                                                 int screenDensity,
+                                                 int bitmapDensity) {
+        // get the delegate from the native int.
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        if (bitmapDelegate == null) {
+            return;
+        }
+
+        BufferedImage image = bitmapDelegate.getImage();
+
+        if (src == null) {
+            drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
+                    0, 0, image.getWidth(), image.getHeight(),
+                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
+        } else {
+            drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
+                    src.left, src.top, src.width(), src.height(),
+                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawBitmap(int nativeCanvas, int bitmap,
+                                                 Rect src, Rect dst,
+                                                 int nativePaintOrZero,
+                                                 int screenDensity,
+                                                 int bitmapDensity) {
+        // get the delegate from the native int.
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        if (bitmapDelegate == null) {
+            return;
+        }
+
+        BufferedImage image = bitmapDelegate.getImage();
+
+        if (src == null) {
+            drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
+                    0, 0, image.getWidth(), image.getHeight(),
+                    dst.left, dst.top, dst.right, dst.bottom);
+        } else {
+            drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
+                    src.left, src.top, src.width(), src.height(),
+                    dst.left, dst.top, dst.right, dst.bottom);
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors,
+                                                int offset, int stride, final float x,
+                                                 final float y, int width, int height,
+                                                 boolean hasAlpha,
+                                                 int nativePaintOrZero) {
+
+        // create a temp BufferedImage containing the content.
+        final BufferedImage image = new BufferedImage(width, height,
+                hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
+        image.setRGB(0, 0, width, height, colors, offset, stride);
+
+        draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/,
+                new GcSnapshot.Drawable() {
+                    public void draw(Graphics2D graphics, Paint_Delegate paint) {
+                        if (paint != null && paint.isFilterBitmap()) {
+                            graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+                                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+                        }
+
+                        graphics.drawImage(image, (int) x, (int) y, null);
+                    }
+        });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap,
+                                                      int nMatrix, int nPaint) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        // get the delegate from the native int, which can be null
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
+
+        // get the delegate from the native int.
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nBitmap);
+        if (bitmapDelegate == null) {
+            return;
+        }
+
+        final BufferedImage image = getImageToDraw(bitmapDelegate, paintDelegate, sBoolOut);
+
+        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
+        if (matrixDelegate == null) {
+            return;
+        }
+
+        final AffineTransform mtx = matrixDelegate.getAffineTransform();
+
+        canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
+                public void draw(Graphics2D graphics, Paint_Delegate paint) {
+                    if (paint != null && paint.isFilterBitmap()) {
+                        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+                                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+                    }
+
+                    //FIXME add support for canvas, screen and bitmap densities.
+                    graphics.drawImage(image, mtx, null);
+                }
+        }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap,
+            int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors,
+            int colorOffset, int nPaint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawBitmapMesh is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeDrawVertices(int nCanvas, int mode, int n,
+            float[] verts, int vertOffset,
+            float[] texs, int texOffset,
+            int[] colors, int colorOffset,
+            short[] indices, int indexOffset,
+            int indexCount, int nPaint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawVertices is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawText(int nativeCanvas,
+            final char[] text, final int index, final int count,
+            final float startX, final float startY, int flags, int paint) {
+        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                new GcSnapshot.Drawable() {
+            public void draw(Graphics2D graphics, Paint_Delegate paint) {
+                // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
+                // Any change to this method should be reflected in Paint.measureText
+                // Paint.TextAlign indicates how the text is positioned relative to X.
+                // LEFT is the default and there's nothing to do.
+                float x = startX;
+                float y = startY;
+                if (paint.getTextAlign() != Paint.Align.LEFT.nativeInt) {
+                    float m = paint.measureText(text, index, count);
+                    if (paint.getTextAlign() == Paint.Align.CENTER.nativeInt) {
+                        x -= m / 2;
+                    } else if (paint.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
+                        x -= m;
+                    }
+                }
+
+                List<FontInfo> fonts = paint.getFonts();
+
+                if (fonts.size() > 0) {
+                    FontInfo mainFont = fonts.get(0);
+                    int i = index;
+                    int lastIndex = index + count;
+                    while (i < lastIndex) {
+                        // always start with the main font.
+                        int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
+                        if (upTo == -1) {
+                            // draw all the rest and exit.
+                            graphics.setFont(mainFont.mFont);
+                            graphics.drawChars(text, i, lastIndex - i, (int)x, (int)y);
+                            return;
+                        } else if (upTo > 0) {
+                            // draw what's possible
+                            graphics.setFont(mainFont.mFont);
+                            graphics.drawChars(text, i, upTo - i, (int)x, (int)y);
+
+                            // compute the width that was drawn to increase x
+                            x += mainFont.mMetrics.charsWidth(text, i, upTo - i);
+
+                            // move index to the first non displayed char.
+                            i = upTo;
+
+                            // don't call continue at this point. Since it is certain the main font
+                            // cannot display the font a index upTo (now ==i), we move on to the
+                            // fallback fonts directly.
+                        }
+
+                        // no char supported, attempt to read the next char(s) with the
+                        // fallback font. In this case we only test the first character
+                        // and then go back to test with the main font.
+                        // Special test for 2-char characters.
+                        boolean foundFont = false;
+                        for (int f = 1 ; f < fonts.size() ; f++) {
+                            FontInfo fontInfo = fonts.get(f);
+
+                            // need to check that the font can display the character. We test
+                            // differently if the char is a high surrogate.
+                            int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
+                            upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
+                            if (upTo == -1) {
+                                // draw that char
+                                graphics.setFont(fontInfo.mFont);
+                                graphics.drawChars(text, i, charCount, (int)x, (int)y);
+
+                                // update x
+                                x += fontInfo.mMetrics.charsWidth(text, i, charCount);
+
+                                // update the index in the text, and move on
+                                i += charCount;
+                                foundFont = true;
+                                break;
+
+                            }
+                        }
+
+                        // in case no font can display the char, display it with the main font.
+                        // (it'll put a square probably)
+                        if (foundFont == false) {
+                            int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
+
+                            graphics.setFont(mainFont.mFont);
+                            graphics.drawChars(text, i, charCount, (int)x, (int)y);
+
+                            // measure it to advance x
+                            x += mainFont.mMetrics.charsWidth(text, i, charCount);
+
+                            // and move to the next chars.
+                            i += charCount;
+                        }
+                    }
+                }
+            }
+        });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawText(int nativeCanvas, String text,
+                                               int start, int end, float x,
+                                               float y, int flags, int paint) {
+        int count = end - start;
+        char[] buffer = TemporaryBuffer.obtain(count);
+        TextUtils.getChars(text, start, end, buffer, 0);
+
+        native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawTextRun(int nativeCanvas, String text,
+            int start, int end, int contextStart, int contextEnd,
+            float x, float y, int flags, int paint) {
+        int count = end - start;
+        char[] buffer = TemporaryBuffer.obtain(count);
+        TextUtils.getChars(text, start, end, buffer, 0);
+
+        native_drawText(nativeCanvas, buffer, start, end, x, y, flags, paint);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text,
+            int start, int count, int contextStart, int contextCount,
+            float x, float y, int flags, int paint) {
+        native_drawText(nativeCanvas, text, start, count, x, y, flags, paint);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawPosText(int nativeCanvas,
+                                                  char[] text, int index,
+                                                  int count, float[] pos,
+                                                  int paint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPosText is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawPosText(int nativeCanvas,
+                                                  String text, float[] pos,
+                                                  int paint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPosText is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawTextOnPath(int nativeCanvas,
+                                                     char[] text, int index,
+                                                     int count, int path,
+                                                     float hOffset,
+                                                     float vOffset, int bidiFlags,
+                                                     int paint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawTextOnPath(int nativeCanvas,
+                                                     String text, int path,
+                                                     float hOffset,
+                                                     float vOffset,
+                                                     int flags, int paint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawPicture(int nativeCanvas,
+                                                  int nativePicture) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPicture is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void finalizer(int nativeCanvas) {
+        // get the delegate from the native int so that it can be disposed.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        canvasDelegate.dispose();
+
+        // remove it from the manager.
+        sManager.removeJavaReferenceFor(nativeCanvas);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    /**
+     * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
+     * <p>Note that the drawable may actually be executed several times if there are
+     * layers involved (see {@link #saveLayer(RectF, int, int)}.
+     */
+    private static void draw(int nCanvas, int nPaint, boolean compositeOnly, boolean forceSrcMode,
+            GcSnapshot.Drawable drawable) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        // get the paint which can be null if nPaint is 0;
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
+
+        canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly, forceSrcMode);
+    }
+
+    /**
+     * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
+     * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
+     * <p>Note that the drawable may actually be executed several times if there are
+     * layers involved (see {@link #saveLayer(RectF, int, int)}.
+     */
+    private static void draw(int nCanvas, GcSnapshot.Drawable drawable) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        canvasDelegate.mSnapshot.draw(drawable);
+    }
+
+    private Canvas_Delegate(Bitmap_Delegate bitmap) {
+        mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
+    }
+
+    private Canvas_Delegate() {
+        mSnapshot = GcSnapshot.createDefaultSnapshot(null /*image*/);
+    }
+
+    /**
+     * Disposes of the {@link Graphics2D} stack.
+     */
+    private void dispose() {
+        mSnapshot.dispose();
+    }
+
+    private int save(int saveFlags) {
+        // get the current save count
+        int count = mSnapshot.size();
+
+        mSnapshot = mSnapshot.save(saveFlags);
+
+        // return the old save count
+        return count;
+    }
+
+    private int saveLayerAlpha(RectF rect, int alpha, int saveFlags) {
+        Paint_Delegate paint = new Paint_Delegate();
+        paint.setAlpha(alpha);
+        return saveLayer(rect, paint, saveFlags);
+    }
+
+    private int saveLayer(RectF rect, Paint_Delegate paint, int saveFlags) {
+        // get the current save count
+        int count = mSnapshot.size();
+
+        mSnapshot = mSnapshot.saveLayer(rect, paint, saveFlags);
+
+        // return the old save count
+        return count;
+    }
+
+    /**
+     * Restores the {@link GcSnapshot} to <var>saveCount</var>
+     * @param saveCount the saveCount
+     */
+    private void restoreTo(int saveCount) {
+        mSnapshot = mSnapshot.restoreTo(saveCount);
+    }
+
+    /**
+     * Restores the {@link GcSnapshot} to <var>saveCount</var>
+     * @param saveCount the saveCount
+     */
+    private void restore() {
+        mSnapshot = mSnapshot.restore();
+    }
+
+    private boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
+        return mSnapshot.clipRect(left, top, right, bottom, regionOp);
+    }
+
+    private void setBitmap(Bitmap_Delegate bitmap) {
+        mBitmap = bitmap;
+        assert mSnapshot.size() == 1;
+        mSnapshot.setBitmap(mBitmap);
+    }
+
+    private static void drawBitmap(
+            int nativeCanvas,
+            Bitmap_Delegate bitmap,
+            int nativePaintOrZero,
+            final int sleft, final int stop, final int sright, final int sbottom,
+            final int dleft, final int dtop, final int dright, final int dbottom) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        // get the paint, which could be null if the int is 0
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
+
+        final BufferedImage image = getImageToDraw(bitmap, paintDelegate, sBoolOut);
+
+        draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0],
+                new GcSnapshot.Drawable() {
+                    public void draw(Graphics2D graphics, Paint_Delegate paint) {
+                        if (paint != null && paint.isFilterBitmap()) {
+                            graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+                                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+                        }
+
+                        //FIXME add support for canvas, screen and bitmap densities.
+                        graphics.drawImage(image, dleft, dtop, dright, dbottom,
+                                sleft, stop, sright, sbottom, null);
+                    }
+        });
+    }
+
+
+    /**
+     * Returns a BufferedImage ready for drawing, based on the bitmap and paint delegate.
+     * The image returns, through a 1-size boolean array, whether the drawing code should
+     * use a SRC composite no matter what the paint says.
+     *
+     * @param bitmap the bitmap
+     * @param paint the paint that will be used to draw
+     * @param forceSrcMode whether the composite will have to be SRC
+     * @return the image to draw
+     */
+    private static BufferedImage getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint,
+            boolean[] forceSrcMode) {
+        BufferedImage image = bitmap.getImage();
+        forceSrcMode[0] = false;
+
+        // if the bitmap config is alpha_8, then we erase all color value from it
+        // before drawing it.
+        if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) {
+            fixAlpha8Bitmap(image);
+        } else if (bitmap.hasAlpha() == false) {
+            // hasAlpha is merely a rendering hint. There can in fact be alpha values
+            // in the bitmap but it should be ignored at drawing time.
+            // There is two ways to do this:
+            // - override the composite to be SRC. This can only be used if the composite
+            //   was going to be SRC or SRC_OVER in the first place
+            // - Create a different bitmap to draw in which all the alpha channel values is set
+            //   to 0xFF.
+            if (paint != null) {
+                Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
+                if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) {
+                    PorterDuff.Mode mode =
+                        ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode();
+
+                    forceSrcMode[0] = mode == PorterDuff.Mode.SRC_OVER ||
+                            mode == PorterDuff.Mode.SRC;
+                }
+            }
+
+            // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB
+            if (forceSrcMode[0] == false) {
+                image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF);
+            }
+        }
+
+        return image;
+    }
+
+    private static void fixAlpha8Bitmap(final BufferedImage image) {
+        int w = image.getWidth();
+        int h = image.getHeight();
+        int[] argb = new int[w * h];
+        image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
+
+        final int length = argb.length;
+        for (int i = 0 ; i < length; i++) {
+            argb[i] &= 0xFF000000;
+        }
+        image.setRGB(0, 0, w, h, argb, 0, w);
+    }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java
new file mode 100644
index 0000000..e5a7ab6
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate implementing the native methods of android.graphics.ColorFilter
+ *
+ * Through the layoutlib_create tool, the original native methods of ColorFilter have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original ColorFilter class.
+ *
+ * This also serve as a base class for all ColorFilter delegate classes.
+ *
+ * @see DelegateManager
+ *
+ */
+public abstract class ColorFilter_Delegate {
+
+    // ---- delegate manager ----
+    protected static final DelegateManager<ColorFilter_Delegate> sManager =
+            new DelegateManager<ColorFilter_Delegate>(ColorFilter_Delegate.class);
+
+    // ---- delegate helper data ----
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    public static ColorFilter_Delegate getDelegate(int nativeShader) {
+        return sManager.getDelegate(nativeShader);
+    }
+
+    public abstract boolean isSupported();
+    public abstract String getSupportMessage();
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static void finalizer(int native_instance, int nativeColorFilter) {
+        sManager.removeJavaReferenceFor(native_instance);
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
new file mode 100644
index 0000000..2de344b
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate implementing the native methods of android.graphics.ColorMatrixColorFilter
+ *
+ * Through the layoutlib_create tool, the original native methods of ColorMatrixColorFilter have
+ * been replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original ColorMatrixColorFilter class.
+ *
+ * Because this extends {@link ColorFilter_Delegate}, there's no need to use a
+ * {@link DelegateManager}, as all the Shader classes will be added to the manager
+ * owned by {@link ColorFilter_Delegate}.
+ *
+ * @see ColorFilter_Delegate
+ *
+ */
+public class ColorMatrixColorFilter_Delegate extends ColorFilter_Delegate {
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public boolean isSupported() {
+        return false;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        return "ColorMatrix Color Filters are not supported.";
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeColorMatrixFilter(float[] array) {
+        ColorMatrixColorFilter_Delegate newDelegate = new ColorMatrixColorFilter_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nColorMatrixFilter(int nativeFilter, float[] array) {
+        // pass
+        return 0;
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java
new file mode 100644
index 0000000..7c04a87
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.awt.Stroke;
+
+/**
+ * Delegate implementing the native methods of android.graphics.ComposePathEffect
+ *
+ * Through the layoutlib_create tool, the original native methods of ComposePathEffect have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original ComposePathEffect class.
+ *
+ * Because this extends {@link PathEffect_Delegate}, there's no need to use a {@link DelegateManager},
+ * as all the Shader classes will be added to the manager owned by {@link PathEffect_Delegate}.
+ *
+ * @see PathEffect_Delegate
+ *
+ */
+public class ComposePathEffect_Delegate extends PathEffect_Delegate {
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public Stroke getStroke(Paint_Delegate paint) {
+        // FIXME
+        return null;
+    }
+
+    @Override
+    public boolean isSupported() {
+        return false;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        return "Compose Path Effects are not supported in Layout Preview mode.";
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreate(int outerpe, int innerpe) {
+        ComposePathEffect_Delegate newDelegate = new ComposePathEffect_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java
deleted file mode 100644
index 863d64a..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2008 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.Paint;
-
-/** A subclass of shader that returns the composition of two other shaders, combined by
-    an {@link android.graphics.Xfermode} subclass.
-*/
-public class ComposeShader extends Shader {
-    /** Create a new compose shader, given shaders A, B, and a combining mode.
-        When the mode is applied, it will be given the result from shader A as its
-        "dst", and the result of from shader B as its "src".
-        @param shaderA  The colors from this shader are seen as the "dst" by the mode
-        @param shaderB  The colors from this shader are seen as the "src" by the mode
-        @param mode     The mode that combines the colors from the two shaders. If mode
-                        is null, then SRC_OVER is assumed.
-    */
-    public ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode) {
-        // FIXME Implement shader
-    }
-
-    /** Create a new compose shader, given shaders A, B, and a combining PorterDuff mode.
-        When the mode is applied, it will be given the result from shader A as its
-        "dst", and the result of from shader B as its "src".
-        @param shaderA  The colors from this shader are seen as the "dst" by the mode
-        @param shaderB  The colors from this shader are seen as the "src" by the mode
-        @param mode     The PorterDuff mode that combines the colors from the two shaders.
-    */
-    public ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode) {
-        // FIXME Implement shader
-    }
-
-    @Override
-    Paint getJavaPaint() {
-        return null;
-    }
-}
-
diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
new file mode 100644
index 0000000..f6e1d00
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.awt.Paint;
+
+/**
+ * Delegate implementing the native methods of android.graphics.ComposeShader
+ *
+ * Through the layoutlib_create tool, the original native methods of ComposeShader have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original ComposeShader class.
+ *
+ * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
+ * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
+ *
+ * @see Shader_Delegate
+ *
+ */
+public class ComposeShader_Delegate extends Shader_Delegate {
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public Paint getJavaPaint() {
+        // FIXME
+        return null;
+    }
+
+    @Override
+    public boolean isSupported() {
+        return false;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        return "Compose Shaders are not supported in Layout Preview mode.";
+    }
+
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreate1(int native_shaderA, int native_shaderB,
+            int native_mode) {
+        // FIXME not supported yet.
+        ComposeShader_Delegate newDelegate = new ComposeShader_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreate2(int native_shaderA, int native_shaderB,
+            int porterDuffMode) {
+        // FIXME not supported yet.
+        ComposeShader_Delegate newDelegate = new ComposeShader_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativePostCreate1(int native_shader, int native_skiaShaderA,
+            int native_skiaShaderB, int native_mode) {
+        // pass, not needed.
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativePostCreate2(int native_shader, int native_skiaShaderA,
+            int native_skiaShaderB, int porterDuffMode) {
+        // pass, not needed.
+        return 0;
+    }
+
+    // ---- Private delegate/helper methods ----
+
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/CornerPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/CornerPathEffect_Delegate.java
new file mode 100644
index 0000000..b0f8168
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/CornerPathEffect_Delegate.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.awt.Stroke;
+
+/**
+ * Delegate implementing the native methods of android.graphics.CornerPathEffect
+ *
+ * Through the layoutlib_create tool, the original native methods of CornerPathEffect have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original CornerPathEffect class.
+ *
+ * Because this extends {@link PathEffect_Delegate}, there's no need to use a {@link DelegateManager},
+ * as all the Shader classes will be added to the manager owned by {@link PathEffect_Delegate}.
+ *
+ * @see PathEffect_Delegate
+ *
+ */
+public class CornerPathEffect_Delegate extends PathEffect_Delegate {
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public Stroke getStroke(Paint_Delegate paint) {
+        // FIXME
+        return null;
+    }
+
+    @Override
+    public boolean isSupported() {
+        return false;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        return "Corner Path Effects are not supported in Layout Preview mode.";
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreate(float radius) {
+        CornerPathEffect_Delegate newDelegate = new CornerPathEffect_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/DashPathEffect.java b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect.java
deleted file mode 100644
index 46d4c70..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/DashPathEffect.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-public class DashPathEffect extends PathEffect {
-
-    private final float[] mIntervals;
-    private final float mPhase;
-
-    /**
-     * The intervals array must contain an even number of entries (>=2), with
-     * the even indices specifying the "on" intervals, and the odd indices
-     * specifying the "off" intervals. phase is an offset into the intervals
-     * array (mod the sum of all of the intervals). The intervals array
-     * controls the length of the dashes. The paint's strokeWidth controls the
-     * thickness of the dashes.
-     * Note: this patheffect only affects drawing with the paint's style is set
-     * to STROKE or STROKE_AND_FILL. It is ignored if the drawing is done with
-     * style == FILL.
-     * @param intervals array of ON and OFF distances
-     * @param phase offset into the intervals array
-     */
-    public DashPathEffect(float intervals[], float phase) {
-        if (intervals.length < 2) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-
-        mIntervals = intervals;
-        mPhase = phase;
-    }
-
-    public float[] getIntervals() {
-        return mIntervals;
-    }
-
-    public float getPhase() {
-        return mPhase;
-    }
-}
-
diff --git a/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java
new file mode 100644
index 0000000..d97c2ec
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.awt.BasicStroke;
+import java.awt.Stroke;
+
+/**
+ * Delegate implementing the native methods of android.graphics.DashPathEffect
+ *
+ * Through the layoutlib_create tool, the original native methods of DashPathEffect have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original DashPathEffect class.
+ *
+ * Because this extends {@link PathEffect_Delegate}, there's no need to use a
+ * {@link DelegateManager}, as all the PathEffect classes will be added to the manager owned by
+ * {@link PathEffect_Delegate}.
+ *
+ * @see PathEffect_Delegate
+ *
+ */
+public final class DashPathEffect_Delegate extends PathEffect_Delegate {
+
+    // ---- delegate data ----
+
+    private final float[] mIntervals;
+    private final float mPhase;
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public Stroke getStroke(Paint_Delegate paint) {
+        return new BasicStroke(
+                paint.getStrokeWidth(),
+                paint.getJavaCap(),
+                paint.getJavaJoin(),
+                paint.getJavaStrokeMiter(),
+                mIntervals,
+                mPhase);
+    }
+
+    @Override
+    public boolean isSupported() {
+        return true;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        // no message since isSupported returns true;
+        return null;
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreate(float intervals[], float phase) {
+        DashPathEffect_Delegate newDelegate = new DashPathEffect_Delegate(intervals, phase);
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    private DashPathEffect_Delegate(float intervals[], float phase) {
+        mIntervals = new float[intervals.length];
+        System.arraycopy(intervals, 0, mIntervals, 0, intervals.length);
+        mPhase = phase;
+    }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/DiscretePathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/DiscretePathEffect_Delegate.java
new file mode 100644
index 0000000..ec4a810
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/DiscretePathEffect_Delegate.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.awt.Stroke;
+
+/**
+ * Delegate implementing the native methods of android.graphics.DiscretePathEffect
+ *
+ * Through the layoutlib_create tool, the original native methods of DiscretePathEffect have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original DiscretePathEffect class.
+ *
+ * Because this extends {@link PathEffect_Delegate}, there's no need to use a {@link DelegateManager},
+ * as all the Shader classes will be added to the manager owned by {@link PathEffect_Delegate}.
+ *
+ * @see PathEffect_Delegate
+ *
+ */
+public class DiscretePathEffect_Delegate extends PathEffect_Delegate {
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public Stroke getStroke(Paint_Delegate paint) {
+        // FIXME
+        return null;
+    }
+
+    @Override
+    public boolean isSupported() {
+        return false;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        return "Discrete Path Effects are not supported in Layout Preview mode.";
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreate(float length, float deviation) {
+        DiscretePathEffect_Delegate newDelegate = new DiscretePathEffect_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/DrawFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/DrawFilter_Delegate.java
new file mode 100644
index 0000000..870c46b
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/DrawFilter_Delegate.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate implementing the native methods of android.graphics.DrawFilter
+ *
+ * Through the layoutlib_create tool, the original native methods of DrawFilter have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original DrawFilter class.
+ *
+ * This also serve as a base class for all DrawFilter delegate classes.
+ *
+ * @see DelegateManager
+ *
+ */
+public abstract class DrawFilter_Delegate {
+
+    // ---- delegate manager ----
+    protected static final DelegateManager<DrawFilter_Delegate> sManager =
+            new DelegateManager<DrawFilter_Delegate>(DrawFilter_Delegate.class);
+
+    // ---- delegate helper data ----
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    public static DrawFilter_Delegate getDelegate(int nativeDrawFilter) {
+        return sManager.getDelegate(nativeDrawFilter);
+    }
+
+    public abstract boolean isSupported();
+    public abstract String getSupportMessage();
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeDestructor(int nativeDrawFilter) {
+        sManager.removeJavaReferenceFor(nativeDrawFilter);
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java
new file mode 100644
index 0000000..ebc1c1d
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate implementing the native methods of android.graphics.EmbossMaskFilter
+ *
+ * Through the layoutlib_create tool, the original native methods of EmbossMaskFilter have
+ * been replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original EmbossMaskFilter class.
+ *
+ * Because this extends {@link MaskFilter_Delegate}, there's no need to use a
+ * {@link DelegateManager}, as all the Shader classes will be added to the manager
+ * owned by {@link MaskFilter_Delegate}.
+ *
+ * @see MaskFilter_Delegate
+ *
+ */
+public class EmbossMaskFilter_Delegate extends MaskFilter_Delegate {
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public boolean isSupported() {
+        return false;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        return "Emboss Mask Filters are not supported.";
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeConstructor(float[] direction, float ambient,
+            float specular, float blurRadius) {
+        EmbossMaskFilter_Delegate newDelegate = new EmbossMaskFilter_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/GradientShader.java b/tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java
similarity index 84%
rename from tools/layoutlib/bridge/src/android/graphics/GradientShader.java
rename to tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java
index 8c5a2a4..38c092d 100644
--- a/tools/layoutlib/bridge/src/android/graphics/GradientShader.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java
@@ -16,23 +16,28 @@
 
 package android.graphics;
 
+import android.graphics.Shader.TileMode;
 
 /**
- * Base class for Gradient shader. This is not a standard android class and is just used
- * as a base class for the re-implemented gradient classes.
- *
- * It also provides a base class to handle common code between the different shaders'
- * implementations of {@link java.awt.Paint}.
- *
- * @see LinearGradient
- * @see RadialGradient
- * @see SweepGradient
+ * Base class for true Gradient shader delegate.
  */
-public abstract class GradientShader extends Shader {
+public abstract class Gradient_Delegate extends Shader_Delegate {
 
     protected final int[] mColors;
     protected final float[] mPositions;
 
+    @Override
+    public boolean isSupported() {
+        // all gradient shaders are supported.
+        return true;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        // all gradient shaders are supported, no need for a gradient support
+        return null;
+    }
+
     /**
      * Creates the base shader and do some basic test on the parameters.
      *
@@ -41,7 +46,7 @@
      *            corresponding color in the colors array. If this is null, the
      *            the colors are distributed evenly along the gradient line.
      */
-    protected GradientShader(int colors[], float positions[]) {
+    protected Gradient_Delegate(int colors[], float positions[]) {
         if (colors.length < 2) {
             throw new IllegalArgumentException("needs >= 2 number of colors");
         }
@@ -90,7 +95,7 @@
          * Pre-computes the colors for the gradient. This must be called once before any call
          * to {@link #getGradientColor(float)}
          */
-        protected synchronized void precomputeGradientColors() {
+        protected void precomputeGradientColors() {
             if (mGradient == null) {
                 // actually create an array with an extra size, so that we can really go
                 // from 0 to SIZE (100%), or currentPos in the loop below will never equal 1.0
@@ -126,20 +131,23 @@
                             pos = 0.f;
                             break;
                         case REPEAT:
-                            // remove the integer part to stay in the [0,1] range
-                            // careful: this is a negative value, so use ceil instead of floor
-                            pos = pos - (float)Math.ceil(pos);
+                            // remove the integer part to stay in the [0,1] range.
+                            // we also need to invert the value from [-1,0] to [0, 1]
+                            pos = pos - (float)Math.floor(pos);
                             break;
                         case MIRROR:
+                            // this is the same as the positive side, just make the value positive
+                            // first.
+                            pos = Math.abs(pos);
+
                             // get the integer and the decimal part
-                            // careful: this is a negative value, so use ceil instead of floor
-                            int intPart = (int)Math.ceil(pos);
+                            int intPart = (int)Math.floor(pos);
                             pos = pos - intPart;
-                            // 0  -> -1 : mirrored order
-                            // -1 -> -2: normal order
+                            // 0 -> 1 : normal order
+                            // 1 -> 2: mirrored
                             // etc..
-                            // this means if the intpart is even we invert
-                            if ((intPart % 2) == 0) {
+                            // this means if the intpart is odd we invert
+                            if ((intPart % 2) == 1) {
                                 pos = 1.f - pos;
                             }
                             break;
@@ -199,7 +207,5 @@
         private int computeChannel(int c1, int c2, float percent) {
             return c1 + (int)((percent * (c2-c1)) + .5);
         }
-
-
     }
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/LayerRasterizer_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LayerRasterizer_Delegate.java
new file mode 100644
index 0000000..51e0576
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/LayerRasterizer_Delegate.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate implementing the native methods of android.graphics.LayerRasterizer
+ *
+ * Through the layoutlib_create tool, the original native methods of LayerRasterizer have
+ * been replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original LayerRasterizer class.
+ *
+ * Because this extends {@link Rasterizer_Delegate}, there's no need to use a
+ * {@link DelegateManager}, as all the Shader classes will be added to the manager
+ * owned by {@link Rasterizer_Delegate}.
+ *
+ * @see Rasterizer_Delegate
+ *
+ */
+public class LayerRasterizer_Delegate extends Rasterizer_Delegate {
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public boolean isSupported() {
+        return false;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        return "Layer Rasterizers are not supported.";
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeConstructor() {
+        LayerRasterizer_Delegate newDelegate = new LayerRasterizer_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeAddLayer(int native_layer, int native_paint, float dx, float dy) {
+
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java
new file mode 100644
index 0000000..0ee883d
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate implementing the native methods of android.graphics.LightingColorFilter
+ *
+ * Through the layoutlib_create tool, the original native methods of LightingColorFilter have
+ * been replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original LightingColorFilter class.
+ *
+ * Because this extends {@link ColorFilter_Delegate}, there's no need to use a
+ * {@link DelegateManager}, as all the Shader classes will be added to the manager
+ * owned by {@link ColorFilter_Delegate}.
+ *
+ * @see ColorFilter_Delegate
+ *
+ */
+public class LightingColorFilter_Delegate extends ColorFilter_Delegate {
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public boolean isSupported() {
+        return false;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        return "Lighting Color Filters are not supported.";
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int native_CreateLightingFilter(int mul, int add) {
+        LightingColorFilter_Delegate newDelegate = new LightingColorFilter_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nCreateLightingFilter(int nativeFilter, int mul, int add) {
+        // pass
+        return 0;
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
deleted file mode 100644
index 10c4a5e..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2008 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;
-
-public class LinearGradient extends GradientShader {
-
-    private java.awt.Paint mJavaPaint;
-
-    /**
-     * Create a shader that draws a linear gradient along a line.
-     *
-     * @param x0 The x-coordinate for the start of the gradient line
-     * @param y0 The y-coordinate for the start of the gradient line
-     * @param x1 The x-coordinate for the end of the gradient line
-     * @param y1 The y-coordinate for the end of the gradient line
-     * @param colors The colors to be distributed along the gradient line
-     * @param positions May be null. The relative positions [0..1] of each
-     *            corresponding color in the colors array. If this is null, the
-     *            the colors are distributed evenly along the gradient line.
-     * @param tile The Shader tiling mode
-     */
-    public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],
-            TileMode tile) {
-        super(colors, positions);
-        mJavaPaint = new LinearGradientPaint(x0, y0, x1, y1, mColors, mPositions, tile);
-    }
-
-    /**
-     * Create a shader that draws a linear gradient along a line.
-     *
-     * @param x0 The x-coordinate for the start of the gradient line
-     * @param y0 The y-coordinate for the start of the gradient line
-     * @param x1 The x-coordinate for the end of the gradient line
-     * @param y1 The y-coordinate for the end of the gradient line
-     * @param color0 The color at the start of the gradient line.
-     * @param color1 The color at the end of the gradient line.
-     * @param tile The Shader tiling mode
-     */
-    public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,
-            TileMode tile) {
-        this(x0, y0, x1, y1, new int[] { color0, color1}, null /*positions*/, tile);
-    }
-
-    // ---------- Custom Methods
-
-    @Override
-    java.awt.Paint getJavaPaint() {
-        return mJavaPaint;
-    }
-
-    /**
-     * Linear Gradient (Java) Paint able to handle more than 2 points, as
-     * {@link java.awt.GradientPaint} only supports 2 points and does not support Android's tile
-     * modes.
-     */
-    private static class LinearGradientPaint extends GradientPaint {
-
-        private final float mX0;
-        private final float mY0;
-        private final float mDx;
-        private final float mDy;
-        private final float mDSize2;
-
-        public LinearGradientPaint(float x0, float y0, float x1, float y1, int colors[],
-                float positions[], TileMode tile) {
-            super(colors, positions, tile);
-                mX0 = x0;
-                mY0 = y0;
-                mDx = x1 - x0;
-                mDy = y1 - y0;
-                mDSize2 = mDx * mDx + mDy * mDy;
-        }
-
-        public java.awt.PaintContext createContext(
-                java.awt.image.ColorModel      colorModel,
-                java.awt.Rectangle             deviceBounds,
-                java.awt.geom.Rectangle2D      userBounds,
-                java.awt.geom.AffineTransform  xform,
-                java.awt.RenderingHints        hints) {
-            precomputeGradientColors();
-            return new LinearGradientPaintContext(colorModel);
-        }
-
-        private class LinearGradientPaintContext implements java.awt.PaintContext {
-
-            private final java.awt.image.ColorModel mColorModel;
-
-            public LinearGradientPaintContext(java.awt.image.ColorModel colorModel) {
-                mColorModel = colorModel;
-                // FIXME: so far all this is always the same rect gotten in getRaster with an indentity matrix?
-            }
-
-            public void dispose() {
-            }
-
-            public java.awt.image.ColorModel getColorModel() {
-                return mColorModel;
-            }
-
-            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
-                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
-                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
-
-                int[] data = new int[w*h];
-
-                if (mDx == 0) { // vertical gradient
-                    // compute first column and copy to all other columns
-                    int index = 0;
-                    for (int iy = 0 ; iy < h ; iy++) {
-                        int color = getColor(iy + y, mY0, mDy);
-                        for (int ix = 0 ; ix < w ; ix++) {
-                            data[index++] = color;
-                        }
-                    }
-                } else if (mDy == 0) { // horizontal
-                    // compute first line in a tmp array and copy to all lines
-                    int[] line = new int[w];
-                    for (int ix = 0 ; ix < w ; ix++) {
-                        line[ix] = getColor(ix + x, mX0, mDx);
-                    }
-
-                    for (int iy = 0 ; iy < h ; iy++) {
-                        System.arraycopy(line, 0, data, iy*w, line.length);
-                    }
-                } else {
-                    int index = 0;
-                    for (int iy = 0 ; iy < h ; iy++) {
-                        for (int ix = 0 ; ix < w ; ix++) {
-                            data[index++] = getColor(ix + x, iy + y);
-                        }
-                    }
-                }
-
-                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
-
-                return image.getRaster();
-            }
-        }
-
-        /** Returns a color for the easy vertical/horizontal mode */
-        private int getColor(float absPos, float refPos, float refSize) {
-            float pos = (absPos - refPos) / refSize;
-
-            return getGradientColor(pos);
-        }
-
-        /**
-         * Returns a color for an arbitrary point.
-         */
-        private int getColor(float x, float y) {
-            // find the x position on the gradient vector.
-            float _x = (mDx*mDy*(y-mY0) + mDy*mDy*mX0 + mDx*mDx*x) / mDSize2;
-            // from it get the position relative to the vector
-            float pos = (float) ((_x - mX0) / mDx);
-
-            return getGradientColor(pos);
-        }
-    }
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
new file mode 100644
index 0000000..a2ba758
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2010 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 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.graphics.Shader.TileMode;
+
+/**
+ * Delegate implementing the native methods of android.graphics.LinearGradient
+ *
+ * Through the layoutlib_create tool, the original native methods of LinearGradient have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original LinearGradient class.
+ *
+ * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
+ * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
+ *
+ * @see Shader_Delegate
+ *
+ */
+public final class LinearGradient_Delegate extends Gradient_Delegate {
+
+    // ---- delegate data ----
+    private java.awt.Paint mJavaPaint;
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public java.awt.Paint getJavaPaint() {
+        return mJavaPaint;
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreate1(LinearGradient thisGradient,
+            float x0, float y0, float x1, float y1,
+            int colors[], float positions[], int tileMode) {
+        LinearGradient_Delegate newDelegate = new LinearGradient_Delegate(x0, y0, x1, y1,
+                colors, positions, Shader_Delegate.getTileMode(tileMode));
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreate2(LinearGradient thisGradient,
+            float x0, float y0, float x1, float y1,
+            int color0, int color1, int tileMode) {
+        return nativeCreate1(thisGradient,
+                x0, y0, x1, y1, new int[] { color0, color1}, null /*positions*/,
+                tileMode);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativePostCreate1(LinearGradient thisGradient,
+            int native_shader, float x0, float y0, float x1, float y1,
+            int colors[], float positions[], int tileMode) {
+        // nothing to be done here.
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativePostCreate2(LinearGradient thisGradient,
+            int native_shader, float x0, float y0, float x1, float y1,
+            int color0, int color1, int tileMode) {
+        // nothing to be done here.
+        return 0;
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    /**
+     * Create a shader that draws a linear gradient along a line.
+     *
+     * @param x0 The x-coordinate for the start of the gradient line
+     * @param y0 The y-coordinate for the start of the gradient line
+     * @param x1 The x-coordinate for the end of the gradient line
+     * @param y1 The y-coordinate for the end of the gradient line
+     * @param colors The colors to be distributed along the gradient line
+     * @param positions May be null. The relative positions [0..1] of each
+     *            corresponding color in the colors array. If this is null, the
+     *            the colors are distributed evenly along the gradient line.
+     * @param tile The Shader tiling mode
+     */
+    private LinearGradient_Delegate(float x0, float y0, float x1, float y1,
+            int colors[], float positions[], TileMode tile) {
+        super(colors, positions);
+        mJavaPaint = new LinearGradientPaint(x0, y0, x1, y1, mColors, mPositions, tile);
+    }
+
+    // ---- Custom Java Paint ----
+    /**
+     * Linear Gradient (Java) Paint able to handle more than 2 points, as
+     * {@link java.awt.GradientPaint} only supports 2 points and does not support Android's tile
+     * modes.
+     */
+    private class LinearGradientPaint extends GradientPaint {
+
+        private final float mX0;
+        private final float mY0;
+        private final float mDx;
+        private final float mDy;
+        private final float mDSize2;
+
+        public LinearGradientPaint(float x0, float y0, float x1, float y1, int colors[],
+                float positions[], TileMode tile) {
+            super(colors, positions, tile);
+            mX0 = x0;
+            mY0 = y0;
+            mDx = x1 - x0;
+            mDy = y1 - y0;
+            mDSize2 = mDx * mDx + mDy * mDy;
+        }
+
+        public java.awt.PaintContext createContext(
+                java.awt.image.ColorModel      colorModel,
+                java.awt.Rectangle             deviceBounds,
+                java.awt.geom.Rectangle2D      userBounds,
+                java.awt.geom.AffineTransform  xform,
+                java.awt.RenderingHints        hints) {
+            precomputeGradientColors();
+
+            java.awt.geom.AffineTransform canvasMatrix;
+            try {
+                canvasMatrix = xform.createInverse();
+            } catch (java.awt.geom.NoninvertibleTransformException e) {
+                Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
+                        "Unable to inverse matrix in LinearGradient", e, null /*data*/);
+                canvasMatrix = new java.awt.geom.AffineTransform();
+            }
+
+            java.awt.geom.AffineTransform localMatrix = getLocalMatrix();
+            try {
+                localMatrix = localMatrix.createInverse();
+            } catch (java.awt.geom.NoninvertibleTransformException e) {
+                Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
+                        "Unable to inverse matrix in LinearGradient", e, null /*data*/);
+                localMatrix = new java.awt.geom.AffineTransform();
+            }
+
+            return new LinearGradientPaintContext(canvasMatrix, localMatrix, colorModel);
+        }
+
+        private class LinearGradientPaintContext implements java.awt.PaintContext {
+
+            private final java.awt.geom.AffineTransform mCanvasMatrix;
+            private final java.awt.geom.AffineTransform mLocalMatrix;
+            private final java.awt.image.ColorModel mColorModel;
+
+            private LinearGradientPaintContext(
+                    java.awt.geom.AffineTransform canvasMatrix,
+                    java.awt.geom.AffineTransform localMatrix,
+                    java.awt.image.ColorModel colorModel) {
+                mCanvasMatrix = canvasMatrix;
+                mLocalMatrix = localMatrix;
+                mColorModel = colorModel;
+            }
+
+            public void dispose() {
+            }
+
+            public java.awt.image.ColorModel getColorModel() {
+                return mColorModel;
+            }
+
+            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
+                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
+                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
+
+                int[] data = new int[w*h];
+
+                int index = 0;
+                float[] pt1 = new float[2];
+                float[] pt2 = new float[2];
+                for (int iy = 0 ; iy < h ; iy++) {
+                    for (int ix = 0 ; ix < w ; ix++) {
+                        // handle the canvas transform
+                        pt1[0] = x + ix;
+                        pt1[1] = y + iy;
+                        mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
+
+                        // handle the local matrix.
+                        pt1[0] = pt2[0];
+                        pt1[1] = pt2[1];
+                        mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
+
+                        data[index++] = getColor(pt2[0], pt2[1]);
+                    }
+                }
+
+                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
+
+                return image.getRaster();
+            }
+        }
+
+        /**
+         * Returns a color for an arbitrary point.
+         */
+        private int getColor(float x, float y) {
+            float pos;
+            if (mDx == 0) {
+                pos = (y - mY0) / mDy;
+            } else if (mDy == 0) {
+                pos = (x - mX0) / mDx;
+            } else {
+                // find the x position on the gradient vector.
+                float _x = (mDx*mDy*(y-mY0) + mDy*mDy*mX0 + mDx*mDx*x) / mDSize2;
+                // from it get the position relative to the vector
+                pos = (_x - mX0) / mDx;
+            }
+
+            return getGradientColor(pos);
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/MaskFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/MaskFilter_Delegate.java
new file mode 100644
index 0000000..c2f27e4
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/MaskFilter_Delegate.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate implementing the native methods of android.graphics.MaskFilter
+ *
+ * Through the layoutlib_create tool, the original native methods of MaskFilter have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original MaskFilter class.
+ *
+ * This also serve as a base class for all MaskFilter delegate classes.
+ *
+ * @see DelegateManager
+ *
+ */
+public abstract class MaskFilter_Delegate {
+
+    // ---- delegate manager ----
+    protected static final DelegateManager<MaskFilter_Delegate> sManager =
+            new DelegateManager<MaskFilter_Delegate>(MaskFilter_Delegate.class);
+
+    // ---- delegate helper data ----
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    public static MaskFilter_Delegate getDelegate(int nativeShader) {
+        return sManager.getDelegate(nativeShader);
+    }
+
+    public abstract boolean isSupported();
+    public abstract String getSupportMessage();
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeDestructor(int native_filter) {
+        sManager.removeJavaReferenceFor(native_filter);
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix.java b/tools/layoutlib/bridge/src/android/graphics/Matrix.java
deleted file mode 100644
index 9e30671..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix.java
+++ /dev/null
@@ -1,1032 +0,0 @@
-/*
- * Copyright (C) 2008 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.NoninvertibleTransformException;
-
-
-/**
- * A matrix implementation overridden by the LayoutLib bridge.
- */
-public class Matrix extends _Original_Matrix {
-
-    float mValues[] = new float[9];
-
-    /**
-     * Create an identity matrix
-     */
-    public Matrix() {
-        reset();
-    }
-
-    /**
-     * Create a matrix that is a (deep) copy of src
-     * @param src The matrix to copy into this matrix
-     */
-    public Matrix(Matrix src) {
-        set(src);
-    }
-
-    /**
-     * Creates a Matrix object from the float array. The array becomes the internal storage
-     * of the object.
-     * @param data
-     */
-    private Matrix(float[] data) {
-        assert data.length != 9;
-        mValues = data;
-    }
-
-    //---------- Custom Methods
-
-    /**
-     * Adds the given transformation to the current Matrix
-     * <p/>This in effect does this = this*matrix
-     * @param matrix
-     */
-    private void addTransform(float[] matrix) {
-        float[] tmp = new float[9];
-
-        // first row
-        tmp[0] = matrix[0] * mValues[0] + matrix[1] * mValues[3] + matrix[2] * mValues[6];
-        tmp[1] = matrix[0] * mValues[1] + matrix[1] * mValues[4] + matrix[2] * mValues[7];
-        tmp[2] = matrix[0] * mValues[2] + matrix[1] * mValues[5] + matrix[2] * mValues[8];
-
-        // 2nd row
-        tmp[3] = matrix[3] * mValues[0] + matrix[4] * mValues[3] + matrix[5] * mValues[6];
-        tmp[4] = matrix[3] * mValues[1] + matrix[4] * mValues[4] + matrix[5] * mValues[7];
-        tmp[5] = matrix[3] * mValues[2] + matrix[4] * mValues[5] + matrix[5] * mValues[8];
-
-        // 3rd row
-        tmp[6] = matrix[6] * mValues[0] + matrix[7] * mValues[3] + matrix[8] * mValues[6];
-        tmp[7] = matrix[6] * mValues[1] + matrix[7] * mValues[4] + matrix[8] * mValues[7];
-        tmp[8] = matrix[6] * mValues[2] + matrix[7] * mValues[5] + matrix[8] * mValues[8];
-
-        // copy the result over to mValues
-        mValues = tmp;
-    }
-
-    public AffineTransform getTransform() {
-        // the AffineTransform constructor takes the value in a different order
-        // for a matrix [ 0 1 2 ]
-        //              [ 3 4 5 ]
-        // the order is 0, 3, 1, 4, 2, 5...
-        return new AffineTransform(mValues[0], mValues[3], mValues[1],
-                mValues[4], mValues[2], mValues[5]);
-    }
-
-    public boolean hasPerspective() {
-        return (mValues[6] != 0 || mValues[7] != 0 || mValues[8] != 1);
-    }
-
-    //----------
-
-    /**
-     * Returns true if the matrix is identity.
-     * This maybe faster than testing if (getType() == 0)
-     */
-    @Override
-    public boolean isIdentity() {
-        for (int i = 0, k = 0; i < 3; i++) {
-            for (int j = 0; j < 3; j++, k++) {
-                if (mValues[k] != ((i==j) ? 1 : 0)) {
-                    return false;
-                }
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns true if will map a rectangle to another rectangle. This can be
-     * true if the matrix is identity, scale-only, or rotates a multiple of 90
-     * degrees.
-     */
-    @Override
-    public boolean rectStaysRect() {
-        return (computeTypeMask() & kRectStaysRect_Mask) != 0;
-    }
-
-    /**
-     * (deep) copy the src matrix into this matrix. If src is null, reset this
-     * matrix to the identity matrix.
-     */
-    public void set(Matrix src) {
-        if (src == null) {
-            reset();
-        } else {
-            System.arraycopy(src.mValues, 0, mValues, 0, mValues.length);
-        }
-    }
-
-    @Override
-    public void set(_Original_Matrix src) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /** Returns true if obj is a Matrix and its values equal our values.
-    */
-    @Override
-    public boolean equals(Object obj) {
-        if (obj != null && obj instanceof Matrix) {
-            Matrix matrix = (Matrix)obj;
-            for (int i = 0 ; i < 9 ; i++) {
-                if (mValues[i] != matrix.mValues[i]) {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-
-        return false;
-    }
-
-    /** Set the matrix to identity */
-    @Override
-    public void reset() {
-        for (int i = 0, k = 0; i < 3; i++) {
-            for (int j = 0; j < 3; j++, k++) {
-                mValues[k] = ((i==j) ? 1 : 0);
-            }
-        }
-    }
-
-    /** Set the matrix to translate by (dx, dy). */
-    @Override
-    public void setTranslate(float dx, float dy) {
-        mValues[0] = 1;
-        mValues[1] = 0;
-        mValues[2] = dx;
-        mValues[3] = 0;
-        mValues[4] = 1;
-        mValues[5] = dy;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-    }
-
-    /**
-     * Set the matrix to scale by sx and sy, with a pivot point at (px, py).
-     * The pivot point is the coordinate that should remain unchanged by the
-     * specified transformation.
-     */
-    @Override
-    public void setScale(float sx, float sy, float px, float py) {
-        // TODO: do it in one pass
-
-        // translate so that the pivot is in 0,0
-        mValues[0] = 1;
-        mValues[1] = 0;
-        mValues[2] = -px;
-        mValues[3] = 0;
-        mValues[4] = 1;
-        mValues[5] = -py;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-
-        // scale
-        addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-    }
-
-    /** Set the matrix to scale by sx and sy. */
-    @Override
-    public void setScale(float sx, float sy) {
-        mValues[0] = sx;
-        mValues[1] = 0;
-        mValues[2] = 0;
-        mValues[3] = 0;
-        mValues[4] = sy;
-        mValues[5] = 0;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-    }
-
-    /**
-     * Set the matrix to rotate by the specified number of degrees, with a pivot
-     * point at (px, py). The pivot point is the coordinate that should remain
-     * unchanged by the specified transformation.
-     */
-    @Override
-    public void setRotate(float degrees, float px, float py) {
-        // TODO: do it in one pass
-
-        // translate so that the pivot is in 0,0
-        mValues[0] = 1;
-        mValues[1] = 0;
-        mValues[2] = -px;
-        mValues[3] = 0;
-        mValues[4] = 1;
-        mValues[5] = -py;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-
-        // scale
-        double rad = Math.toRadians(degrees);
-        float cos = (float)Math.cos(rad);
-        float sin = (float)Math.sin(rad);
-        addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-    }
-
-    /**
-     * Set the matrix to rotate about (0,0) by the specified number of degrees.
-     */
-    @Override
-    public void setRotate(float degrees) {
-        double rad = Math.toRadians(degrees);
-        float cos = (float)Math.cos(rad);
-        float sin = (float)Math.sin(rad);
-
-        mValues[0] = cos;
-        mValues[1] = -sin;
-        mValues[2] = 0;
-        mValues[3] = sin;
-        mValues[4] = cos;
-        mValues[5] = 0;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-    }
-
-    /**
-     * Set the matrix to rotate by the specified sine and cosine values, with a
-     * pivot point at (px, py). The pivot point is the coordinate that should
-     * remain unchanged by the specified transformation.
-     */
-    @Override
-    public void setSinCos(float sinValue, float cosValue, float px, float py) {
-        // TODO: do it in one pass
-
-        // translate so that the pivot is in 0,0
-        mValues[0] = 1;
-        mValues[1] = 0;
-        mValues[2] = -px;
-        mValues[3] = 0;
-        mValues[4] = 1;
-        mValues[5] = -py;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-
-        // scale
-        addTransform(new float[] { cosValue, -sinValue, 0, sinValue, cosValue, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-    }
-
-    /** Set the matrix to rotate by the specified sine and cosine values. */
-    @Override
-    public void setSinCos(float sinValue, float cosValue) {
-        mValues[0] = cosValue;
-        mValues[1] = -sinValue;
-        mValues[2] = 0;
-        mValues[3] = sinValue;
-        mValues[4] = cosValue;
-        mValues[5] = 0;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-    }
-
-    /**
-     * Set the matrix to skew by sx and sy, with a pivot point at (px, py).
-     * The pivot point is the coordinate that should remain unchanged by the
-     * specified transformation.
-     */
-    @Override
-    public void setSkew(float kx, float ky, float px, float py) {
-        // TODO: do it in one pass
-
-        // translate so that the pivot is in 0,0
-        mValues[0] = 1;
-        mValues[1] = 0;
-        mValues[2] = -px;
-        mValues[3] = 0;
-        mValues[4] = 1;
-        mValues[5] = -py;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-
-        // scale
-        addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-    }
-
-    /** Set the matrix to skew by sx and sy. */
-    @Override
-    public void setSkew(float kx, float ky) {
-        mValues[0] = 1;
-        mValues[1] = kx;
-        mValues[2] = -0;
-        mValues[3] = ky;
-        mValues[4] = 1;
-        mValues[5] = 0;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-    }
-
-    /**
-     * Set the matrix to the concatenation of the two specified matrices,
-     * returning true if the the result can be represented. Either of the two
-     * matrices may also be the target matrix. this = a * b
-     */
-    public boolean setConcat(Matrix a, Matrix b) {
-        if (a == this) {
-            preConcat(b);
-        } else if (b == this) {
-            postConcat(b);
-        } else {
-            Matrix tmp = new Matrix(b);
-            tmp.addTransform(a.mValues);
-            set(tmp);
-        }
-
-        return true;
-    }
-
-    @Override
-    public boolean setConcat(_Original_Matrix a, _Original_Matrix b) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /**
-     * Preconcats the matrix with the specified translation.
-     * M' = M * T(dx, dy)
-     */
-    @Override
-    public boolean preTranslate(float dx, float dy) {
-        // create a matrix that will be multiply by this
-        Matrix m = new Matrix(new float[] { 1, 0, dx, 0, 1, dy, 0, 0, 1 });
-        m.addTransform(this.mValues);
-
-        System.arraycopy(m.mValues, 0, mValues, 0, 9);
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified scale.
-     * M' = M * S(sx, sy, px, py)
-     */
-    @Override
-    public boolean preScale(float sx, float sy, float px, float py) {
-        Matrix m = new Matrix();
-        m.setScale(sx, sy, px, py);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified scale.
-     * M' = M * S(sx, sy)
-     */
-    @Override
-    public boolean preScale(float sx, float sy) {
-        Matrix m = new Matrix();
-        m.setScale(sx, sy);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified rotation.
-     * M' = M * R(degrees, px, py)
-     */
-    @Override
-    public boolean preRotate(float degrees, float px, float py) {
-        Matrix m = new Matrix();
-        m.setRotate(degrees, px, py);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified rotation.
-     * M' = M * R(degrees)
-     */
-    @Override
-    public boolean preRotate(float degrees) {
-        Matrix m = new Matrix();
-        m.setRotate(degrees);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified skew.
-     * M' = M * K(kx, ky, px, py)
-     */
-    @Override
-    public boolean preSkew(float kx, float ky, float px, float py) {
-        Matrix m = new Matrix();
-        m.setSkew(kx, ky, px, py);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified skew.
-     * M' = M * K(kx, ky)
-     */
-    @Override
-    public boolean preSkew(float kx, float ky) {
-        Matrix m = new Matrix();
-        m.setSkew(kx, ky);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified matrix.
-     * M' = M * other
-     */
-    public boolean preConcat(Matrix other) {
-        Matrix m = new Matrix(other);
-        other.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    @Override
-    public boolean preConcat(_Original_Matrix other) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /**
-     * Postconcats the matrix with the specified translation.
-     * M' = T(dx, dy) * M
-     */
-    @Override
-    public boolean postTranslate(float dx, float dy) {
-        addTransform(new float[] { 1, 0, dx, 0, 1, dy, 0, 0, 1 });
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified scale.
-     * M' = S(sx, sy, px, py) * M
-     */
-    @Override
-    public boolean postScale(float sx, float sy, float px, float py) {
-        // TODO: do it in one pass
-        // translate so that the pivot is in 0,0
-        addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
-        // scale
-        addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified scale.
-     * M' = S(sx, sy) * M
-     */
-    @Override
-    public boolean postScale(float sx, float sy) {
-        addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified rotation.
-     * M' = R(degrees, px, py) * M
-     */
-    @Override
-    public boolean postRotate(float degrees, float px, float py) {
-        // TODO: do it in one pass
-        // translate so that the pivot is in 0,0
-        addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
-        // scale
-        double rad = Math.toRadians(degrees);
-        float cos = (float)Math.cos(rad);
-        float sin = (float)Math.sin(rad);
-        addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified rotation.
-     * M' = R(degrees) * M
-     */
-    @Override
-    public boolean postRotate(float degrees) {
-        double rad = Math.toRadians(degrees);
-        float cos = (float)Math.cos(rad);
-        float sin = (float)Math.sin(rad);
-        addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 });
-
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified skew.
-     * M' = K(kx, ky, px, py) * M
-     */
-    @Override
-    public boolean postSkew(float kx, float ky, float px, float py) {
-        // TODO: do it in one pass
-        // translate so that the pivot is in 0,0
-        addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
-        // scale
-        addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified skew.
-     * M' = K(kx, ky) * M
-     */
-    @Override
-    public boolean postSkew(float kx, float ky) {
-        addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
-
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified matrix.
-     * M' = other * M
-     */
-    public boolean postConcat(Matrix other) {
-        addTransform(other.mValues);
-
-        return true;
-    }
-
-    @Override
-    public boolean postConcat(_Original_Matrix other) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /** Controlls how the src rect should align into the dst rect for
-        setRectToRect().
-    */
-    public enum ScaleToFit {
-        /**
-         * Scale in X and Y independently, so that src matches dst exactly.
-         * This may change the aspect ratio of the src.
-         */
-        FILL    (0),
-        /**
-         * Compute a scale that will maintain the original src aspect ratio,
-         * but will also ensure that src fits entirely inside dst. At least one
-         * axis (X or Y) will fit exactly. START aligns the result to the
-         * left and top edges of dst.
-         */
-        START   (1),
-        /**
-         * Compute a scale that will maintain the original src aspect ratio,
-         * but will also ensure that src fits entirely inside dst. At least one
-         * axis (X or Y) will fit exactly. The result is centered inside dst.
-         */
-        CENTER  (2),
-        /**
-         * Compute a scale that will maintain the original src aspect ratio,
-         * but will also ensure that src fits entirely inside dst. At least one
-         * axis (X or Y) will fit exactly. END aligns the result to the
-         * right and bottom edges of dst.
-         */
-        END     (3);
-
-        // the native values must match those in SkMatrix.h
-        ScaleToFit(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-    }
-
-    /**
-     * Set the matrix to the scale and translate values that map the source
-     * rectangle to the destination rectangle, returning true if the result
-     * can be represented.
-     *
-     * @param src the source rectangle to map from.
-     * @param dst the destination rectangle to map to.
-     * @param stf the ScaleToFit option
-     * @return true if the matrix can be represented by the rectangle mapping.
-     */
-    public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) {
-        if (dst == null || src == null) {
-            throw new NullPointerException();
-        }
-
-        if (src.isEmpty()) {
-            reset();
-            return false;
-        }
-
-        if (dst.isEmpty()) {
-            mValues[0] = mValues[1] = mValues[2] = mValues[3] = mValues[4] = mValues[5]
-               = mValues[6] = mValues[7] = 0;
-            mValues[8] = 1;
-        } else {
-            float    tx, sx = dst.width() / src.width();
-            float    ty, sy = dst.height() / src.height();
-            boolean  xLarger = false;
-
-            if (stf != ScaleToFit.FILL) {
-                if (sx > sy) {
-                    xLarger = true;
-                    sx = sy;
-                } else {
-                    sy = sx;
-                }
-            }
-
-            tx = dst.left - src.left * sx;
-            ty = dst.top - src.top * sy;
-            if (stf == ScaleToFit.CENTER || stf == ScaleToFit.END) {
-                float diff;
-
-                if (xLarger) {
-                    diff = dst.width() - src.width() * sy;
-                } else {
-                    diff = dst.height() - src.height() * sy;
-                }
-
-                if (stf == ScaleToFit.CENTER) {
-                    diff = diff / 2;
-                }
-
-                if (xLarger) {
-                    tx += diff;
-                } else {
-                    ty += diff;
-                }
-            }
-
-            mValues[0] = sx;
-            mValues[4] = sy;
-            mValues[2] = tx;
-            mValues[5] = ty;
-            mValues[1]  = mValues[3] = mValues[6] = mValues[7] = 0;
-
-        }
-        // shared cleanup
-        mValues[8] = 1;
-        return true;
-    }
-
-    @Override
-    public boolean setRectToRect(RectF src, RectF dst, _Original_Matrix.ScaleToFit stf) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /**
-     * Set the matrix such that the specified src points would map to the
-     * specified dst points. The "points" are represented as an array of floats,
-     * order [x0, y0, x1, y1, ...], where each "point" is 2 float values.
-     *
-     * @param src   The array of src [x,y] pairs (points)
-     * @param srcIndex Index of the first pair of src values
-     * @param dst   The array of dst [x,y] pairs (points)
-     * @param dstIndex Index of the first pair of dst values
-     * @param pointCount The number of pairs/points to be used. Must be [0..4]
-     * @return true if the matrix was set to the specified transformation
-     */
-    @Override
-    public boolean setPolyToPoly(float[] src, int srcIndex,
-                                 float[] dst, int dstIndex,
-                                 int pointCount) {
-        if (pointCount > 4) {
-            throw new IllegalArgumentException();
-        }
-        checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
-        throw new UnsupportedOperationException("STUB NEEDED");
-    }
-
-    /**
-     * If this matrix can be inverted, return true and if inverse is not null,
-     * set inverse to be the inverse of this matrix. If this matrix cannot be
-     * inverted, ignore inverse and return false.
-     */
-    public boolean invert(Matrix inverse) {
-        if (inverse == null) {
-            return false;
-        }
-
-        try {
-            AffineTransform affineTransform = getTransform();
-            AffineTransform inverseTransform = affineTransform.createInverse();
-            inverse.mValues[0] = (float)inverseTransform.getScaleX();
-            inverse.mValues[1] = (float)inverseTransform.getShearX();
-            inverse.mValues[2] = (float)inverseTransform.getTranslateX();
-            inverse.mValues[3] = (float)inverseTransform.getScaleX();
-            inverse.mValues[4] = (float)inverseTransform.getShearY();
-            inverse.mValues[5] = (float)inverseTransform.getTranslateY();
-
-            return true;
-        } catch (NoninvertibleTransformException e) {
-            return false;
-        }
-    }
-
-    @Override
-    public boolean invert(_Original_Matrix inverse) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /**
-    * Apply this matrix to the array of 2D points specified by src, and write
-     * the transformed points into the array of points specified by dst. The
-     * two arrays represent their "points" as pairs of floats [x, y].
-     *
-     * @param dst   The array of dst points (x,y pairs)
-     * @param dstIndex The index of the first [x,y] pair of dst floats
-     * @param src   The array of src points (x,y pairs)
-     * @param srcIndex The index of the first [x,y] pair of src floats
-     * @param pointCount The number of points (x,y pairs) to transform
-     */
-    @Override
-    public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
-                          int pointCount) {
-        checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
-
-        for (int i = 0 ; i < pointCount ; i++) {
-            // just in case we are doing in place, we better put this in temp vars
-            float x = mValues[0] * src[i + srcIndex] +
-                      mValues[1] * src[i + srcIndex + 1] +
-                      mValues[2];
-            float y = mValues[3] * src[i + srcIndex] +
-                      mValues[4] * src[i + srcIndex + 1] +
-                      mValues[5];
-
-            dst[i + dstIndex]     = x;
-            dst[i + dstIndex + 1] = y;
-        }
-    }
-
-    /**
-    * Apply this matrix to the array of 2D vectors specified by src, and write
-     * the transformed vectors into the array of vectors specified by dst. The
-     * two arrays represent their "vectors" as pairs of floats [x, y].
-     *
-     * @param dst   The array of dst vectors (x,y pairs)
-     * @param dstIndex The index of the first [x,y] pair of dst floats
-     * @param src   The array of src vectors (x,y pairs)
-     * @param srcIndex The index of the first [x,y] pair of src floats
-     * @param vectorCount The number of vectors (x,y pairs) to transform
-     */
-    @Override
-    public void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex,
-                          int vectorCount) {
-        checkPointArrays(src, srcIndex, dst, dstIndex, vectorCount);
-        throw new UnsupportedOperationException("STUB NEEDED");
-    }
-
-    /**
-     * Apply this matrix to the array of 2D points specified by src, and write
-     * the transformed points into the array of points specified by dst. The
-     * two arrays represent their "points" as pairs of floats [x, y].
-     *
-     * @param dst   The array of dst points (x,y pairs)
-     * @param src   The array of src points (x,y pairs)
-     */
-    @Override
-    public void mapPoints(float[] dst, float[] src) {
-        if (dst.length != src.length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        mapPoints(dst, 0, src, 0, dst.length >> 1);
-    }
-
-    /**
-     * Apply this matrix to the array of 2D vectors specified by src, and write
-     * the transformed vectors into the array of vectors specified by dst. The
-     * two arrays represent their "vectors" as pairs of floats [x, y].
-     *
-     * @param dst   The array of dst vectors (x,y pairs)
-     * @param src   The array of src vectors (x,y pairs)
-     */
-    @Override
-    public void mapVectors(float[] dst, float[] src) {
-        if (dst.length != src.length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        mapVectors(dst, 0, src, 0, dst.length >> 1);
-    }
-
-    /**
-     * Apply this matrix to the array of 2D points, and write the transformed
-     * points back into the array
-     *
-     * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
-     */
-    @Override
-    public void mapPoints(float[] pts) {
-        mapPoints(pts, 0, pts, 0, pts.length >> 1);
-    }
-
-    /**
-     * Apply this matrix to the array of 2D vectors, and write the transformed
-     * vectors back into the array.
-     * @param vecs The array [x0, y0, x1, y1, ...] of vectors to transform.
-     */
-    @Override
-    public void mapVectors(float[] vecs) {
-        mapVectors(vecs, 0, vecs, 0, vecs.length >> 1);
-    }
-
-    /**
-     * Apply this matrix to the src rectangle, and write the transformed
-     * rectangle into dst. This is accomplished by transforming the 4 corners of
-     * src, and then setting dst to the bounds of those points.
-     *
-     * @param dst Where the transformed rectangle is written.
-     * @param src The original rectangle to be transformed.
-     * @return the result of calling rectStaysRect()
-     */
-    @Override
-    public boolean mapRect(RectF dst, RectF src) {
-        if (dst == null || src == null) {
-            throw new NullPointerException();
-        }
-
-        // array with 4 corners
-        float[] corners = new float[] {
-                src.left, src.top,
-                src.right, src.top,
-                src.right, src.bottom,
-                src.left, src.bottom,
-        };
-
-        // apply the transform to them.
-        mapPoints(corners);
-
-        // now put the result in the rect. We take the min/max of Xs and min/max of Ys
-        dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
-        dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
-
-        dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
-        dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
-
-        return rectStaysRect();
-    }
-
-    /**
-     * Apply this matrix to the rectangle, and write the transformed rectangle
-     * back into it. This is accomplished by transforming the 4 corners of rect,
-     * and then setting it to the bounds of those points
-     *
-     * @param rect The rectangle to transform.
-     * @return the result of calling rectStaysRect()
-     */
-    @Override
-    public boolean mapRect(RectF rect) {
-        return mapRect(rect, rect);
-    }
-
-    /**
-     * Return the mean radius of a circle after it has been mapped by
-     * this matrix. NOTE: in perspective this value assumes the circle
-     * has its center at the origin.
-     */
-    @Override
-    public float mapRadius(float radius) {
-        throw new UnsupportedOperationException("STUB NEEDED");
-    }
-
-    /** Copy 9 values from the matrix into the array.
-    */
-    @Override
-    public void getValues(float[] values) {
-        if (values.length < 9) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        System.arraycopy(mValues, 0, values, 0, mValues.length);
-    }
-
-    /** Copy 9 values from the array into the matrix.
-        Depending on the implementation of Matrix, these may be
-        transformed into 16.16 integers in the Matrix, such that
-        a subsequent call to getValues() will not yield exactly
-        the same values.
-    */
-    @Override
-    public void setValues(float[] values) {
-        if (values.length < 9) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        System.arraycopy(values, 0, mValues, 0, mValues.length);
-    }
-
-    @SuppressWarnings("unused")
-    private final static int kIdentity_Mask      = 0;
-    private final static int kTranslate_Mask     = 0x01;  //!< set if the matrix has translation
-    private final static int kScale_Mask         = 0x02;  //!< set if the matrix has X or Y scale
-    private final static int kAffine_Mask        = 0x04;  //!< set if the matrix skews or rotates
-    private final static int kPerspective_Mask   = 0x08;  //!< set if the matrix is in perspective
-    private final static int kRectStaysRect_Mask = 0x10;
-    @SuppressWarnings("unused")
-    private final static int kUnknown_Mask       = 0x80;
-
-    @SuppressWarnings("unused")
-    private final static int kAllMasks           = kTranslate_Mask |
-                                                     kScale_Mask |
-                                                     kAffine_Mask |
-                                                     kPerspective_Mask |
-                                                     kRectStaysRect_Mask;
-
-    // these guys align with the masks, so we can compute a mask from a variable 0/1
-    @SuppressWarnings("unused")
-    private final static int kTranslate_Shift = 0;
-    @SuppressWarnings("unused")
-    private final static int kScale_Shift = 1;
-    @SuppressWarnings("unused")
-    private final static int kAffine_Shift = 2;
-    @SuppressWarnings("unused")
-    private final static int kPerspective_Shift = 3;
-    private final static int kRectStaysRect_Shift = 4;
-
-    private int computeTypeMask() {
-        int mask = 0;
-
-        if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) {
-            mask |= kPerspective_Mask;
-        }
-
-        if (mValues[2] != 0. || mValues[5] != 0.) {
-            mask |= kTranslate_Mask;
-        }
-
-        float m00 = mValues[0];
-        float m01 = mValues[1];
-        float m10 = mValues[3];
-        float m11 = mValues[4];
-
-        if (m01 != 0. || m10 != 0.) {
-            mask |= kAffine_Mask;
-        }
-
-        if (m00 != 1. || m11 != 1.) {
-            mask |= kScale_Mask;
-        }
-
-        if ((mask & kPerspective_Mask) == 0) {
-            // map non-zero to 1
-            int im00 = m00 != 0 ? 1 : 0;
-            int im01 = m01 != 0 ? 1 : 0;
-            int im10 = m10 != 0 ? 1 : 0;
-            int im11 = m11 != 0 ? 1 : 0;
-
-            // record if the (p)rimary and (s)econdary diagonals are all 0 or
-            // all non-zero (answer is 0 or 1)
-            int dp0 = (im00 | im11) ^ 1;  // true if both are 0
-            int dp1 = im00 & im11;        // true if both are 1
-            int ds0 = (im01 | im10) ^ 1;  // true if both are 0
-            int ds1 = im01 & im10;        // true if both are 1
-
-            // return 1 if primary is 1 and secondary is 0 or
-            // primary is 0 and secondary is 1
-            mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
-        }
-
-        return mask;
-    }
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
new file mode 100644
index 0000000..451edd2
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
@@ -0,0 +1,1129 @@
+/*
+ * Copyright (C) 2010 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 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.graphics.Matrix.ScaleToFit;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Matrix
+ *
+ * Through the layoutlib_create tool, the original native methods of Matrix have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Matrix class.
+ *
+ * @see DelegateManager
+ *
+ */
+public final class Matrix_Delegate {
+
+    private final static int MATRIX_SIZE = 9;
+
+    // ---- delegate manager ----
+    private static final DelegateManager<Matrix_Delegate> sManager =
+            new DelegateManager<Matrix_Delegate>(Matrix_Delegate.class);
+
+    // ---- delegate data ----
+    private float mValues[] = new float[MATRIX_SIZE];
+
+    // ---- Public Helper methods ----
+
+    public static Matrix_Delegate getDelegate(int native_instance) {
+        return sManager.getDelegate(native_instance);
+    }
+
+    /**
+     * Returns an {@link AffineTransform} matching the given Matrix.
+     */
+    public static AffineTransform getAffineTransform(Matrix m) {
+        Matrix_Delegate delegate = sManager.getDelegate(m.native_instance);
+        if (delegate == null) {
+            return null;
+        }
+
+        return delegate.getAffineTransform();
+    }
+
+    public static boolean hasPerspective(Matrix m) {
+        Matrix_Delegate delegate = sManager.getDelegate(m.native_instance);
+        if (delegate == null) {
+            return false;
+        }
+
+        return delegate.hasPerspective();
+    }
+
+    /**
+     * Sets the content of the matrix with the content of another matrix.
+     */
+    public void set(Matrix_Delegate matrix) {
+        System.arraycopy(matrix.mValues, 0, mValues, 0, MATRIX_SIZE);
+    }
+
+    /**
+     * Sets the content of the matrix with the content of another matrix represented as an array
+     * of values.
+     */
+    public void set(float[] values) {
+        System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE);
+    }
+
+    /**
+     * Resets the matrix to be the identity matrix.
+     */
+    public void reset() {
+        reset(mValues);
+    }
+
+    /**
+     * Returns whether or not the matrix is identity.
+     */
+    public boolean isIdentity() {
+        for (int i = 0, k = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++, k++) {
+                if (mValues[k] != ((i==j) ? 1 : 0)) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    public static float[] makeValues(AffineTransform matrix) {
+        float[] values = new float[MATRIX_SIZE];
+        values[0] = (float) matrix.getScaleX();
+        values[1] = (float) matrix.getShearX();
+        values[2] = (float) matrix.getTranslateX();
+        values[3] = (float) matrix.getShearY();
+        values[4] = (float) matrix.getScaleY();
+        values[5] = (float) matrix.getTranslateY();
+        values[6] = 0.f;
+        values[7] = 0.f;
+        values[8] = 1.f;
+
+        return values;
+    }
+
+    public static Matrix_Delegate make(AffineTransform matrix) {
+        return new Matrix_Delegate(makeValues(matrix));
+    }
+
+    public boolean mapRect(RectF dst, RectF src) {
+        // array with 4 corners
+        float[] corners = new float[] {
+                src.left, src.top,
+                src.right, src.top,
+                src.right, src.bottom,
+                src.left, src.bottom,
+        };
+
+        // apply the transform to them.
+        mapPoints(corners);
+
+        // now put the result in the rect. We take the min/max of Xs and min/max of Ys
+        dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
+        dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
+
+        dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
+        dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
+
+
+        return (computeTypeMask() & kRectStaysRect_Mask) != 0;
+    }
+
+
+    /**
+     * Returns an {@link AffineTransform} matching the matrix.
+     */
+    public AffineTransform getAffineTransform() {
+        return getAffineTransform(mValues);
+    }
+
+    public boolean hasPerspective() {
+        return (mValues[6] != 0 || mValues[7] != 0 || mValues[8] != 1);
+    }
+
+
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int native_create(int native_src_or_zero) {
+        // create the delegate
+        Matrix_Delegate newDelegate = new Matrix_Delegate();
+
+        // copy from values if needed.
+        if (native_src_or_zero > 0) {
+            Matrix_Delegate oldDelegate = sManager.getDelegate(native_src_or_zero);
+            if (oldDelegate != null) {
+                System.arraycopy(
+                        oldDelegate.mValues, 0,
+                        newDelegate.mValues, 0,
+                        MATRIX_SIZE);
+            }
+        }
+
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_isIdentity(int native_object) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        return d.isIdentity();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_rectStaysRect(int native_object) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return true;
+        }
+
+        return (d.computeTypeMask() & kRectStaysRect_Mask) != 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_reset(int native_object) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return;
+        }
+
+        reset(d.mValues);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_set(int native_object, int other) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return;
+        }
+
+        Matrix_Delegate src = sManager.getDelegate(other);
+        if (src == null) {
+            return;
+        }
+
+        System.arraycopy(src.mValues, 0, d.mValues, 0, MATRIX_SIZE);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setTranslate(int native_object, float dx, float dy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return;
+        }
+
+        setTranslate(d.mValues, dx, dy);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setScale(int native_object, float sx, float sy,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return;
+        }
+
+        d.mValues = getScale(sx, sy, px, py);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setScale(int native_object, float sx, float sy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return;
+        }
+
+        d.mValues[0] = sx;
+        d.mValues[1] = 0;
+        d.mValues[2] = 0;
+        d.mValues[3] = 0;
+        d.mValues[4] = sy;
+        d.mValues[5] = 0;
+        d.mValues[6] = 0;
+        d.mValues[7] = 0;
+        d.mValues[8] = 1;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setRotate(int native_object, float degrees, float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return;
+        }
+
+        d.mValues = getRotate(degrees, px, py);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setRotate(int native_object, float degrees) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return;
+        }
+
+        setRotate(d.mValues, degrees);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setSinCos(int native_object, float sinValue, float cosValue,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return;
+        }
+
+        // TODO: do it in one pass
+
+        // translate so that the pivot is in 0,0
+        setTranslate(d.mValues, -px, -py);
+
+        // scale
+        d.postTransform(getRotate(sinValue, cosValue));
+        // translate back the pivot
+        d.postTransform(getTranslate(px, py));
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setSinCos(int native_object, float sinValue, float cosValue) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return;
+        }
+
+        setRotate(d.mValues, sinValue, cosValue);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setSkew(int native_object, float kx, float ky,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return;
+        }
+
+        d.mValues = getSkew(kx, ky, px, py);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setSkew(int native_object, float kx, float ky) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return;
+        }
+
+        d.mValues[0] = 1;
+        d.mValues[1] = kx;
+        d.mValues[2] = -0;
+        d.mValues[3] = ky;
+        d.mValues[4] = 1;
+        d.mValues[5] = 0;
+        d.mValues[6] = 0;
+        d.mValues[7] = 0;
+        d.mValues[8] = 1;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_setConcat(int native_object, int a, int b) {
+        if (a == native_object) {
+            return native_preConcat(native_object, b);
+        } else if (b == native_object) {
+            return native_postConcat(native_object, a);
+        }
+
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        Matrix_Delegate a_mtx = sManager.getDelegate(a);
+        if (a_mtx == null) {
+            return false;
+        }
+
+        Matrix_Delegate b_mtx = sManager.getDelegate(b);
+        if (b_mtx == null) {
+            return false;
+        }
+
+        multiply(d.mValues, a_mtx.mValues, b_mtx.mValues);
+
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_preTranslate(int native_object, float dx, float dy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        d.preTransform(getTranslate(dx, dy));
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_preScale(int native_object, float sx, float sy,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        d.preTransform(getScale(sx, sy, px, py));
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_preScale(int native_object, float sx, float sy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        d.preTransform(getScale(sx, sy));
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_preRotate(int native_object, float degrees,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        d.preTransform(getRotate(degrees, px, py));
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_preRotate(int native_object, float degrees) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        double rad = Math.toRadians(degrees);
+        float sin = (float)Math.sin(rad);
+        float cos = (float)Math.cos(rad);
+
+        d.preTransform(getRotate(sin, cos));
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_preSkew(int native_object, float kx, float ky,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        d.preTransform(getSkew(kx, ky, px, py));
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_preSkew(int native_object, float kx, float ky) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        d.preTransform(getSkew(kx, ky));
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_preConcat(int native_object, int other_matrix) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        Matrix_Delegate other = sManager.getDelegate(other_matrix);
+        if (d == null) {
+            return false;
+        }
+
+        d.preTransform(other.mValues);
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_postTranslate(int native_object, float dx, float dy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        d.postTransform(getTranslate(dx, dy));
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_postScale(int native_object, float sx, float sy,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        d.postTransform(getScale(sx, sy, px, py));
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_postScale(int native_object, float sx, float sy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        d.postTransform(getScale(sx, sy));
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_postRotate(int native_object, float degrees,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        d.postTransform(getRotate(degrees, px, py));
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_postRotate(int native_object, float degrees) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        d.postTransform(getRotate(degrees));
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_postSkew(int native_object, float kx, float ky,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        d.postTransform(getSkew(kx, ky, px, py));
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_postSkew(int native_object, float kx, float ky) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        d.postTransform(getSkew(kx, ky));
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_postConcat(int native_object, int other_matrix) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        Matrix_Delegate other = sManager.getDelegate(other_matrix);
+        if (d == null) {
+            return false;
+        }
+
+        d.postTransform(other.mValues);
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_setRectToRect(int native_object, RectF src,
+            RectF dst, int stf) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        if (src.isEmpty()) {
+            reset(d.mValues);
+            return false;
+        }
+
+        if (dst.isEmpty()) {
+            d.mValues[0] = d.mValues[1] = d.mValues[2] = d.mValues[3] = d.mValues[4] = d.mValues[5]
+               = d.mValues[6] = d.mValues[7] = 0;
+            d.mValues[8] = 1;
+        } else {
+            float    tx, sx = dst.width() / src.width();
+            float    ty, sy = dst.height() / src.height();
+            boolean  xLarger = false;
+
+            if (stf != ScaleToFit.FILL.nativeInt) {
+                if (sx > sy) {
+                    xLarger = true;
+                    sx = sy;
+                } else {
+                    sy = sx;
+                }
+            }
+
+            tx = dst.left - src.left * sx;
+            ty = dst.top - src.top * sy;
+            if (stf == ScaleToFit.CENTER.nativeInt || stf == ScaleToFit.END.nativeInt) {
+                float diff;
+
+                if (xLarger) {
+                    diff = dst.width() - src.width() * sy;
+                } else {
+                    diff = dst.height() - src.height() * sy;
+                }
+
+                if (stf == ScaleToFit.CENTER.nativeInt) {
+                    diff = diff / 2;
+                }
+
+                if (xLarger) {
+                    tx += diff;
+                } else {
+                    ty += diff;
+                }
+            }
+
+            d.mValues[0] = sx;
+            d.mValues[4] = sy;
+            d.mValues[2] = tx;
+            d.mValues[5] = ty;
+            d.mValues[1]  = d.mValues[3] = d.mValues[6] = d.mValues[7] = 0;
+
+        }
+        // shared cleanup
+        d.mValues[8] = 1;
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_setPolyToPoly(int native_object, float[] src, int srcIndex,
+            float[] dst, int dstIndex, int pointCount) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Matrix.setPolyToPoly is not supported.",
+                null, null /*data*/);
+        return false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_invert(int native_object, int inverse) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        Matrix_Delegate inv_mtx = sManager.getDelegate(inverse);
+        if (inv_mtx == null) {
+            return false;
+        }
+
+        try {
+            AffineTransform affineTransform = d.getAffineTransform();
+            AffineTransform inverseTransform = affineTransform.createInverse();
+            inv_mtx.mValues[0] = (float)inverseTransform.getScaleX();
+            inv_mtx.mValues[1] = (float)inverseTransform.getShearX();
+            inv_mtx.mValues[2] = (float)inverseTransform.getTranslateX();
+            inv_mtx.mValues[3] = (float)inverseTransform.getScaleX();
+            inv_mtx.mValues[4] = (float)inverseTransform.getShearY();
+            inv_mtx.mValues[5] = (float)inverseTransform.getTranslateY();
+
+            return true;
+        } catch (NoninvertibleTransformException e) {
+            return false;
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_mapPoints(int native_object, float[] dst, int dstIndex,
+            float[] src, int srcIndex, int ptCount, boolean isPts) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return;
+        }
+
+        if (isPts) {
+            d.mapPoints(dst, dstIndex, src, srcIndex, ptCount);
+        } else {
+            d.mapVectors(dst, dstIndex, src, srcIndex, ptCount);
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_mapRect(int native_object, RectF dst, RectF src) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return false;
+        }
+
+        return d.mapRect(dst, src);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float native_mapRadius(int native_object, float radius) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return 0.f;
+        }
+
+        float[] src = new float[] { radius, 0.f, 0.f, radius };
+        d.mapVectors(src, 0, src, 0, 2);
+
+        float l1 = getPointLength(src, 0);
+        float l2 = getPointLength(src, 2);
+
+        return (float) Math.sqrt(l1 * l2);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_getValues(int native_object, float[] values) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return;
+        }
+
+        System.arraycopy(d.mValues, 0, d.mValues, 0, MATRIX_SIZE);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setValues(int native_object, float[] values) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return;
+        }
+
+        System.arraycopy(values, 0, d.mValues, 0, MATRIX_SIZE);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_equals(int native_a, int native_b) {
+        Matrix_Delegate a = sManager.getDelegate(native_a);
+        if (a == null) {
+            return false;
+        }
+
+        Matrix_Delegate b = sManager.getDelegate(native_b);
+        if (b == null) {
+            return false;
+        }
+
+        for (int i = 0 ; i < MATRIX_SIZE ; i++) {
+            if (a.mValues[i] != b.mValues[i]) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void finalizer(int native_instance) {
+        sManager.removeJavaReferenceFor(native_instance);
+    }
+
+    // ---- Private helper methods ----
+
+    /*package*/ static AffineTransform getAffineTransform(float[] matrix) {
+        // the AffineTransform constructor takes the value in a different order
+        // for a matrix [ 0 1 2 ]
+        //              [ 3 4 5 ]
+        // the order is 0, 3, 1, 4, 2, 5...
+        return new AffineTransform(
+                matrix[0], matrix[3], matrix[1],
+                matrix[4], matrix[2], matrix[5]);
+    }
+
+    /**
+     * Reset a matrix to the identity
+     */
+    private static void reset(float[] mtx) {
+        for (int i = 0, k = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++, k++) {
+                mtx[k] = ((i==j) ? 1 : 0);
+            }
+        }
+    }
+
+    @SuppressWarnings("unused")
+    private final static int kIdentity_Mask      = 0;
+    private final static int kTranslate_Mask     = 0x01;  //!< set if the matrix has translation
+    private final static int kScale_Mask         = 0x02;  //!< set if the matrix has X or Y scale
+    private final static int kAffine_Mask        = 0x04;  //!< set if the matrix skews or rotates
+    private final static int kPerspective_Mask   = 0x08;  //!< set if the matrix is in perspective
+    private final static int kRectStaysRect_Mask = 0x10;
+    @SuppressWarnings("unused")
+    private final static int kUnknown_Mask       = 0x80;
+
+    @SuppressWarnings("unused")
+    private final static int kAllMasks           = kTranslate_Mask |
+                                                   kScale_Mask |
+                                                   kAffine_Mask |
+                                                   kPerspective_Mask |
+                                                   kRectStaysRect_Mask;
+
+    // these guys align with the masks, so we can compute a mask from a variable 0/1
+    @SuppressWarnings("unused")
+    private final static int kTranslate_Shift = 0;
+    @SuppressWarnings("unused")
+    private final static int kScale_Shift = 1;
+    @SuppressWarnings("unused")
+    private final static int kAffine_Shift = 2;
+    @SuppressWarnings("unused")
+    private final static int kPerspective_Shift = 3;
+    private final static int kRectStaysRect_Shift = 4;
+
+    private int computeTypeMask() {
+        int mask = 0;
+
+        if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) {
+            mask |= kPerspective_Mask;
+        }
+
+        if (mValues[2] != 0. || mValues[5] != 0.) {
+            mask |= kTranslate_Mask;
+        }
+
+        float m00 = mValues[0];
+        float m01 = mValues[1];
+        float m10 = mValues[3];
+        float m11 = mValues[4];
+
+        if (m01 != 0. || m10 != 0.) {
+            mask |= kAffine_Mask;
+        }
+
+        if (m00 != 1. || m11 != 1.) {
+            mask |= kScale_Mask;
+        }
+
+        if ((mask & kPerspective_Mask) == 0) {
+            // map non-zero to 1
+            int im00 = m00 != 0 ? 1 : 0;
+            int im01 = m01 != 0 ? 1 : 0;
+            int im10 = m10 != 0 ? 1 : 0;
+            int im11 = m11 != 0 ? 1 : 0;
+
+            // record if the (p)rimary and (s)econdary diagonals are all 0 or
+            // all non-zero (answer is 0 or 1)
+            int dp0 = (im00 | im11) ^ 1;  // true if both are 0
+            int dp1 = im00 & im11;        // true if both are 1
+            int ds0 = (im01 | im10) ^ 1;  // true if both are 0
+            int ds1 = im01 & im10;        // true if both are 1
+
+            // return 1 if primary is 1 and secondary is 0 or
+            // primary is 0 and secondary is 1
+            mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
+        }
+
+        return mask;
+    }
+
+    private Matrix_Delegate() {
+        reset();
+    }
+
+    private Matrix_Delegate(float[] values) {
+        System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE);
+    }
+
+    /**
+     * Adds the given transformation to the current Matrix
+     * <p/>This in effect does this = this*matrix
+     * @param matrix
+     */
+    private void postTransform(float[] matrix) {
+        float[] tmp = new float[9];
+        multiply(tmp, mValues, matrix);
+        mValues = tmp;
+    }
+
+    /**
+     * Adds the given transformation to the current Matrix
+     * <p/>This in effect does this = matrix*this
+     * @param matrix
+     */
+    private void preTransform(float[] matrix) {
+        float[] tmp = new float[9];
+        multiply(tmp, matrix, mValues);
+        mValues = tmp;
+    }
+
+    /**
+     * Apply this matrix to the array of 2D points specified by src, and write
+      * the transformed points into the array of points specified by dst. The
+      * two arrays represent their "points" as pairs of floats [x, y].
+      *
+      * @param dst   The array of dst points (x,y pairs)
+      * @param dstIndex The index of the first [x,y] pair of dst floats
+      * @param src   The array of src points (x,y pairs)
+      * @param srcIndex The index of the first [x,y] pair of src floats
+      * @param pointCount The number of points (x,y pairs) to transform
+      */
+
+     private void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
+                           int pointCount) {
+         final int count = pointCount * 2;
+
+         float[] tmpDest = dst;
+         boolean inPlace = dst == src;
+         if (inPlace) {
+             tmpDest = new float[dstIndex + count];
+         }
+
+         for (int i = 0 ; i < count ; i += 2) {
+             // just in case we are doing in place, we better put this in temp vars
+             float x = mValues[0] * src[i + srcIndex] +
+                       mValues[1] * src[i + srcIndex + 1] +
+                       mValues[2];
+             float y = mValues[3] * src[i + srcIndex] +
+                       mValues[4] * src[i + srcIndex + 1] +
+                       mValues[5];
+
+             tmpDest[i + dstIndex]     = x;
+             tmpDest[i + dstIndex + 1] = y;
+         }
+
+         if (inPlace) {
+             System.arraycopy(tmpDest, dstIndex, dst, dstIndex, count);
+         }
+     }
+
+     /**
+      * Apply this matrix to the array of 2D points, and write the transformed
+      * points back into the array
+      *
+      * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
+      */
+
+     private void mapPoints(float[] pts) {
+         mapPoints(pts, 0, pts, 0, pts.length >> 1);
+     }
+
+     private void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int ptCount) {
+         if (hasPerspective()) {
+             // transform the (0,0) point
+             float[] origin = new float[] { 0.f, 0.f};
+             mapPoints(origin);
+
+             // translate the vector data as points
+             mapPoints(dst, dstIndex, src, srcIndex, ptCount);
+
+             // then substract the transformed origin.
+             final int count = ptCount * 2;
+             for (int i = 0 ; i < count ; i += 2) {
+                 dst[dstIndex + i] = dst[dstIndex + i] - origin[0];
+                 dst[dstIndex + i + 1] = dst[dstIndex + i + 1] - origin[1];
+             }
+         } else {
+             // make a copy of the matrix
+             Matrix_Delegate copy = new Matrix_Delegate(mValues);
+
+             // remove the translation
+             setTranslate(copy.mValues, 0, 0);
+
+             // map the content as points.
+             copy.mapPoints(dst, dstIndex, src, srcIndex, ptCount);
+         }
+     }
+
+     private static float getPointLength(float[] src, int index) {
+         return (float) Math.sqrt(src[index] * src[index] + src[index + 1] * src[index + 1]);
+     }
+
+    /**
+     * multiply two matrices and store them in a 3rd.
+     * <p/>This in effect does dest = a*b
+     * dest cannot be the same as a or b.
+     */
+     /*package*/ static void multiply(float dest[], float[] a, float[] b) {
+        // first row
+        dest[0] = b[0] * a[0] + b[1] * a[3] + b[2] * a[6];
+        dest[1] = b[0] * a[1] + b[1] * a[4] + b[2] * a[7];
+        dest[2] = b[0] * a[2] + b[1] * a[5] + b[2] * a[8];
+
+        // 2nd row
+        dest[3] = b[3] * a[0] + b[4] * a[3] + b[5] * a[6];
+        dest[4] = b[3] * a[1] + b[4] * a[4] + b[5] * a[7];
+        dest[5] = b[3] * a[2] + b[4] * a[5] + b[5] * a[8];
+
+        // 3rd row
+        dest[6] = b[6] * a[0] + b[7] * a[3] + b[8] * a[6];
+        dest[7] = b[6] * a[1] + b[7] * a[4] + b[8] * a[7];
+        dest[8] = b[6] * a[2] + b[7] * a[5] + b[8] * a[8];
+    }
+
+    /**
+     * Returns a matrix that represents a given translate
+     * @param dx
+     * @param dy
+     * @return
+     */
+    /*package*/ static float[] getTranslate(float dx, float dy) {
+        return setTranslate(new float[9], dx, dy);
+    }
+
+    /*package*/ static float[] setTranslate(float[] dest, float dx, float dy) {
+        dest[0] = 1;
+        dest[1] = 0;
+        dest[2] = dx;
+        dest[3] = 0;
+        dest[4] = 1;
+        dest[5] = dy;
+        dest[6] = 0;
+        dest[7] = 0;
+        dest[8] = 1;
+        return dest;
+    }
+
+    /*package*/ static float[] getScale(float sx, float sy) {
+        return new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
+    }
+
+    /**
+     * Returns a matrix that represents the given scale info.
+     * @param sx
+     * @param sy
+     * @param px
+     * @param py
+     */
+    /*package*/ static float[] getScale(float sx, float sy, float px, float py) {
+        float[] tmp = new float[9];
+        float[] tmp2 = new float[9];
+
+        // TODO: do it in one pass
+
+        // translate tmp so that the pivot is in 0,0
+        setTranslate(tmp, -px, -py);
+
+        // scale into tmp2
+        multiply(tmp2, tmp, getScale(sx, sy));
+
+        // translate back the pivot back into tmp
+        multiply(tmp, tmp2, getTranslate(px, py));
+
+        return tmp;
+    }
+
+
+    /*package*/ static float[] getRotate(float degrees) {
+        double rad = Math.toRadians(degrees);
+        float sin = (float)Math.sin(rad);
+        float cos = (float)Math.cos(rad);
+
+        return getRotate(sin, cos);
+    }
+
+    /*package*/ static float[] getRotate(float sin, float cos) {
+        return setRotate(new float[9], sin, cos);
+    }
+
+    /*package*/ static float[] setRotate(float[] dest, float degrees) {
+        double rad = Math.toRadians(degrees);
+        float sin = (float)Math.sin(rad);
+        float cos = (float)Math.cos(rad);
+
+        return setRotate(dest, sin, cos);
+    }
+
+    /*package*/ static float[] setRotate(float[] dest, float sin, float cos) {
+        dest[0] = cos;
+        dest[1] = -sin;
+        dest[2] = 0;
+        dest[3] = sin;
+        dest[4] = cos;
+        dest[5] = 0;
+        dest[6] = 0;
+        dest[7] = 0;
+        dest[8] = 1;
+        return dest;
+    }
+
+    /*package*/ static float[] getRotate(float degrees, float px, float py) {
+        float[] tmp = new float[9];
+        float[] tmp2 = new float[9];
+
+        // TODO: do it in one pass
+
+        // translate so that the pivot is in 0,0
+        setTranslate(tmp, -px, -py);
+
+        // rotate into tmp2
+        double rad = Math.toRadians(degrees);
+        float cos = (float)Math.cos(rad);
+        float sin = (float)Math.sin(rad);
+        multiply(tmp2, tmp, getRotate(sin, cos));
+
+        // translate back the pivot back into tmp
+        multiply(tmp, tmp2, getTranslate(px, py));
+
+        return tmp;
+    }
+
+    /*package*/ static float[] getSkew(float kx, float ky) {
+        return new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 };
+    }
+
+    /*package*/ static float[] getSkew(float kx, float ky, float px, float py) {
+        float[] tmp = new float[9];
+        float[] tmp2 = new float[9];
+
+        // TODO: do it in one pass
+
+        // translate so that the pivot is in 0,0
+        setTranslate(tmp, -px, -py);
+
+        // skew into tmp2
+        multiply(tmp2, tmp, new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
+        // translate back the pivot back into tmp
+        multiply(tmp, tmp2, getTranslate(px, py));
+
+        return tmp;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
new file mode 100644
index 0000000..5e882ce
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2010 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 com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.layoutlib.bridge.impl.GcSnapshot;
+import com.android.ninepatch.NinePatchChunk;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.graphics.drawable.NinePatchDrawable;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.ref.SoftReference;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Delegate implementing the native methods of android.graphics.NinePatch
+ *
+ * Through the layoutlib_create tool, the original native methods of NinePatch have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
+ * around to map int to instance of the delegate.
+ *
+ */
+public final class NinePatch_Delegate {
+
+    /**
+     * Cache map for {@link NinePatchChunk}.
+     * When the chunks are created they are serialized into a byte[], and both are put
+     * in the cache, using a {@link SoftReference} for the chunk. The default Java classes
+     * for {@link NinePatch} and {@link NinePatchDrawable} only reference to the byte[] data, and
+     * provide this for drawing.
+     * Using the cache map allows us to not have to deserialize the byte[] back into a
+     * {@link NinePatchChunk} every time a rendering is done.
+     */
+    private final static Map<byte[], SoftReference<NinePatchChunk>> sChunkCache =
+        new HashMap<byte[], SoftReference<NinePatchChunk>>();
+
+    // ---- Public Helper methods ----
+
+    /**
+     * Serializes the given chunk.
+     *
+     * @return the serialized data for the chunk.
+     */
+    public static byte[] serialize(NinePatchChunk chunk) {
+        // serialize the chunk to get a byte[]
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = null;
+        try {
+            oos = new ObjectOutputStream(baos);
+            oos.writeObject(chunk);
+        } catch (IOException e) {
+            Bridge.getLog().error(null, "Failed to serialize NinePatchChunk.", e, null /*data*/);
+            return null;
+        } finally {
+            if (oos != null) {
+                try {
+                    oos.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+
+        // get the array and add it to the cache
+        byte[] array = baos.toByteArray();
+        sChunkCache.put(array, new SoftReference<NinePatchChunk>(chunk));
+        return array;
+    }
+
+    /**
+     * Returns a {@link NinePatchChunk} object for the given serialized representation.
+     *
+     * If the chunk is present in the cache then the object from the cache is returned, otherwise
+     * the array is deserialized into a {@link NinePatchChunk} object.
+     *
+     * @param array the serialized representation of the chunk.
+     * @return the NinePatchChunk or null if deserialization failed.
+     */
+    public static NinePatchChunk getChunk(byte[] array) {
+        SoftReference<NinePatchChunk> chunkRef = sChunkCache.get(array);
+        NinePatchChunk chunk = chunkRef.get();
+        if (chunk == null) {
+            ByteArrayInputStream bais = new ByteArrayInputStream(array);
+            ObjectInputStream ois = null;
+            try {
+                ois = new ObjectInputStream(bais);
+                chunk = (NinePatchChunk) ois.readObject();
+
+                // put back the chunk in the cache
+                if (chunk != null) {
+                    sChunkCache.put(array, new SoftReference<NinePatchChunk>(chunk));
+                }
+            } catch (IOException e) {
+                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                        "Failed to deserialize NinePatchChunk content.", e, null /*data*/);
+                return null;
+            } catch (ClassNotFoundException e) {
+                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                        "Failed to deserialize NinePatchChunk class.", e, null /*data*/);
+                return null;
+            } finally {
+                if (ois != null) {
+                    try {
+                        ois.close();
+                    } catch (IOException e) {
+                    }
+                }
+            }
+        }
+
+        return chunk;
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static boolean isNinePatchChunk(byte[] chunk) {
+        NinePatchChunk chunkObject = getChunk(chunk);
+        if (chunkObject != null) {
+            return true;
+        }
+
+        return false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void validateNinePatchChunk(int bitmap, byte[] chunk) {
+        // the default JNI implementation only checks that the byte[] has the same
+        // size as the C struct it represent. Since we cannot do the same check (serialization
+        // will return different size depending on content), we do nothing.
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeDraw(int canvas_instance, RectF loc, int bitmap_instance,
+            byte[] c, int paint_instance_or_null, int destDensity, int srcDensity) {
+        draw(canvas_instance,
+                (int) loc.left, (int) loc.top, (int) loc.width(), (int) loc.height(),
+                bitmap_instance, c, paint_instance_or_null,
+                destDensity, srcDensity);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeDraw(int canvas_instance, Rect loc, int bitmap_instance,
+            byte[] c, int paint_instance_or_null, int destDensity, int srcDensity) {
+        draw(canvas_instance,
+                loc.left, loc.top, loc.width(), loc.height(),
+                bitmap_instance, c, paint_instance_or_null,
+                destDensity, srcDensity);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeGetTransparentRegion(int bitmap, byte[] chunk, Rect location) {
+        return 0;
+    }
+
+    // ---- Private Helper methods ----
+
+    private static void draw(int canvas_instance,
+            final int left, final int top, final int right, final int bottom,
+            int bitmap_instance, byte[] c, int paint_instance_or_null,
+            final int destDensity, final int srcDensity) {
+        // get the delegate from the native int.
+        final Bitmap_Delegate bitmap_delegate = Bitmap_Delegate.getDelegate(bitmap_instance);
+        if (bitmap_delegate == null) {
+            return;
+        }
+
+        if (c == null) {
+            // not a 9-patch?
+            BufferedImage image = bitmap_delegate.getImage();
+            Canvas_Delegate.native_drawBitmap(canvas_instance, bitmap_instance,
+                    new Rect(0, 0, image.getWidth(), image.getHeight()),
+                    new Rect(left, top, right, bottom),
+                    paint_instance_or_null, destDensity, srcDensity);
+            return;
+        }
+
+        final NinePatchChunk chunkObject = getChunk(c);
+        assert chunkObject != null;
+        if (chunkObject == null) {
+            return;
+        }
+
+        Canvas_Delegate canvas_delegate = Canvas_Delegate.getDelegate(canvas_instance);
+        if (canvas_delegate == null) {
+            return;
+        }
+
+        // this one can be null
+        Paint_Delegate paint_delegate = Paint_Delegate.getDelegate(paint_instance_or_null);
+
+        canvas_delegate.getSnapshot().draw(new GcSnapshot.Drawable() {
+                public void draw(Graphics2D graphics, Paint_Delegate paint) {
+                    chunkObject.draw(bitmap_delegate.getImage(), graphics,
+                            left, top, right - left, bottom - top, destDensity, srcDensity);
+                }
+            }, paint_delegate, true /*compositeOnly*/, false /*forceSrcMode*/);
+
+     }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint.java b/tools/layoutlib/bridge/src/android/graphics/Paint.java
deleted file mode 100644
index d13b5fe..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Paint.java
+++ /dev/null
@@ -1,1211 +0,0 @@
-/*
- * Copyright (C) 2008 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 android.text.SpannableString;
-import android.text.SpannableStringBuilder;
-import android.text.SpannedString;
-import android.text.TextUtils;
-
-import java.awt.BasicStroke;
-import java.awt.Font;
-import java.awt.Toolkit;
-import java.awt.font.FontRenderContext;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Rectangle2D;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A paint implementation overridden by the LayoutLib bridge.
- */
-public class Paint extends _Original_Paint {
-
-    private int mColor = 0xFFFFFFFF;
-    private float mStrokeWidth = 1.f;
-    private float mTextSize = 20;
-    private float mScaleX = 1;
-    private float mSkewX = 0;
-    private Align mAlign = Align.LEFT;
-    private Style mStyle = Style.FILL;
-    private float mStrokeMiter = 4.0f;
-    private Cap mCap = Cap.BUTT;
-    private Join mJoin = Join.MITER;
-    private int mFlags = 0;
-
-    /**
-     * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
-     */
-    public static final class FontInfo {
-        Font mFont;
-        java.awt.FontMetrics mMetrics;
-    }
-
-    private List<FontInfo> mFonts;
-    private final FontRenderContext mFontContext = new FontRenderContext(
-            new AffineTransform(), true, true);
-
-    public static final int ANTI_ALIAS_FLAG       = _Original_Paint.ANTI_ALIAS_FLAG;
-    public static final int FILTER_BITMAP_FLAG    = _Original_Paint.FILTER_BITMAP_FLAG;
-    public static final int DITHER_FLAG           = _Original_Paint.DITHER_FLAG;
-    public static final int UNDERLINE_TEXT_FLAG   = _Original_Paint.UNDERLINE_TEXT_FLAG;
-    public static final int STRIKE_THRU_TEXT_FLAG = _Original_Paint.STRIKE_THRU_TEXT_FLAG;
-    public static final int FAKE_BOLD_TEXT_FLAG   = _Original_Paint.FAKE_BOLD_TEXT_FLAG;
-    public static final int LINEAR_TEXT_FLAG      = _Original_Paint.LINEAR_TEXT_FLAG;
-    public static final int SUBPIXEL_TEXT_FLAG    = _Original_Paint.SUBPIXEL_TEXT_FLAG;
-    public static final int DEV_KERN_TEXT_FLAG    = _Original_Paint.DEV_KERN_TEXT_FLAG;
-
-    public static class FontMetrics extends _Original_Paint.FontMetrics {
-    }
-
-    public static class FontMetricsInt extends _Original_Paint.FontMetricsInt {
-    }
-
-    /**
-     * The Style specifies if the primitive being drawn is filled,
-     * stroked, or both (in the same color). The default is FILL.
-     */
-    public enum Style {
-        /**
-         * Geometry and text drawn with this style will be filled, ignoring all
-         * stroke-related settings in the paint.
-         */
-        FILL            (0),
-        /**
-         * Geometry and text drawn with this style will be stroked, respecting
-         * the stroke-related fields on the paint.
-         */
-        STROKE          (1),
-        /**
-         * Geometry and text drawn with this style will be both filled and
-         * stroked at the same time, respecting the stroke-related fields on
-         * the paint.
-         */
-        FILL_AND_STROKE (2);
-
-        Style(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-    }
-
-    /**
-     * The Cap specifies the treatment for the beginning and ending of
-     * stroked lines and paths. The default is BUTT.
-     */
-    public enum Cap {
-        /**
-         * The stroke ends with the path, and does not project beyond it.
-         */
-        BUTT    (0),
-        /**
-         * The stroke projects out as a square, with the center at the end
-         * of the path.
-         */
-        ROUND   (1),
-        /**
-         * The stroke projects out as a semicircle, with the center at the
-         * end of the path.
-         */
-        SQUARE  (2);
-
-        private Cap(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-
-        /** custom for layoutlib */
-        public int getJavaCap() {
-            switch (this) {
-                case BUTT:
-                    return BasicStroke.CAP_BUTT;
-                case ROUND:
-                    return BasicStroke.CAP_ROUND;
-                default:
-                case SQUARE:
-                    return BasicStroke.CAP_SQUARE;
-            }
-        }
-    }
-
-    /**
-     * The Join specifies the treatment where lines and curve segments
-     * join on a stroked path. The default is MITER.
-     */
-    public enum Join {
-        /**
-         * The outer edges of a join meet at a sharp angle
-         */
-        MITER   (0),
-        /**
-         * The outer edges of a join meet in a circular arc.
-         */
-        ROUND   (1),
-        /**
-         * The outer edges of a join meet with a straight line
-         */
-        BEVEL   (2);
-
-        private Join(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-
-        /** custom for layoutlib */
-        public int getJavaJoin() {
-            switch (this) {
-                default:
-                case MITER:
-                    return BasicStroke.JOIN_MITER;
-                case ROUND:
-                    return BasicStroke.JOIN_ROUND;
-                case BEVEL:
-                    return BasicStroke.JOIN_BEVEL;
-            }
-        }
-    }
-
-    /**
-     * Align specifies how drawText aligns its text relative to the
-     * [x,y] coordinates. The default is LEFT.
-     */
-    public enum Align {
-        /**
-         * The text is drawn to the right of the x,y origin
-         */
-        LEFT    (0),
-        /**
-         * The text is drawn centered horizontally on the x,y origin
-         */
-        CENTER  (1),
-        /**
-         * The text is drawn to the left of the x,y origin
-         */
-        RIGHT   (2);
-
-        private Align(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-    }
-
-    public Paint() {
-        this(0);
-    }
-
-    /*
-     * Do not remove or com.android.layoutlib.bridge.TestClassReplacement fails.
-     */
-    @Override
-    public void finalize() { }
-
-    public Paint(int flags) {
-        setFlags(flags | DEFAULT_PAINT_FLAGS);
-        initFont();
-    }
-
-    public Paint(Paint paint) {
-        set(paint);
-        initFont();
-    }
-
-    @Override
-    public void reset() {
-        super.reset();
-    }
-
-    /**
-     * Returns the list of {@link Font} objects. The first item is the main font, the rest
-     * are fall backs for characters not present in the main font.
-     */
-    public List<FontInfo> getFonts() {
-        return mFonts;
-    }
-
-    private void initFont() {
-        mTypeface = Typeface.DEFAULT;
-        updateFontObject();
-    }
-
-    /**
-     * Update the {@link Font} object from the typeface, text size and scaling
-     */
-    @SuppressWarnings("deprecation")
-    private void updateFontObject() {
-        if (mTypeface != null) {
-            // Get the fonts from the TypeFace object.
-            List<Font> fonts = mTypeface.getFonts();
-
-            // create new font objects as well as FontMetrics, based on the current text size
-            // and skew info.
-            ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
-            for (Font font : fonts) {
-                FontInfo info = new FontInfo();
-                info.mFont = font.deriveFont(mTextSize);
-                if (mScaleX != 1.0 || mSkewX != 0) {
-                    // TODO: support skew
-                    info.mFont = info.mFont.deriveFont(new AffineTransform(
-                            mScaleX, mSkewX, 0, 0, 1, 0));
-                }
-                info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
-
-                infoList.add(info);
-            }
-
-            mFonts = Collections.unmodifiableList(infoList);
-        }
-    }
-
-    //----------------------------------------
-
-    public void set(Paint src) {
-        if (this != src) {
-            mColor = src.mColor;
-            mTextSize = src.mTextSize;
-            mScaleX = src.mScaleX;
-            mSkewX = src.mSkewX;
-            mAlign = src.mAlign;
-            mStyle = src.mStyle;
-            mFlags = src.mFlags;
-
-            updateFontObject();
-
-            super.set(src);
-        }
-    }
-
-    @Override
-    public void setCompatibilityScaling(float factor) {
-        super.setCompatibilityScaling(factor);
-    }
-
-    @Override
-    public int getFlags() {
-        return mFlags;
-    }
-
-    @Override
-    public void setFlags(int flags) {
-        mFlags = flags;
-    }
-
-    @Override
-    public boolean isAntiAlias() {
-        return super.isAntiAlias();
-    }
-
-    @Override
-    public boolean isDither() {
-        return super.isDither();
-    }
-
-    @Override
-    public boolean isLinearText() {
-        return super.isLinearText();
-    }
-
-    @Override
-    public boolean isStrikeThruText() {
-        return super.isStrikeThruText();
-    }
-
-    @Override
-    public boolean isUnderlineText() {
-        return super.isUnderlineText();
-    }
-
-    @Override
-    public boolean isFakeBoldText() {
-        return super.isFakeBoldText();
-    }
-
-    @Override
-    public boolean isSubpixelText() {
-        return super.isSubpixelText();
-    }
-
-    @Override
-    public boolean isFilterBitmap() {
-        return super.isFilterBitmap();
-    }
-
-    /**
-     * Return the font's recommended interline spacing, given the Paint's
-     * settings for typeface, textSize, etc. If metrics is not null, return the
-     * fontmetric values in it.
-     *
-     * @param metrics If this object is not null, its fields are filled with
-     *                the appropriate values given the paint's text attributes.
-     * @return the font's recommended interline spacing.
-     */
-    public float getFontMetrics(FontMetrics metrics) {
-        if (mFonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
-            if (metrics != null) {
-                // Android expects negative ascent so we invert the value from Java.
-                metrics.top = - javaMetrics.getMaxAscent();
-                metrics.ascent = - javaMetrics.getAscent();
-                metrics.descent = javaMetrics.getDescent();
-                metrics.bottom = javaMetrics.getMaxDescent();
-                metrics.leading = javaMetrics.getLeading();
-            }
-
-            return javaMetrics.getHeight();
-        }
-
-        return 0;
-    }
-
-    public int getFontMetricsInt(FontMetricsInt metrics) {
-        if (mFonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
-            if (metrics != null) {
-                // Android expects negative ascent so we invert the value from Java.
-                metrics.top = - javaMetrics.getMaxAscent();
-                metrics.ascent = - javaMetrics.getAscent();
-                metrics.descent = javaMetrics.getDescent();
-                metrics.bottom = javaMetrics.getMaxDescent();
-                metrics.leading = javaMetrics.getLeading();
-            }
-
-            return javaMetrics.getHeight();
-        }
-
-        return 0;
-    }
-
-    /**
-     * Reimplemented to return Paint.FontMetrics instead of _Original_Paint.FontMetrics
-     */
-    public FontMetrics getFontMetrics() {
-        FontMetrics fm = new FontMetrics();
-        getFontMetrics(fm);
-        return fm;
-    }
-
-    /**
-     * Reimplemented to return Paint.FontMetricsInt instead of _Original_Paint.FontMetricsInt
-     */
-    public FontMetricsInt getFontMetricsInt() {
-        FontMetricsInt fm = new FontMetricsInt();
-        getFontMetricsInt(fm);
-        return fm;
-    }
-
-
-
-    @Override
-    public float getFontMetrics(_Original_Paint.FontMetrics metrics) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    @Override
-    public int getFontMetricsInt(_Original_Paint.FontMetricsInt metrics) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    @Override
-    public Typeface setTypeface(Typeface typeface) {
-        if (typeface != null) {
-            mTypeface = typeface;
-        } else {
-            mTypeface = Typeface.DEFAULT;
-        }
-
-        updateFontObject();
-
-        return typeface;
-    }
-
-    @Override
-    public Typeface getTypeface() {
-        return super.getTypeface();
-    }
-
-    @Override
-    public int getColor() {
-        return mColor;
-    }
-
-    @Override
-    public void setColor(int color) {
-        mColor = color;
-    }
-
-    @Override
-    public void setARGB(int a, int r, int g, int b) {
-        super.setARGB(a, r, g, b);
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        mColor = (alpha << 24) | (mColor & 0x00FFFFFF);
-    }
-
-    @Override
-    public int getAlpha() {
-        return mColor >>> 24;
-    }
-
-    /**
-     * Set or clear the shader object.
-     * <p />
-     * Pass null to clear any previous shader.
-     * As a convenience, the parameter passed is also returned.
-     *
-     * @param shader May be null. the new shader to be installed in the paint
-     * @return       shader
-     */
-    @Override
-    public Shader setShader(Shader shader) {
-        return mShader = shader;
-    }
-
-    @Override
-    public Shader getShader() {
-        return super.getShader();
-    }
-
-    /**
-     * Set or clear the paint's colorfilter, returning the parameter.
-     *
-     * @param filter May be null. The new filter to be installed in the paint
-     * @return       filter
-     */
-    @Override
-    public ColorFilter setColorFilter(ColorFilter filter) {
-        mColorFilter = filter;
-        return filter;
-    }
-
-    @Override
-    public ColorFilter getColorFilter() {
-        return super.getColorFilter();
-    }
-
-    /**
-     * Set or clear the xfermode object.
-     * <p />
-     * Pass null to clear any previous xfermode.
-     * As a convenience, the parameter passed is also returned.
-     *
-     * @param xfermode May be null. The xfermode to be installed in the paint
-     * @return         xfermode
-     */
-    @Override
-    public Xfermode setXfermode(Xfermode xfermode) {
-        return mXfermode = xfermode;
-    }
-
-    @Override
-    public Xfermode getXfermode() {
-        return super.getXfermode();
-    }
-
-    @Override
-    public Rasterizer setRasterizer(Rasterizer rasterizer) {
-        mRasterizer = rasterizer;
-        return rasterizer;
-    }
-
-    @Override
-    public Rasterizer getRasterizer() {
-        return super.getRasterizer();
-    }
-
-    @Override
-    public void setShadowLayer(float radius, float dx, float dy, int color) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void clearShadowLayer() {
-        super.clearShadowLayer();
-    }
-
-    public void setTextAlign(Align align) {
-        mAlign = align;
-    }
-
-    @Override
-    public void setTextAlign(android.graphics._Original_Paint.Align align) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public Align getTextAlign() {
-        return mAlign;
-    }
-
-    public void setStyle(Style style) {
-        mStyle = style;
-    }
-
-    @Override
-    public void setStyle(android.graphics._Original_Paint.Style style) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public Style getStyle() {
-        return mStyle;
-    }
-
-    @Override
-    public void setDither(boolean dither) {
-        mFlags |= dither ? DITHER_FLAG : ~DITHER_FLAG;
-    }
-
-    @Override
-    public void setAntiAlias(boolean aa) {
-        mFlags |= aa ? ANTI_ALIAS_FLAG : ~ANTI_ALIAS_FLAG;
-    }
-
-    @Override
-    public void setFakeBoldText(boolean flag) {
-        mFlags |= flag ? FAKE_BOLD_TEXT_FLAG : ~FAKE_BOLD_TEXT_FLAG;
-    }
-
-    @Override
-    public void setLinearText(boolean flag) {
-        mFlags |= flag ? LINEAR_TEXT_FLAG : ~LINEAR_TEXT_FLAG;
-    }
-
-    @Override
-    public void setSubpixelText(boolean flag) {
-        mFlags |= flag ? SUBPIXEL_TEXT_FLAG : ~SUBPIXEL_TEXT_FLAG;
-    }
-
-    @Override
-    public void setUnderlineText(boolean flag) {
-        mFlags |= flag ? UNDERLINE_TEXT_FLAG : ~UNDERLINE_TEXT_FLAG;
-    }
-
-    @Override
-    public void setStrikeThruText(boolean flag) {
-        mFlags |= flag ? STRIKE_THRU_TEXT_FLAG : ~STRIKE_THRU_TEXT_FLAG;
-    }
-
-    @Override
-    public void setFilterBitmap(boolean flag) {
-        mFlags |= flag ? FILTER_BITMAP_FLAG : ~FILTER_BITMAP_FLAG;
-    }
-
-    @Override
-    public float getStrokeWidth() {
-        return mStrokeWidth;
-    }
-
-    @Override
-    public void setStrokeWidth(float width) {
-        mStrokeWidth = width;
-    }
-
-    @Override
-    public float getStrokeMiter() {
-        return mStrokeMiter;
-    }
-
-    @Override
-    public void setStrokeMiter(float miter) {
-        mStrokeMiter = miter;
-    }
-
-    @Override
-    public void setStrokeCap(android.graphics._Original_Paint.Cap cap) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public void setStrokeCap(Cap cap) {
-        mCap = cap;
-    }
-
-    public Cap getStrokeCap() {
-        return mCap;
-    }
-
-    @Override
-    public void setStrokeJoin(android.graphics._Original_Paint.Join join) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public void setStrokeJoin(Join join) {
-        mJoin = join;
-    }
-
-    public Join getStrokeJoin() {
-        return mJoin;
-    }
-
-    @Override
-    public boolean getFillPath(Path src, Path dst) {
-        return super.getFillPath(src, dst);
-    }
-
-    @Override
-    public PathEffect setPathEffect(PathEffect effect) {
-        mPathEffect = effect;
-        return effect;
-    }
-
-    @Override
-    public PathEffect getPathEffect() {
-        return super.getPathEffect();
-    }
-
-    @Override
-    public MaskFilter setMaskFilter(MaskFilter maskfilter) {
-        mMaskFilter = maskfilter;
-        return maskfilter;
-    }
-
-    @Override
-    public MaskFilter getMaskFilter() {
-        return super.getMaskFilter();
-    }
-
-    /**
-     * Return the paint's text size.
-     *
-     * @return the paint's text size.
-     */
-    @Override
-    public float getTextSize() {
-        return mTextSize;
-    }
-
-    /**
-     * Set the paint's text size. This value must be > 0
-     *
-     * @param textSize set the paint's text size.
-     */
-    @Override
-    public void setTextSize(float textSize) {
-        mTextSize = textSize;
-
-        updateFontObject();
-    }
-
-    /**
-     * Return the paint's horizontal scale factor for text. The default value
-     * is 1.0.
-     *
-     * @return the paint's scale factor in X for drawing/measuring text
-     */
-    @Override
-    public float getTextScaleX() {
-        return mScaleX;
-    }
-
-    /**
-     * Set the paint's horizontal scale factor for text. The default value
-     * is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will
-     * stretch the text narrower.
-     *
-     * @param scaleX set the paint's scale in X for drawing/measuring text.
-     */
-    @Override
-    public void setTextScaleX(float scaleX) {
-        mScaleX = scaleX;
-
-        updateFontObject();
-    }
-
-    /**
-     * Return the paint's horizontal skew factor for text. The default value
-     * is 0.
-     *
-     * @return         the paint's skew factor in X for drawing text.
-     */
-    @Override
-    public float getTextSkewX() {
-        return mSkewX;
-    }
-
-    /**
-     * Set the paint's horizontal skew factor for text. The default value
-     * is 0. For approximating oblique text, use values around -0.25.
-     *
-     * @param skewX set the paint's skew factor in X for drawing text.
-     */
-    @Override
-    public void setTextSkewX(float skewX) {
-        mSkewX = skewX;
-
-        updateFontObject();
-    }
-
-    @Override
-    public float getFontSpacing() {
-        return super.getFontSpacing();
-    }
-
-    /**
-     * Return the distance above (negative) the baseline (ascent) based on the
-     * current typeface and text size.
-     *
-     * @return the distance above (negative) the baseline (ascent) based on the
-     *         current typeface and text size.
-     */
-    @Override
-    public float ascent() {
-        if (mFonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
-            // Android expects negative ascent so we invert the value from Java.
-            return - javaMetrics.getAscent();
-        }
-
-        return 0;
-    }
-
-    /**
-     * Return the distance below (positive) the baseline (descent) based on the
-     * current typeface and text size.
-     *
-     * @return the distance below (positive) the baseline (descent) based on
-     *         the current typeface and text size.
-     */
-    @Override
-    public float descent() {
-        if (mFonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
-            return javaMetrics.getDescent();
-        }
-
-        return 0;
-    }
-
-    /**
-     * Return the width of the text.
-     *
-     * @param text  The text to measure
-     * @param index The index of the first character to start measuring
-     * @param count THe number of characters to measure, beginning with start
-     * @return      The width of the text
-     */
-    @Override
-    public float measureText(char[] text, int index, int count) {
-        // WARNING: the logic in this method is similar to Canvas.drawText.
-        // Any change to this method should be reflected in Canvas.drawText
-        if (mFonts.size() > 0) {
-            FontInfo mainFont = mFonts.get(0);
-            int i = index;
-            int lastIndex = index + count;
-            float total = 0f;
-            while (i < lastIndex) {
-                // always start with the main font.
-                int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
-                if (upTo == -1) {
-                    // shortcut to exit
-                    return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i);
-                } else if (upTo > 0) {
-                    total += mainFont.mMetrics.charsWidth(text, i, upTo - i);
-                    i = upTo;
-                    // don't call continue at this point. Since it is certain the main font
-                    // cannot display the font a index upTo (now ==i), we move on to the
-                    // fallback fonts directly.
-                }
-
-                // no char supported, attempt to read the next char(s) with the
-                // fallback font. In this case we only test the first character
-                // and then go back to test with the main font.
-                // Special test for 2-char characters.
-                boolean foundFont = false;
-                for (int f = 1 ; f < mFonts.size() ; f++) {
-                    FontInfo fontInfo = mFonts.get(f);
-
-                    // need to check that the font can display the character. We test
-                    // differently if the char is a high surrogate.
-                    int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
-                    upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
-                    if (upTo == -1) {
-                        total += fontInfo.mMetrics.charsWidth(text, i, charCount);
-                        i += charCount;
-                        foundFont = true;
-                        break;
-
-                    }
-                }
-
-                // in case no font can display the char, measure it with the main font.
-                if (foundFont == false) {
-                    int size = Character.isHighSurrogate(text[i]) ? 2 : 1;
-                    total += mainFont.mMetrics.charsWidth(text, i, size);
-                    i += size;
-                }
-            }
-        }
-
-        return 0;
-    }
-
-    /**
-     * Return the width of the text.
-     *
-     * @param text  The text to measure
-     * @param start The index of the first character to start measuring
-     * @param end   1 beyond the index of the last character to measure
-     * @return      The width of the text
-     */
-    @Override
-    public float measureText(String text, int start, int end) {
-        return measureText(text.toCharArray(), start, end - start);
-    }
-
-    /**
-     * Return the width of the text.
-     *
-     * @param text  The text to measure
-     * @return      The width of the text
-     */
-    @Override
-    public float measureText(String text) {
-        return measureText(text.toCharArray(), 0, text.length());
-    }
-
-    /*
-     * re-implement to call SpannableStringBuilder.measureText with a Paint object
-     * instead of an _Original_Paint
-     */
-    @Override
-    public float measureText(CharSequence text, int start, int end) {
-        if (text instanceof String) {
-            return measureText((String)text, start, end);
-        }
-        if (text instanceof SpannedString ||
-            text instanceof SpannableString) {
-            return measureText(text.toString(), start, end);
-        }
-        if (text instanceof SpannableStringBuilder) {
-            return ((SpannableStringBuilder)text).measureText(start, end, this);
-        }
-
-        char[] buf = TemporaryBuffer.obtain(end - start);
-        TextUtils.getChars(text, start, end, buf, 0);
-        float result = measureText(buf, 0, end - start);
-        TemporaryBuffer.recycle(buf);
-        return result;
-    }
-
-    /**
-     * Measure the text, stopping early if the measured width exceeds maxWidth.
-     * Return the number of chars that were measured, and if measuredWidth is
-     * not null, return in it the actual width measured.
-     *
-     * @param text  The text to measure
-     * @param index The offset into text to begin measuring at
-     * @param count The number of maximum number of entries to measure. If count
-     *              is negative, then the characters before index are measured
-     *              in reverse order. This allows for measuring the end of
-     *              string.
-     * @param maxWidth The maximum width to accumulate.
-     * @param measuredWidth Optional. If not null, returns the actual width
-     *                     measured.
-     * @return The number of chars that were measured. Will always be <=
-     *         abs(count).
-     */
-    @Override
-    public int breakText(char[] text, int index, int count,
-                                float maxWidth, float[] measuredWidth) {
-        int inc = count > 0 ? 1 : -1;
-
-        int measureIndex = 0;
-        float measureAcc = 0;
-        for (int i = index ; i != index + count ; i += inc, measureIndex++) {
-            int start, end;
-            if (i < index) {
-                start = i;
-                end = index;
-            } else {
-                start = index;
-                end = i;
-            }
-
-            // measure from start to end
-            float res = measureText(text, start, end - start + 1);
-
-            if (measuredWidth != null) {
-                measuredWidth[measureIndex] = res;
-            }
-
-            measureAcc += res;
-            if (res > maxWidth) {
-                // we should not return this char index, but since it's 0-based and we need
-                // to return a count, we simply return measureIndex;
-                return measureIndex;
-            }
-
-        }
-
-        return measureIndex;
-    }
-
-    /**
-     * Measure the text, stopping early if the measured width exceeds maxWidth.
-     * Return the number of chars that were measured, and if measuredWidth is
-     * not null, return in it the actual width measured.
-     *
-     * @param text  The text to measure
-     * @param measureForwards If true, measure forwards, starting at index.
-     *                        Otherwise, measure backwards, starting with the
-     *                        last character in the string.
-     * @param maxWidth The maximum width to accumulate.
-     * @param measuredWidth Optional. If not null, returns the actual width
-     *                     measured.
-     * @return The number of chars that were measured. Will always be <=
-     *         abs(count).
-     */
-    @Override
-    public int breakText(String text, boolean measureForwards,
-                                float maxWidth, float[] measuredWidth) {
-        return breakText(text,
-                0 /* start */, text.length() /* end */,
-                measureForwards, maxWidth, measuredWidth);
-    }
-
-    /**
-     * Measure the text, stopping early if the measured width exceeds maxWidth.
-     * Return the number of chars that were measured, and if measuredWidth is
-     * not null, return in it the actual width measured.
-     *
-     * @param text  The text to measure
-     * @param start The offset into text to begin measuring at
-     * @param end   The end of the text slice to measure.
-     * @param measureForwards If true, measure forwards, starting at start.
-     *                        Otherwise, measure backwards, starting with end.
-     * @param maxWidth The maximum width to accumulate.
-     * @param measuredWidth Optional. If not null, returns the actual width
-     *                     measured.
-     * @return The number of chars that were measured. Will always be <=
-     *         abs(end - start).
-     */
-    @Override
-    public int breakText(CharSequence text, int start, int end, boolean measureForwards,
-            float maxWidth, float[] measuredWidth) {
-        char[] buf = new char[end - start];
-        int result;
-
-        TextUtils.getChars(text, start, end, buf, 0);
-
-        if (measureForwards) {
-            result = breakText(buf, 0, end - start, maxWidth, measuredWidth);
-        } else {
-            result = breakText(buf, 0, -(end - start), maxWidth, measuredWidth);
-        }
-
-        return result;
-    }
-
-    /**
-     * Return the advance widths for the characters in the string.
-     *
-     * @param text     The text to measure
-     * @param index    The index of the first char to to measure
-     * @param count    The number of chars starting with index to measure
-     * @param widths   array to receive the advance widths of the characters.
-     *                 Must be at least a large as count.
-     * @return         the actual number of widths returned.
-     */
-    @Override
-    public int getTextWidths(char[] text, int index, int count,
-                             float[] widths) {
-        if (mFonts.size() > 0) {
-            if ((index | count) < 0 || index + count > text.length
-                    || count > widths.length) {
-                throw new ArrayIndexOutOfBoundsException();
-            }
-
-            // FIXME: handle multi-char characters.
-            // Need to figure out if the lengths of the width array takes into account
-            // multi-char characters.
-            for (int i = 0; i < count; i++) {
-                char c = text[i + index];
-                boolean found = false;
-                for (FontInfo info : mFonts) {
-                    if (info.mFont.canDisplay(c)) {
-                        widths[i] = info.mMetrics.charWidth(c);
-                        found = true;
-                        break;
-                    }
-                }
-
-                if (found == false) {
-                    // we stop there.
-                    return i;
-                }
-            }
-
-            return count;
-        }
-
-        return 0;
-    }
-
-    /**
-     * Return the advance widths for the characters in the string.
-     *
-     * @param text   The text to measure
-     * @param start  The index of the first char to to measure
-     * @param end    The end of the text slice to measure
-     * @param widths array to receive the advance widths of the characters.
-     *               Must be at least a large as the text.
-     * @return       the number of unichars in the specified text.
-     */
-    @Override
-    public int getTextWidths(String text, int start, int end, float[] widths) {
-        if ((start | end | (end - start) | (text.length() - end)) < 0) {
-            throw new IndexOutOfBoundsException();
-        }
-        if (end - start > widths.length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-
-        return getTextWidths(text.toCharArray(), start, end - start, widths);
-    }
-
-    /*
-     * re-implement to call SpannableStringBuilder.getTextWidths with a Paint object
-     * instead of an _Original_Paint
-     */
-    @Override
-    public int getTextWidths(CharSequence text, int start, int end, float[] widths) {
-        if (text instanceof String) {
-            return getTextWidths((String)text, start, end, widths);
-        }
-        if (text instanceof SpannedString || text instanceof SpannableString) {
-            return getTextWidths(text.toString(), start, end, widths);
-        }
-        if (text instanceof SpannableStringBuilder) {
-            return ((SpannableStringBuilder)text).getTextWidths(start, end, widths, this);
-        }
-
-        char[] buf = TemporaryBuffer.obtain(end - start);
-        TextUtils.getChars(text, start, end, buf, 0);
-        int result = getTextWidths(buf, 0, end - start, widths);
-        TemporaryBuffer.recycle(buf);
-        return result;
-    }
-
-    @Override
-    public int getTextWidths(String text, float[] widths) {
-        return super.getTextWidths(text, widths);
-    }
-
-    /**
-     * Return the path (outline) for the specified text.
-     * Note: just like Canvas.drawText, this will respect the Align setting in
-     * the paint.
-     *
-     * @param text     The text to retrieve the path from
-     * @param index    The index of the first character in text
-     * @param count    The number of characterss starting with index
-     * @param x        The x coordinate of the text's origin
-     * @param y        The y coordinate of the text's origin
-     * @param path     The path to receive the data describing the text. Must
-     *                 be allocated by the caller.
-     */
-    @Override
-    public void getTextPath(char[] text, int index, int count,
-                            float x, float y, Path path) {
-
-        // TODO this is the ORIGINAL implementation. REPLACE AS NEEDED OR REMOVE
-
-        if ((index | count) < 0 || index + count > text.length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-
-        // TODO native_getTextPath(mNativePaint, text, index, count, x, y, path.ni());
-
-        throw new UnsupportedOperationException("IMPLEMENT AS NEEDED");
-    }
-
-    /**
-     * Return the path (outline) for the specified text.
-     * Note: just like Canvas.drawText, this will respect the Align setting
-     * in the paint.
-     *
-     * @param text  The text to retrieve the path from
-     * @param start The first character in the text
-     * @param end   1 past the last charcter in the text
-     * @param x     The x coordinate of the text's origin
-     * @param y     The y coordinate of the text's origin
-     * @param path  The path to receive the data describing the text. Must
-     *              be allocated by the caller.
-     */
-    @Override
-    public void getTextPath(String text, int start, int end,
-                            float x, float y, Path path) {
-        if ((start | end | (end - start) | (text.length() - end)) < 0) {
-            throw new IndexOutOfBoundsException();
-        }
-
-        getTextPath(text.toCharArray(), start, end - start, x, y, path);
-    }
-
-    /**
-     * Return in bounds (allocated by the caller) the smallest rectangle that
-     * encloses all of the characters, with an implied origin at (0,0).
-     *
-     * @param text  String to measure and return its bounds
-     * @param start Index of the first char in the string to measure
-     * @param end   1 past the last char in the string measure
-     * @param bounds Returns the unioned bounds of all the text. Must be
-     *               allocated by the caller.
-     */
-    @Override
-    public void getTextBounds(String text, int start, int end, Rect bounds) {
-        if ((start | end | (end - start) | (text.length() - end)) < 0) {
-            throw new IndexOutOfBoundsException();
-        }
-        if (bounds == null) {
-            throw new NullPointerException("need bounds Rect");
-        }
-
-        getTextBounds(text.toCharArray(), start, end - start, bounds);
-    }
-
-    /**
-     * Return in bounds (allocated by the caller) the smallest rectangle that
-     * encloses all of the characters, with an implied origin at (0,0).
-     *
-     * @param text  Array of chars to measure and return their unioned bounds
-     * @param index Index of the first char in the array to measure
-     * @param count The number of chars, beginning at index, to measure
-     * @param bounds Returns the unioned bounds of all the text. Must be
-     *               allocated by the caller.
-     */
-    @Override
-    public void getTextBounds(char[] text, int index, int count, Rect bounds) {
-        // FIXME
-        if (mFonts.size() > 0) {
-            if ((index | count) < 0 || index + count > text.length) {
-                throw new ArrayIndexOutOfBoundsException();
-            }
-            if (bounds == null) {
-                throw new NullPointerException("need bounds Rect");
-            }
-
-            FontInfo mainInfo = mFonts.get(0);
-
-            Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count, mFontContext);
-            bounds.set(0, 0, (int)rect.getWidth(), (int)rect.getHeight());
-        }
-    }
-
-    public static void finalizer(int foo) {
-        // pass
-    }
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java
new file mode 100644
index 0000000..71d346a
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate implementing the native methods of android.graphics.PaintFlagsDrawFilter
+ *
+ * Through the layoutlib_create tool, the original native methods of PaintFlagsDrawFilter have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original PaintFlagsDrawFilter class.
+ *
+ * Because this extends {@link DrawFilter_Delegate}, there's no need to use a
+ * {@link DelegateManager}, as all the DrawFilter classes will be added to the manager owned by
+ * {@link DrawFilter_Delegate}.
+ *
+ * @see DrawFilter_Delegate
+ *
+ */
+public class PaintFlagsDrawFilter_Delegate extends DrawFilter_Delegate {
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public boolean isSupported() {
+        return false;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        return "Paint Flags Draw Filters are not supported.";
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeConstructor(int clearBits, int setBits) {
+        PaintFlagsDrawFilter_Delegate newDelegate = new PaintFlagsDrawFilter_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
new file mode 100644
index 0000000..373f482
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -0,0 +1,1240 @@
+/*
+ * Copyright (C) 2010 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 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.graphics.Paint.FontMetrics;
+import android.graphics.Paint.FontMetricsInt;
+import android.text.TextUtils;
+
+import java.awt.BasicStroke;
+import java.awt.Font;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.Toolkit;
+import java.awt.font.FontRenderContext;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Paint
+ *
+ * Through the layoutlib_create tool, the original native methods of Paint have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Paint class.
+ *
+ * @see DelegateManager
+ *
+ */
+public class Paint_Delegate {
+
+    /**
+     * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
+     */
+    /*package*/ static final class FontInfo {
+        Font mFont;
+        java.awt.FontMetrics mMetrics;
+    }
+
+    // ---- delegate manager ----
+    private static final DelegateManager<Paint_Delegate> sManager =
+            new DelegateManager<Paint_Delegate>(Paint_Delegate.class);
+
+    // ---- delegate helper data ----
+    private List<FontInfo> mFonts;
+    private final FontRenderContext mFontContext = new FontRenderContext(
+            new AffineTransform(), true, true);
+
+    // ---- delegate data ----
+    private int mFlags;
+    private int mColor;
+    private int mStyle;
+    private int mCap;
+    private int mJoin;
+    private int mTextAlign;
+    private Typeface_Delegate mTypeface;
+    private float mStrokeWidth;
+    private float mStrokeMiter;
+    private float mTextSize;
+    private float mTextScaleX;
+    private float mTextSkewX;
+
+    private Xfermode_Delegate mXfermode;
+    private ColorFilter_Delegate mColorFilter;
+    private Shader_Delegate mShader;
+    private PathEffect_Delegate mPathEffect;
+    private MaskFilter_Delegate mMaskFilter;
+    private Rasterizer_Delegate mRasterizer;
+
+
+    // ---- Public Helper methods ----
+
+    public static Paint_Delegate getDelegate(int native_paint) {
+        return sManager.getDelegate(native_paint);
+    }
+
+    /**
+     * Returns the list of {@link Font} objects. The first item is the main font, the rest
+     * are fall backs for characters not present in the main font.
+     */
+    public List<FontInfo> getFonts() {
+        return mFonts;
+    }
+
+    public boolean isAntiAliased() {
+        return (mFlags & Paint.ANTI_ALIAS_FLAG) != 0;
+    }
+
+    public boolean isFilterBitmap() {
+        return (mFlags & Paint.FILTER_BITMAP_FLAG) != 0;
+    }
+
+    public int getStyle() {
+        return mStyle;
+    }
+
+    public int getColor() {
+        return mColor;
+    }
+
+    public int getAlpha() {
+        return mColor >>> 24;
+    }
+
+    public void setAlpha(int alpha) {
+        mColor = (alpha << 24) | (mColor & 0x00FFFFFF);
+    }
+
+    public int getTextAlign() {
+        return mTextAlign;
+    }
+
+    public float getStrokeWidth() {
+        return mStrokeWidth;
+    }
+
+    /**
+     * returns the value of stroke miter needed by the java api.
+     */
+    public float getJavaStrokeMiter() {
+        float miter = mStrokeMiter * mStrokeWidth;
+        if (miter < 1.f) {
+            miter = 1.f;
+        }
+        return miter;
+    }
+
+    public int getJavaCap() {
+        switch (Paint.sCapArray[mCap]) {
+            case BUTT:
+                return BasicStroke.CAP_BUTT;
+            case ROUND:
+                return BasicStroke.CAP_ROUND;
+            default:
+            case SQUARE:
+                return BasicStroke.CAP_SQUARE;
+        }
+    }
+
+    public int getJavaJoin() {
+        switch (Paint.sJoinArray[mJoin]) {
+            default:
+            case MITER:
+                return BasicStroke.JOIN_MITER;
+            case ROUND:
+                return BasicStroke.JOIN_ROUND;
+            case BEVEL:
+                return BasicStroke.JOIN_BEVEL;
+        }
+    }
+
+    public Stroke getJavaStroke() {
+        if (mPathEffect != null) {
+            if (mPathEffect.isSupported()) {
+                Stroke stroke = mPathEffect.getStroke(this);
+                assert stroke != null;
+                if (stroke != null) {
+                    return stroke;
+                }
+            } else {
+                Bridge.getLog().fidelityWarning(LayoutLog.TAG_PATHEFFECT,
+                        mPathEffect.getSupportMessage(),
+                        null, null /*data*/);
+            }
+        }
+
+        // if no custom stroke as been set, set the default one.
+        return new BasicStroke(
+                    getStrokeWidth(),
+                    getJavaCap(),
+                    getJavaJoin(),
+                    getJavaStrokeMiter());
+    }
+
+    /**
+     * Returns the {@link Xfermode} delegate or null if none have been set
+     *
+     * @return the delegate or null.
+     */
+    public Xfermode_Delegate getXfermode() {
+        return mXfermode;
+    }
+
+    /**
+     * Returns the {@link ColorFilter} delegate or null if none have been set
+     *
+     * @return the delegate or null.
+     */
+    public ColorFilter_Delegate getColorFilter() {
+        return mColorFilter;
+    }
+
+    /**
+     * Returns the {@link Shader} delegate or null if none have been set
+     *
+     * @return the delegate or null.
+     */
+    public Shader_Delegate getShader() {
+        return mShader;
+    }
+
+    /**
+     * Returns the {@link MaskFilter} delegate or null if none have been set
+     *
+     * @return the delegate or null.
+     */
+    public MaskFilter_Delegate getMaskFilter() {
+        return mMaskFilter;
+    }
+
+    /**
+     * Returns the {@link Rasterizer} delegate or null if none have been set
+     *
+     * @return the delegate or null.
+     */
+    public Rasterizer_Delegate getRasterizer() {
+        return mRasterizer;
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int getFlags(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return 0;
+        }
+
+        return delegate.mFlags;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void setFlags(Paint thisPaint, int flags) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.mFlags = flags;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void setFilterBitmap(Paint thisPaint, boolean filter) {
+        setFlag(thisPaint, Paint.FILTER_BITMAP_FLAG, filter);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void setAntiAlias(Paint thisPaint, boolean aa) {
+        setFlag(thisPaint, Paint.ANTI_ALIAS_FLAG, aa);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void setSubpixelText(Paint thisPaint, boolean subpixelText) {
+        setFlag(thisPaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void setUnderlineText(Paint thisPaint, boolean underlineText) {
+        setFlag(thisPaint, Paint.UNDERLINE_TEXT_FLAG, underlineText);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void setStrikeThruText(Paint thisPaint, boolean strikeThruText) {
+        setFlag(thisPaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void setFakeBoldText(Paint thisPaint, boolean fakeBoldText) {
+        setFlag(thisPaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void setDither(Paint thisPaint, boolean dither) {
+        setFlag(thisPaint, Paint.DITHER_FLAG, dither);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void setLinearText(Paint thisPaint, boolean linearText) {
+        setFlag(thisPaint, Paint.LINEAR_TEXT_FLAG, linearText);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int getColor(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return 0;
+        }
+
+        return delegate.mColor;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void setColor(Paint thisPaint, int color) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.mColor = color;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int getAlpha(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return 0;
+        }
+
+        return delegate.getAlpha();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void setAlpha(Paint thisPaint, int a) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.setAlpha(a);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float getStrokeWidth(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return 1.f;
+        }
+
+        return delegate.mStrokeWidth;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void setStrokeWidth(Paint thisPaint, float width) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.mStrokeWidth = width;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float getStrokeMiter(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return 1.f;
+        }
+
+        return delegate.mStrokeMiter;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void setStrokeMiter(Paint thisPaint, float miter) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.mStrokeMiter = miter;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy,
+            int color) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Paint.setShadowLayer is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float getTextSize(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return 1.f;
+        }
+
+        return delegate.mTextSize;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void setTextSize(Paint thisPaint, float textSize) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.mTextSize = textSize;
+        delegate.updateFontObject();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float getTextScaleX(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return 1.f;
+        }
+
+        return delegate.mTextScaleX;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.mTextScaleX = scaleX;
+        delegate.updateFontObject();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float getTextSkewX(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return 1.f;
+        }
+
+        return delegate.mTextSkewX;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.mTextSkewX = skewX;
+        delegate.updateFontObject();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float ascent(Paint thisPaint) {
+        // get the delegate
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return 0;
+        }
+
+        if (delegate.mFonts.size() > 0) {
+            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
+            // Android expects negative ascent so we invert the value from Java.
+            return - javaMetrics.getAscent();
+        }
+
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float descent(Paint thisPaint) {
+        // get the delegate
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return 0;
+        }
+
+        if (delegate.mFonts.size() > 0) {
+            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
+            return javaMetrics.getDescent();
+        }
+
+        return 0;
+
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) {
+        // get the delegate
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return 0;
+        }
+
+        return delegate.getFontMetrics(metrics);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) {
+        // get the delegate
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return 0;
+        }
+
+        if (delegate.mFonts.size() > 0) {
+            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
+            if (fmi != null) {
+                // Android expects negative ascent so we invert the value from Java.
+                fmi.top = - javaMetrics.getMaxAscent();
+                fmi.ascent = - javaMetrics.getAscent();
+                fmi.descent = javaMetrics.getDescent();
+                fmi.bottom = javaMetrics.getMaxDescent();
+                fmi.leading = javaMetrics.getLeading();
+            }
+
+            return javaMetrics.getHeight();
+        }
+
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index,
+            int count) {
+        // get the delegate
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return 0;
+        }
+
+        return delegate.measureText(text, index, count);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end) {
+        return native_measureText(thisPaint, text.toCharArray(), start, end - start);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float native_measureText(Paint thisPaint, String text) {
+        return native_measureText(thisPaint, text.toCharArray(), 0, text.length());
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count,
+            float maxWidth, float[] measuredWidth) {
+
+        // get the delegate
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return 0;
+        }
+
+        int inc = count > 0 ? 1 : -1;
+
+        int measureIndex = 0;
+        float measureAcc = 0;
+        for (int i = index; i != index + count; i += inc, measureIndex++) {
+            int start, end;
+            if (i < index) {
+                start = i;
+                end = index;
+            } else {
+                start = index;
+                end = i;
+            }
+
+            // measure from start to end
+            float res = delegate.measureText(text, start, end - start + 1);
+
+            if (measuredWidth != null) {
+                measuredWidth[measureIndex] = res;
+            }
+
+            measureAcc += res;
+            if (res > maxWidth) {
+                // we should not return this char index, but since it's 0-based
+                // and we need to return a count, we simply return measureIndex;
+                return measureIndex;
+            }
+
+        }
+
+        return measureIndex;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards,
+            float maxWidth, float[] measuredWidth) {
+        return native_breakText(thisPaint, text.toCharArray(), 0, text.length(), maxWidth,
+                measuredWidth);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_init() {
+        Paint_Delegate newDelegate = new Paint_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_initWithPaint(int paint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(paint);
+        if (delegate == null) {
+            return 0;
+        }
+
+        Paint_Delegate newDelegate = new Paint_Delegate(delegate);
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_reset(int native_object) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.reset();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_set(int native_dst, int native_src) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate_dst = sManager.getDelegate(native_dst);
+        if (delegate_dst == null) {
+            return;
+        }
+
+        // get the delegate from the native int.
+        Paint_Delegate delegate_src = sManager.getDelegate(native_src);
+        if (delegate_src == null) {
+            return;
+        }
+
+        delegate_dst.set(delegate_src);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_getStyle(int native_object) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return 0;
+        }
+
+        return delegate.mStyle;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setStyle(int native_object, int style) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.mStyle = style;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_getStrokeCap(int native_object) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return 0;
+        }
+
+        return delegate.mCap;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setStrokeCap(int native_object, int cap) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.mCap = cap;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_getStrokeJoin(int native_object) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return 0;
+        }
+
+        return delegate.mJoin;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setStrokeJoin(int native_object, int join) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.mJoin = join;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_getFillPath(int native_object, int src, int dst) {
+        Paint_Delegate paint = sManager.getDelegate(native_object);
+        if (paint == null) {
+            return false;
+        }
+
+        Path_Delegate srcPath = Path_Delegate.getDelegate(src);
+        if (srcPath == null) {
+            return true;
+        }
+
+        Path_Delegate dstPath = Path_Delegate.getDelegate(dst);
+        if (dstPath == null) {
+            return true;
+        }
+
+        Stroke stroke = paint.getJavaStroke();
+        Shape strokeShape = stroke.createStrokedShape(srcPath.getJavaShape());
+
+        dstPath.setJavaShape(strokeShape);
+
+        // FIXME figure out the return value?
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_setShader(int native_object, int shader) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return shader;
+        }
+
+        delegate.mShader = Shader_Delegate.getDelegate(shader);
+
+        return shader;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_setColorFilter(int native_object, int filter) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return filter;
+        }
+
+        delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);;
+
+        // since none of those are supported, display a fidelity warning right away
+        if (delegate.mColorFilter != null && delegate.mColorFilter.isSupported() == false) {
+            Bridge.getLog().fidelityWarning(LayoutLog.TAG_COLORFILTER,
+                    delegate.mColorFilter.getSupportMessage(), null, null /*data*/);
+        }
+
+        return filter;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_setXfermode(int native_object, int xfermode) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return xfermode;
+        }
+
+        delegate.mXfermode = Xfermode_Delegate.getDelegate(xfermode);
+
+        return xfermode;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_setPathEffect(int native_object, int effect) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return effect;
+        }
+
+        delegate.mPathEffect = PathEffect_Delegate.getDelegate(effect);
+
+        return effect;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_setMaskFilter(int native_object, int maskfilter) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return maskfilter;
+        }
+
+        delegate.mMaskFilter = MaskFilter_Delegate.getDelegate(maskfilter);
+
+        // since none of those are supported, display a fidelity warning right away
+        if (delegate.mMaskFilter != null && delegate.mMaskFilter.isSupported() == false) {
+            Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
+                    delegate.mMaskFilter.getSupportMessage(), null, null /*data*/);
+        }
+
+        return maskfilter;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_setTypeface(int native_object, int typeface) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return 0;
+        }
+
+        delegate.mTypeface = Typeface_Delegate.getDelegate(typeface);
+        delegate.updateFontObject();
+        return typeface;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_setRasterizer(int native_object, int rasterizer) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return rasterizer;
+        }
+
+        delegate.mRasterizer = Rasterizer_Delegate.getDelegate(rasterizer);
+
+        // since none of those are supported, display a fidelity warning right away
+        if (delegate.mRasterizer != null && delegate.mRasterizer.isSupported() == false) {
+            Bridge.getLog().fidelityWarning(LayoutLog.TAG_RASTERIZER,
+                    delegate.mRasterizer.getSupportMessage(), null, null /*data*/);
+        }
+
+        return rasterizer;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_getTextAlign(int native_object) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return 0;
+        }
+
+        return delegate.mTextAlign;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setTextAlign(int native_object, int align) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.mTextAlign = align;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float native_getFontMetrics(int native_paint, FontMetrics metrics) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_paint);
+        if (delegate == null) {
+            return 0.f;
+        }
+
+        return delegate.getFontMetrics(metrics);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_getTextWidths(int native_object, char[] text, int index,
+            int count, float[] widths) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return 0;
+        }
+
+        if (delegate.mFonts.size() > 0) {
+            // FIXME: handle multi-char characters (see measureText)
+            float totalAdvance = 0;
+            for (int i = 0; i < count; i++) {
+                char c = text[i + index];
+                boolean found = false;
+                for (FontInfo info : delegate.mFonts) {
+                    if (info.mFont.canDisplay(c)) {
+                        float adv = info.mMetrics.charWidth(c);
+                        totalAdvance += adv;
+                        if (widths != null) {
+                            widths[i] = adv;
+                        }
+
+                        found = true;
+                        break;
+                    }
+                }
+
+                if (found == false) {
+                    // no advance for this char.
+                    if (widths != null) {
+                        widths[i] = 0.f;
+                    }
+                }
+            }
+
+            return (int) totalAdvance;
+        }
+
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_getTextWidths(int native_object, String text, int start,
+            int end, float[] widths) {
+        return native_getTextWidths(native_object, text.toCharArray(), start, end - start, widths);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float native_getTextRunAdvances(int native_object,
+            char[] text, int index, int count, int contextIndex, int contextCount,
+            int flags, float[] advances, int advancesIndex) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return 0.f;
+        }
+
+        if (delegate.mFonts.size() > 0) {
+            // FIXME: handle multi-char characters (see measureText)
+            float totalAdvance = 0;
+            for (int i = 0; i < count; i++) {
+                char c = text[i + index];
+                boolean found = false;
+                for (FontInfo info : delegate.mFonts) {
+                    if (info.mFont.canDisplay(c)) {
+                        float adv = info.mMetrics.charWidth(c);
+                        totalAdvance += adv;
+                        if (advances != null) {
+                            advances[i] = adv;
+                        }
+
+                        found = true;
+                        break;
+                    }
+                }
+
+                if (found == false) {
+                    // no advance for this char.
+                    if (advances != null) {
+                        advances[i] = 0.f;
+                    }
+                }
+            }
+
+            return totalAdvance;
+        }
+
+        return 0;
+
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float native_getTextRunAdvances(int native_object,
+            String text, int start, int end, int contextStart, int contextEnd,
+            int flags, float[] advances, int advancesIndex) {
+        // FIXME: support contextStart, contextEnd and direction flag
+        int count = end - start;
+        char[] buffer = TemporaryBuffer.obtain(count);
+        TextUtils.getChars(text, start, end, buffer, 0);
+
+        return native_getTextRunAdvances(native_object, buffer, 0, count, contextStart,
+                contextEnd - contextStart, flags, advances, advancesIndex);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, char[] text,
+            int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Paint.getTextRunCursor is not supported.", null, null /*data*/);
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, String text,
+            int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Paint.getTextRunCursor is not supported.", null, null /*data*/);
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
+                char[] text, int index, int count, float x, float y, int path) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Paint.getTextPath is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
+            String text, int start, int end, float x, float y, int path) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Paint.getTextPath is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeGetStringBounds(int nativePaint, String text, int start,
+            int end, Rect bounds) {
+        nativeGetCharArrayBounds(nativePaint, text.toCharArray(), start, end - start, bounds);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeGetCharArrayBounds(int nativePaint, char[] text, int index,
+            int count, Rect bounds) {
+
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
+        if (delegate == null) {
+            return;
+        }
+
+        // FIXME should test if the main font can display all those characters.
+        // See MeasureText
+        if (delegate.mFonts.size() > 0) {
+            FontInfo mainInfo = delegate.mFonts.get(0);
+
+            Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count,
+                    delegate.mFontContext);
+            bounds.set(0, 0, (int) rect.getWidth(), (int) rect.getHeight());
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void finalizer(int nativePaint) {
+        sManager.removeJavaReferenceFor(nativePaint);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    /*package*/ Paint_Delegate() {
+        reset();
+    }
+
+    private Paint_Delegate(Paint_Delegate paint) {
+        set(paint);
+    }
+
+    private void set(Paint_Delegate paint) {
+        mFlags = paint.mFlags;
+        mColor = paint.mColor;
+        mStyle = paint.mStyle;
+        mCap = paint.mCap;
+        mJoin = paint.mJoin;
+        mTextAlign = paint.mTextAlign;
+        mTypeface = paint.mTypeface;
+        mStrokeWidth = paint.mStrokeWidth;
+        mStrokeMiter = paint.mStrokeMiter;
+        mTextSize = paint.mTextSize;
+        mTextScaleX = paint.mTextScaleX;
+        mTextSkewX = paint.mTextSkewX;
+        mXfermode = paint.mXfermode;
+        mColorFilter = paint.mColorFilter;
+        mShader = paint.mShader;
+        mPathEffect = paint.mPathEffect;
+        mMaskFilter = paint.mMaskFilter;
+        mRasterizer = paint.mRasterizer;
+        updateFontObject();
+    }
+
+    private void reset() {
+        mFlags = Paint.DEFAULT_PAINT_FLAGS;
+        mColor = 0xFF000000;
+        mStyle = Paint.Style.FILL.nativeInt;
+        mCap = Paint.Cap.BUTT.nativeInt;
+        mJoin = Paint.Join.MITER.nativeInt;
+        mTextAlign = 0;
+        mTypeface = Typeface_Delegate.getDelegate(Typeface.sDefaults[0].native_instance);
+        mStrokeWidth = 1.f;
+        mStrokeMiter = 4.f;
+        mTextSize = 20.f;
+        mTextScaleX = 1.f;
+        mTextSkewX = 0.f;
+        mXfermode = null;
+        mColorFilter = null;
+        mShader = null;
+        mPathEffect = null;
+        mMaskFilter = null;
+        mRasterizer = null;
+        updateFontObject();
+    }
+
+    /**
+     * Update the {@link Font} object from the typeface, text size and scaling
+     */
+    @SuppressWarnings("deprecation")
+    private void updateFontObject() {
+        if (mTypeface != null) {
+            // Get the fonts from the TypeFace object.
+            List<Font> fonts = mTypeface.getFonts();
+
+            // create new font objects as well as FontMetrics, based on the current text size
+            // and skew info.
+            ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
+            for (Font font : fonts) {
+                FontInfo info = new FontInfo();
+                info.mFont = font.deriveFont(mTextSize);
+                if (mTextScaleX != 1.0 || mTextSkewX != 0) {
+                    // TODO: support skew
+                    info.mFont = info.mFont.deriveFont(new AffineTransform(
+                            mTextScaleX, mTextSkewX, 0, 0, 1, 0));
+                }
+                info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
+
+                infoList.add(info);
+            }
+
+            mFonts = Collections.unmodifiableList(infoList);
+        }
+    }
+
+    /*package*/ float measureText(char[] text, int index, int count) {
+
+        // WARNING: the logic in this method is similar to Canvas_Delegate.native_drawText
+        // Any change to this method should be reflected there as well
+
+        if (mFonts.size() > 0) {
+            FontInfo mainFont = mFonts.get(0);
+            int i = index;
+            int lastIndex = index + count;
+            float total = 0f;
+            while (i < lastIndex) {
+                // always start with the main font.
+                int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
+                if (upTo == -1) {
+                    // shortcut to exit
+                    return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i);
+                } else if (upTo > 0) {
+                    total += mainFont.mMetrics.charsWidth(text, i, upTo - i);
+                    i = upTo;
+                    // don't call continue at this point. Since it is certain the main font
+                    // cannot display the font a index upTo (now ==i), we move on to the
+                    // fallback fonts directly.
+                }
+
+                // no char supported, attempt to read the next char(s) with the
+                // fallback font. In this case we only test the first character
+                // and then go back to test with the main font.
+                // Special test for 2-char characters.
+                boolean foundFont = false;
+                for (int f = 1 ; f < mFonts.size() ; f++) {
+                    FontInfo fontInfo = mFonts.get(f);
+
+                    // need to check that the font can display the character. We test
+                    // differently if the char is a high surrogate.
+                    int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
+                    upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
+                    if (upTo == -1) {
+                        total += fontInfo.mMetrics.charsWidth(text, i, charCount);
+                        i += charCount;
+                        foundFont = true;
+                        break;
+
+                    }
+                }
+
+                // in case no font can display the char, measure it with the main font.
+                if (foundFont == false) {
+                    int size = Character.isHighSurrogate(text[i]) ? 2 : 1;
+                    total += mainFont.mMetrics.charsWidth(text, i, size);
+                    i += size;
+                }
+            }
+
+            return total;
+        }
+
+        return 0;
+    }
+
+    private float getFontMetrics(FontMetrics metrics) {
+        if (mFonts.size() > 0) {
+            java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
+            if (metrics != null) {
+                // Android expects negative ascent so we invert the value from Java.
+                metrics.top = - javaMetrics.getMaxAscent();
+                metrics.ascent = - javaMetrics.getAscent();
+                metrics.descent = javaMetrics.getDescent();
+                metrics.bottom = javaMetrics.getMaxDescent();
+                metrics.leading = javaMetrics.getLeading();
+            }
+
+            return javaMetrics.getHeight();
+        }
+
+        return 0;
+    }
+
+
+
+    private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return;
+        }
+
+        if (flagValue) {
+            delegate.mFlags |= flagMask;
+        } else {
+            delegate.mFlags &= ~flagMask;
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path.java b/tools/layoutlib/bridge/src/android/graphics/Path.java
deleted file mode 100644
index 12d2cde..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Path.java
+++ /dev/null
@@ -1,611 +0,0 @@
-/*
- * Copyright (C) 2006 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.Shape;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Ellipse2D;
-import java.awt.geom.GeneralPath;
-import java.awt.geom.PathIterator;
-import java.awt.geom.Rectangle2D;
-
-/**
- * The Path class encapsulates compound (multiple contour) geometric paths
- * consisting of straight line segments, quadratic curves, and cubic curves.
- * It can be drawn with canvas.drawPath(path, paint), either filled or stroked
- * (based on the paint's Style), or it can be used for clipping or to draw
- * text on a path.
- */
-public class Path {
-    
-    private FillType mFillType = FillType.WINDING;
-    private GeneralPath mPath = new GeneralPath();
-    
-    private float mLastX = 0;
-    private float mLastY = 0;
-    
-    //---------- Custom methods ----------
-
-    public Shape getAwtShape() {
-        return mPath;
-    }
-
-    //----------
-
-    /**
-     * Create an empty path
-     */
-    public Path() {
-    }
-
-    /**
-     * Create a new path, copying the contents from the src path.
-     *
-     * @param src The path to copy from when initializing the new path
-     */
-    public Path(Path src) {
-        mPath.append(src.mPath, false /* connect */);
-    }
-    
-    /**
-     * Clear any lines and curves from the path, making it empty.
-     * This does NOT change the fill-type setting.
-     */
-    public void reset() {
-        mPath = new GeneralPath();
-    }
-
-    /**
-     * Rewinds the path: clears any lines and curves from the path but
-     * keeps the internal data structure for faster reuse.
-     */
-    public void rewind() {
-        // FIXME
-        throw new UnsupportedOperationException();
-    }
-
-    /** Replace the contents of this with the contents of src.
-    */
-    public void set(Path src) {
-        mPath.append(src.mPath, false /* connect */);
-    }
-
-    /** Enum for the ways a path may be filled
-    */
-    public enum FillType {
-        // these must match the values in SkPath.h
-        WINDING         (GeneralPath.WIND_NON_ZERO, false),
-        EVEN_ODD        (GeneralPath.WIND_EVEN_ODD, false),
-        INVERSE_WINDING (GeneralPath.WIND_NON_ZERO, true),
-        INVERSE_EVEN_ODD(GeneralPath.WIND_EVEN_ODD, true);
-        
-        FillType(int rule, boolean inverse) {
-            this.rule = rule;
-            this.inverse = inverse;
-        }
-
-        final int rule;
-        final boolean inverse;
-    }
-    
-    /**
-     * Return the path's fill type. This defines how "inside" is
-     * computed. The default value is WINDING.
-     *
-     * @return the path's fill type
-     */
-    public FillType getFillType() {
-        return mFillType;
-    }
-
-    /**
-     * Set the path's fill type. This defines how "inside" is computed.
-     *
-     * @param ft The new fill type for this path
-     */
-    public void setFillType(FillType ft) {
-        mFillType = ft;
-        mPath.setWindingRule(ft.rule);
-    }
-    
-    /**
-     * Returns true if the filltype is one of the INVERSE variants
-     *
-     * @return true if the filltype is one of the INVERSE variants
-     */
-    public boolean isInverseFillType() {
-        return mFillType.inverse;
-    }
-    
-    /**
-     * Toggles the INVERSE state of the filltype
-     */
-    public void toggleInverseFillType() {
-        switch (mFillType) {
-            case WINDING:
-                mFillType = FillType.INVERSE_WINDING;
-                break;
-            case EVEN_ODD:
-                mFillType = FillType.INVERSE_EVEN_ODD;
-                break;
-            case INVERSE_WINDING:
-                mFillType = FillType.WINDING;
-                break;
-            case INVERSE_EVEN_ODD:
-                mFillType = FillType.EVEN_ODD;
-                break;
-        }
-    }
-    
-    /**
-     * Returns true if the path is empty (contains no lines or curves)
-     *
-     * @return true if the path is empty (contains no lines or curves)
-     */
-    public boolean isEmpty() {
-        return mPath.getCurrentPoint() == null;
-    }
-
-    /**
-     * Returns true if the path specifies a rectangle. If so, and if rect is
-     * not null, set rect to the bounds of the path. If the path does not
-     * specify a rectangle, return false and ignore rect.
-     *
-     * @param rect If not null, returns the bounds of the path if it specifies
-     *             a rectangle
-     * @return     true if the path specifies a rectangle
-     */
-    public boolean isRect(RectF rect) {
-        // FIXME
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * Compute the bounds of the path, and write the answer into bounds. If the
-     * path contains 0 or 1 points, the bounds is set to (0,0,0,0)
-     *
-     * @param bounds Returns the computed bounds of the path
-     * @param exact If true, return the exact (but slower) bounds, else return
-     *              just the bounds of all control points
-     */
-    public void computeBounds(RectF bounds, boolean exact) {
-        Rectangle2D rect = mPath.getBounds2D();
-        bounds.left = (float)rect.getMinX();
-        bounds.right = (float)rect.getMaxX();
-        bounds.top = (float)rect.getMinY();
-        bounds.bottom = (float)rect.getMaxY();
-    }
-
-    /**
-     * Hint to the path to prepare for adding more points. This can allow the
-     * path to more efficiently allocate its storage.
-     *
-     * @param extraPtCount The number of extra points that may be added to this
-     *                     path
-     */
-    public void incReserve(int extraPtCount) {
-        // pass
-    }
-
-    /**
-     * Set the beginning of the next contour to the point (x,y).
-     *
-     * @param x The x-coordinate of the start of a new contour
-     * @param y The y-coordinate of the start of a new contour
-     */
-    public void moveTo(float x, float y) {
-        mPath.moveTo(mLastX = x, mLastY = y);
-    }
-
-    /**
-     * Set the beginning of the next contour relative to the last point on the
-     * previous contour. If there is no previous contour, this is treated the
-     * same as moveTo().
-     *
-     * @param dx The amount to add to the x-coordinate of the end of the
-     *           previous contour, to specify the start of a new contour
-     * @param dy The amount to add to the y-coordinate of the end of the
-     *           previous contour, to specify the start of a new contour
-     */
-    public void rMoveTo(float dx, float dy) {
-        dx += mLastX;
-        dy += mLastY;
-        mPath.moveTo(mLastX = dx, mLastY = dy);
-    }
-
-    /**
-     * Add a line from the last point to the specified point (x,y).
-     * If no moveTo() call has been made for this contour, the first point is
-     * automatically set to (0,0).
-     *
-     * @param x The x-coordinate of the end of a line
-     * @param y The y-coordinate of the end of a line
-     */
-    public void lineTo(float x, float y) {
-        mPath.lineTo(mLastX = x, mLastY = y);
-    }
-
-    /**
-     * Same as lineTo, but the coordinates are considered relative to the last
-     * point on this contour. If there is no previous point, then a moveTo(0,0)
-     * is inserted automatically.
-     *
-     * @param dx The amount to add to the x-coordinate of the previous point on
-     *           this contour, to specify a line
-     * @param dy The amount to add to the y-coordinate of the previous point on
-     *           this contour, to specify a line
-     */
-    public void rLineTo(float dx, float dy) {
-        if (isEmpty()) {
-            mPath.moveTo(mLastX = 0, mLastY = 0);
-        }
-        dx += mLastX;
-        dy += mLastY;
-        mPath.lineTo(mLastX = dx, mLastY = dy);
-    }
-
-    /**
-     * Add a quadratic bezier from the last point, approaching control point
-     * (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
-     * this contour, the first point is automatically set to (0,0).
-     *
-     * @param x1 The x-coordinate of the control point on a quadratic curve
-     * @param y1 The y-coordinate of the control point on a quadratic curve
-     * @param x2 The x-coordinate of the end point on a quadratic curve
-     * @param y2 The y-coordinate of the end point on a quadratic curve
-     */
-    public void quadTo(float x1, float y1, float x2, float y2) {
-        mPath.quadTo(x1, y1, mLastX = x2, mLastY = y2);
-    }
-
-    /**
-     * Same as quadTo, but the coordinates are considered relative to the last
-     * point on this contour. If there is no previous point, then a moveTo(0,0)
-     * is inserted automatically.
-     *
-     * @param dx1 The amount to add to the x-coordinate of the last point on
-     *            this contour, for the control point of a quadratic curve
-     * @param dy1 The amount to add to the y-coordinate of the last point on
-     *            this contour, for the control point of a quadratic curve
-     * @param dx2 The amount to add to the x-coordinate of the last point on
-     *            this contour, for the end point of a quadratic curve
-     * @param dy2 The amount to add to the y-coordinate of the last point on
-     *            this contour, for the end point of a quadratic curve
-     */
-    public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
-        if (isEmpty()) {
-            mPath.moveTo(mLastX = 0, mLastY = 0);
-        }
-        dx1 += mLastX;
-        dy1 += mLastY;
-        dx2 += mLastX;
-        dy2 += mLastY;
-        mPath.quadTo(dx1, dy1, mLastX = dx2, mLastY = dy2);
-    }
-
-    /**
-     * Add a cubic bezier from the last point, approaching control points
-     * (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
-     * made for this contour, the first point is automatically set to (0,0).
-     *
-     * @param x1 The x-coordinate of the 1st control point on a cubic curve
-     * @param y1 The y-coordinate of the 1st control point on a cubic curve
-     * @param x2 The x-coordinate of the 2nd control point on a cubic curve
-     * @param y2 The y-coordinate of the 2nd control point on a cubic curve
-     * @param x3 The x-coordinate of the end point on a cubic curve
-     * @param y3 The y-coordinate of the end point on a cubic curve
-     */
-    public void cubicTo(float x1, float y1, float x2, float y2,
-                        float x3, float y3) {
-        mPath.curveTo(x1, y1, x2, y2, mLastX = x3, mLastY = y3);
-    }
-
-    /**
-     * Same as cubicTo, but the coordinates are considered relative to the
-     * current point on this contour. If there is no previous point, then a
-     * moveTo(0,0) is inserted automatically.
-     */
-    public void rCubicTo(float dx1, float dy1, float dx2, float dy2,
-                         float dx3, float dy3) {
-        if (isEmpty()) {
-            mPath.moveTo(mLastX = 0, mLastY = 0);
-        }
-        dx1 += mLastX;
-        dy1 += mLastY;
-        dx2 += mLastX;
-        dy2 += mLastY;
-        dx3 += mLastX;
-        dy3 += mLastY;
-        mPath.curveTo(dx1, dy1, dx2, dy2, mLastX = dx3, mLastY = dy3);
-    }
-
-    /**
-     * Append the specified arc to the path as a new contour. If the start of
-     * the path is different from the path's current last point, then an
-     * automatic lineTo() is added to connect the current contour to the
-     * start of the arc. However, if the path is empty, then we call moveTo()
-     * with the first point of the arc. The sweep angle is tread mod 360.
-     *
-     * @param oval        The bounds of oval defining shape and size of the arc
-     * @param startAngle  Starting angle (in degrees) where the arc begins
-     * @param sweepAngle  Sweep angle (in degrees) measured clockwise, treated
-     *                    mod 360.
-     * @param forceMoveTo If true, always begin a new contour with the arc
-     */
-    public void arcTo(RectF oval, float startAngle, float sweepAngle,
-                      boolean forceMoveTo) {
-        throw new UnsupportedOperationException();
-    }
-    
-    /**
-     * Append the specified arc to the path as a new contour. If the start of
-     * the path is different from the path's current last point, then an
-     * automatic lineTo() is added to connect the current contour to the
-     * start of the arc. However, if the path is empty, then we call moveTo()
-     * with the first point of the arc.
-     *
-     * @param oval        The bounds of oval defining shape and size of the arc
-     * @param startAngle  Starting angle (in degrees) where the arc begins
-     * @param sweepAngle  Sweep angle (in degrees) measured clockwise
-     */
-    public void arcTo(RectF oval, float startAngle, float sweepAngle) {
-        throw new UnsupportedOperationException();
-    }
-    
-    /**
-     * Close the current contour. If the current point is not equal to the
-     * first point of the contour, a line segment is automatically added.
-     */
-    public void close() {
-        mPath.closePath();
-    }
-
-    /**
-     * Specifies how closed shapes (e.g. rects, ovals) are oriented when they
-     * are added to a path.
-     */
-    public enum Direction {
-        /** clockwise */
-        CW  (0),    // must match enum in SkPath.h
-        /** counter-clockwise */
-        CCW (1);    // must match enum in SkPath.h
-        
-        Direction(int ni) {
-            nativeInt = ni;
-        }
-        final int nativeInt;
-    }
-    
-    /**
-     * Add a closed rectangle contour to the path
-     *
-     * @param rect The rectangle to add as a closed contour to the path
-     * @param dir  The direction to wind the rectangle's contour
-     */
-    public void addRect(RectF rect, Direction dir) {
-        if (rect == null) {
-            throw new NullPointerException("need rect parameter");
-        }
-        
-        addRect(rect.left, rect.top, rect.right, rect.bottom, dir);
-    }
-
-    /**
-     * Add a closed rectangle contour to the path
-     *
-     * @param left   The left side of a rectangle to add to the path
-     * @param top    The top of a rectangle to add to the path
-     * @param right  The right side of a rectangle to add to the path
-     * @param bottom The bottom of a rectangle to add to the path
-     * @param dir    The direction to wind the rectangle's contour
-     */
-    public void addRect(float left, float top, float right, float bottom,
-                        Direction dir) {
-        moveTo(left, top);
-
-        switch (dir) {
-            case CW:
-                lineTo(right, top);
-                lineTo(right, bottom);
-                lineTo(left, bottom);
-                break;
-            case CCW:
-                lineTo(left, bottom);
-                lineTo(right, bottom);
-                lineTo(right, top);
-                break;
-        }
-
-        close();
-    }
-
-    /**
-     * Add a closed oval contour to the path
-     *
-     * @param oval The bounds of the oval to add as a closed contour to the path
-     * @param dir  The direction to wind the oval's contour
-     */
-    public void addOval(RectF oval, Direction dir) {
-        if (oval == null) {
-            throw new NullPointerException("need oval parameter");
-        }
-
-        // FIXME Need to support direction
-        Ellipse2D ovalShape = new Ellipse2D.Float(oval.left, oval.top, oval.width(), oval.height());
-        
-        mPath.append(ovalShape, false /* connect */);
-    }
-
-    /**
-     * Add a closed circle contour to the path
-     *
-     * @param x   The x-coordinate of the center of a circle to add to the path
-     * @param y   The y-coordinate of the center of a circle to add to the path
-     * @param radius The radius of a circle to add to the path
-     * @param dir    The direction to wind the circle's contour
-     */
-    public void addCircle(float x, float y, float radius, Direction dir) {
-        // FIXME
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * Add the specified arc to the path as a new contour.
-     *
-     * @param oval The bounds of oval defining the shape and size of the arc
-     * @param startAngle Starting angle (in degrees) where the arc begins
-     * @param sweepAngle Sweep angle (in degrees) measured clockwise
-     */
-    public void addArc(RectF oval, float startAngle, float sweepAngle) {
-        if (oval == null) {
-            throw new NullPointerException("need oval parameter");
-        }
-        // FIXME
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-        * Add a closed round-rectangle contour to the path
-     *
-     * @param rect The bounds of a round-rectangle to add to the path
-     * @param rx   The x-radius of the rounded corners on the round-rectangle
-     * @param ry   The y-radius of the rounded corners on the round-rectangle
-     * @param dir  The direction to wind the round-rectangle's contour
-     */
-    public void addRoundRect(RectF rect, float rx, float ry, Direction dir) {
-        if (rect == null) {
-            throw new NullPointerException("need rect parameter");
-        }
-        // FIXME
-        throw new UnsupportedOperationException();
-    }
-    
-    /**
-     * Add a closed round-rectangle contour to the path. Each corner receives
-     * two radius values [X, Y]. The corners are ordered top-left, top-right,
-     * bottom-right, bottom-left
-     *
-     * @param rect The bounds of a round-rectangle to add to the path
-     * @param radii Array of 8 values, 4 pairs of [X,Y] radii
-     * @param dir  The direction to wind the round-rectangle's contour
-     */
-    public void addRoundRect(RectF rect, float[] radii, Direction dir) {
-        if (rect == null) {
-            throw new NullPointerException("need rect parameter");
-        }
-        if (radii.length < 8) {
-            throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
-        }
-        // FIXME
-        throw new UnsupportedOperationException();
-    }
-    
-    /**
-     * Add a copy of src to the path, offset by (dx,dy)
-     *
-     * @param src The path to add as a new contour
-     * @param dx  The amount to translate the path in X as it is added
-     */
-    public void addPath(Path src, float dx, float dy) {
-        PathIterator iterator = src.mPath.getPathIterator(new AffineTransform(0, 0, dx, 0, 0, dy));
-        mPath.append(iterator, false /* connect */);
-    }
-
-    /**
-     * Add a copy of src to the path
-     *
-     * @param src The path that is appended to the current path
-     */
-    public void addPath(Path src) {
-        addPath(src, 0, 0);
-    }
-
-    /**
-     * Add a copy of src to the path, transformed by matrix
-     *
-     * @param src The path to add as a new contour
-     */
-    public void addPath(Path src, Matrix matrix) {
-        // FIXME
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * Offset the path by (dx,dy), returning true on success
-     *
-     * @param dx  The amount in the X direction to offset the entire path
-     * @param dy  The amount in the Y direction to offset the entire path
-     * @param dst The translated path is written here. If this is null, then
-     *            the original path is modified.
-     */
-    public void offset(float dx, float dy, Path dst) {
-        GeneralPath newPath = new GeneralPath();
-        
-        PathIterator iterator = mPath.getPathIterator(new AffineTransform(0, 0, dx, 0, 0, dy));
-        
-        newPath.append(iterator, false /* connect */);
-        
-        if (dst != null) {
-            dst.mPath = newPath;
-        } else {
-            mPath = newPath;
-        }
-    }
-
-    /**
-     * Offset the path by (dx,dy), returning true on success
-     *
-     * @param dx The amount in the X direction to offset the entire path
-     * @param dy The amount in the Y direction to offset the entire path
-     */
-    public void offset(float dx, float dy) {
-        offset(dx, dy, null /* dst */);
-    }
-
-    /**
-     * Sets the last point of the path.
-     *
-     * @param dx The new X coordinate for the last point
-     * @param dy The new Y coordinate for the last point
-     */
-    public void setLastPoint(float dx, float dy) {
-        mLastX = dx;
-        mLastY = dy;
-    }
-
-    /**
-     * Transform the points in this path by matrix, and write the answer
-     * into dst. If dst is null, then the the original path is modified.
-     *
-     * @param matrix The matrix to apply to the path
-     * @param dst    The transformed path is written here. If dst is null,
-     *               then the the original path is modified
-     */
-    public void transform(Matrix matrix, Path dst) {
-        // FIXME
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * Transform the points in this path by matrix.
-     *
-     * @param matrix The matrix to apply to the path
-     */
-    public void transform(Matrix matrix) {
-        transform(matrix, null /* dst */);
-    }
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/PathDashPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PathDashPathEffect_Delegate.java
new file mode 100644
index 0000000..c448f0e
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/PathDashPathEffect_Delegate.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.awt.Stroke;
+
+/**
+ * Delegate implementing the native methods of android.graphics.PathDashPathEffect
+ *
+ * Through the layoutlib_create tool, the original native methods of PathDashPathEffect have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original PathDashPathEffect class.
+ *
+ * Because this extends {@link PathEffect_Delegate}, there's no need to use a {@link DelegateManager},
+ * as all the Shader classes will be added to the manager owned by {@link PathEffect_Delegate}.
+ *
+ * @see PathEffect_Delegate
+ *
+ */
+public class PathDashPathEffect_Delegate extends PathEffect_Delegate {
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public Stroke getStroke(Paint_Delegate paint) {
+        // FIXME
+        return null;
+    }
+
+    @Override
+    public boolean isSupported() {
+        return false;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        return "Path Dash Path Effects are not supported in Layout Preview mode.";
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreate(int native_path, float advance, float phase,
+            int native_style) {
+        PathDashPathEffect_Delegate newDelegate = new PathDashPathEffect_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java
new file mode 100644
index 0000000..bd2b6de
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.awt.Stroke;
+
+/**
+ * Delegate implementing the native methods of android.graphics.PathEffect
+ *
+ * Through the layoutlib_create tool, the original native methods of PathEffect have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original PathEffect class.
+ *
+ * This also serve as a base class for all PathEffect delegate classes.
+ *
+ * @see DelegateManager
+ *
+ */
+public abstract class PathEffect_Delegate {
+
+    // ---- delegate manager ----
+    protected static final DelegateManager<PathEffect_Delegate> sManager =
+            new DelegateManager<PathEffect_Delegate>(PathEffect_Delegate.class);
+
+    // ---- delegate helper data ----
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    public static PathEffect_Delegate getDelegate(int nativeShader) {
+        return sManager.getDelegate(nativeShader);
+    }
+
+    public abstract Stroke getStroke(Paint_Delegate paint);
+    public abstract boolean isSupported();
+    public abstract String getSupportMessage();
+
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeDestructor(int native_patheffect) {
+        sManager.removeJavaReferenceFor(native_patheffect);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
new file mode 100644
index 0000000..6c9f48f
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
@@ -0,0 +1,761 @@
+/*
+ * Copyright (C) 2010 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 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.graphics.Path.Direction;
+import android.graphics.Path.FillType;
+
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Area;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Path
+ *
+ * Through the layoutlib_create tool, the original native methods of Path have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Path class.
+ *
+ * @see DelegateManager
+ *
+ */
+public final class Path_Delegate {
+
+    // ---- delegate manager ----
+    private static final DelegateManager<Path_Delegate> sManager =
+            new DelegateManager<Path_Delegate>(Path_Delegate.class);
+
+    // ---- delegate data ----
+    private FillType mFillType = FillType.WINDING;
+    private GeneralPath mPath = new GeneralPath();
+
+    private float mLastX = 0;
+    private float mLastY = 0;
+
+    // ---- Public Helper methods ----
+
+    public static Path_Delegate getDelegate(int nPath) {
+        return sManager.getDelegate(nPath);
+    }
+
+    public Shape getJavaShape() {
+        return mPath;
+    }
+
+    public void setJavaShape(Shape shape) {
+        mPath.reset();
+        mPath.append(shape, false /*connect*/);
+    }
+
+    public void reset() {
+        mPath.reset();
+    }
+
+    public void setPathIterator(PathIterator iterator) {
+        mPath.reset();
+        mPath.append(iterator, false /*connect*/);
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int init1() {
+        // create the delegate
+        Path_Delegate newDelegate = new Path_Delegate();
+
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int init2(int nPath) {
+        // create the delegate
+        Path_Delegate newDelegate = new Path_Delegate();
+
+        // get the delegate to copy, which could be null if nPath is 0
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate != null) {
+            newDelegate.set(pathDelegate);
+        }
+
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_reset(int nPath) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        pathDelegate.mPath.reset();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_rewind(int nPath) {
+        // call out to reset since there's nothing to optimize in
+        // terms of data structs.
+        native_reset(nPath);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_set(int native_dst, int native_src) {
+        Path_Delegate pathDstDelegate = sManager.getDelegate(native_dst);
+        if (pathDstDelegate == null) {
+            return;
+        }
+
+        Path_Delegate pathSrcDelegate = sManager.getDelegate(native_src);
+        if (pathSrcDelegate == null) {
+            return;
+        }
+
+        pathDstDelegate.set(pathSrcDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_getFillType(int nPath) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return 0;
+        }
+
+        return pathDelegate.mFillType.nativeInt;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setFillType(int nPath, int ft) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        pathDelegate.mFillType = Path.sFillTypeArray[ft];
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_isEmpty(int nPath) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return true;
+        }
+
+        return pathDelegate.isEmpty();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_isRect(int nPath, RectF rect) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return false;
+        }
+
+        // create an Area that can test if the path is a rect
+        Area area = new Area(pathDelegate.mPath);
+        if (area.isRectangular()) {
+            if (rect != null) {
+                pathDelegate.fillBounds(rect);
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_computeBounds(int nPath, RectF bounds) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        pathDelegate.fillBounds(bounds);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_incReserve(int nPath, int extraPtCount) {
+        // since we use a java2D path, there's no way to pre-allocate new points,
+        // so we do nothing.
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_moveTo(int nPath, float x, float y) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        pathDelegate.moveTo(x, y);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_rMoveTo(int nPath, float dx, float dy) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        pathDelegate.rMoveTo(dx, dy);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_lineTo(int nPath, float x, float y) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        pathDelegate.lineTo(x, y);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_rLineTo(int nPath, float dx, float dy) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        pathDelegate.rLineTo(dx, dy);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_quadTo(int nPath, float x1, float y1, float x2, float y2) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        pathDelegate.quadTo(x1, y1, x2, y2);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_rQuadTo(int nPath, float dx1, float dy1, float dx2, float dy2) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        pathDelegate.rQuadTo(dx1, dy1, dx2, dy2);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_cubicTo(int nPath, float x1, float y1,
+            float x2, float y2, float x3, float y3) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        pathDelegate.cubicTo(x1, y1, x2, y2, x3, y3);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_rCubicTo(int nPath, float x1, float y1,
+            float x2, float y2, float x3, float y3) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        pathDelegate.rCubicTo(x1, y1, x2, y2, x3, y3);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_arcTo(int nPath, RectF oval,
+                    float startAngle, float sweepAngle, boolean forceMoveTo) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        pathDelegate.arcTo(oval, startAngle, sweepAngle, forceMoveTo);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_close(int nPath) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        pathDelegate.close();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_addRect(int nPath, RectF rect, int dir) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        pathDelegate.addRect(rect.left, rect.top, rect.right, rect.bottom, dir);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_addRect(int nPath,
+            float left, float top, float right, float bottom, int dir) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        pathDelegate.addRect(left, top, right, bottom, dir);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_addOval(int nPath, RectF oval, int dir) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Path.addOval is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_addCircle(int nPath, float x, float y, float radius, int dir) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Path.addCircle is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_addArc(int nPath, RectF oval,
+            float startAngle, float sweepAngle) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Path.addArc is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_addRoundRect(int nPath, RectF rect,
+            float rx, float ry, int dir) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Path.addRoundRect is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_addRoundRect(int nPath, RectF r, float[] radii, int dir) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Path.addRoundRect is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_addPath(int nPath, int src, float dx, float dy) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Path.addPath is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_addPath(int nPath, int src) {
+        native_addPath(nPath, src, 0, 0);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_addPath(int nPath, int src, int matrix) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Path.addPath is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_offset(int nPath, float dx, float dy, int dst_path) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        // could be null if the int is 0;
+        Path_Delegate dstDelegate = sManager.getDelegate(dst_path);
+
+        pathDelegate.offset(dx, dy, dstDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_offset(int nPath, float dx, float dy) {
+        native_offset(nPath, dx, dy, 0);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setLastPoint(int nPath, float dx, float dy) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        pathDelegate.mLastX = dx;
+        pathDelegate.mLastY = dy;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_transform(int nPath, int matrix,
+                                                int dst_path) {
+        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix);
+        if (matrixDelegate == null) {
+            return;
+        }
+
+        // this can be null if dst_path is 0
+        Path_Delegate dstDelegate = sManager.getDelegate(dst_path);
+
+        pathDelegate.transform(matrixDelegate, dstDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_transform(int nPath, int matrix) {
+        native_transform(nPath, matrix, 0);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void finalizer(int nPath) {
+        sManager.removeJavaReferenceFor(nPath);
+    }
+
+
+    // ---- Private helper methods ----
+
+    private void set(Path_Delegate delegate) {
+        mPath.reset();
+        setFillType(delegate.mFillType);
+        mPath.append(delegate.mPath, false /*connect*/);
+    }
+
+    private void setFillType(FillType fillType) {
+        mFillType = fillType;
+        mPath.setWindingRule(getWindingRule(fillType));
+    }
+
+    /**
+     * Returns the Java2D winding rules matching a given Android {@link FillType}.
+     * @param type the android fill type
+     * @return the matching java2d winding rule.
+     */
+    private static int getWindingRule(FillType type) {
+        switch (type) {
+            case WINDING:
+            case INVERSE_WINDING:
+                return GeneralPath.WIND_NON_ZERO;
+            case EVEN_ODD:
+            case INVERSE_EVEN_ODD:
+                return GeneralPath.WIND_EVEN_ODD;
+        }
+
+        assert false;
+        throw new IllegalArgumentException();
+    }
+
+    private static Direction getDirection(int direction) {
+        for (Direction d : Direction.values()) {
+            if (direction == d.nativeInt) {
+                return d;
+            }
+        }
+
+        assert false;
+        return null;
+    }
+
+    /**
+     * Returns whether the path is empty.
+     * @return true if the path is empty.
+     */
+    private boolean isEmpty() {
+        return mPath.getCurrentPoint() == null;
+    }
+
+    /**
+     * Fills the given {@link RectF} with the path bounds.
+     * @param bounds the RectF to be filled.
+     */
+    private void fillBounds(RectF bounds) {
+        Rectangle2D rect = mPath.getBounds2D();
+        bounds.left = (float)rect.getMinX();
+        bounds.right = (float)rect.getMaxX();
+        bounds.top = (float)rect.getMinY();
+        bounds.bottom = (float)rect.getMaxY();
+    }
+
+    /**
+     * Set the beginning of the next contour to the point (x,y).
+     *
+     * @param x The x-coordinate of the start of a new contour
+     * @param y The y-coordinate of the start of a new contour
+     */
+    private void moveTo(float x, float y) {
+        mPath.moveTo(mLastX = x, mLastY = y);
+    }
+
+    /**
+     * Set the beginning of the next contour relative to the last point on the
+     * previous contour. If there is no previous contour, this is treated the
+     * same as moveTo().
+     *
+     * @param dx The amount to add to the x-coordinate of the end of the
+     *           previous contour, to specify the start of a new contour
+     * @param dy The amount to add to the y-coordinate of the end of the
+     *           previous contour, to specify the start of a new contour
+     */
+    private void rMoveTo(float dx, float dy) {
+        dx += mLastX;
+        dy += mLastY;
+        mPath.moveTo(mLastX = dx, mLastY = dy);
+    }
+
+    /**
+     * Add a line from the last point to the specified point (x,y).
+     * If no moveTo() call has been made for this contour, the first point is
+     * automatically set to (0,0).
+     *
+     * @param x The x-coordinate of the end of a line
+     * @param y The y-coordinate of the end of a line
+     */
+    private void lineTo(float x, float y) {
+        mPath.lineTo(mLastX = x, mLastY = y);
+    }
+
+    /**
+     * Same as lineTo, but the coordinates are considered relative to the last
+     * point on this contour. If there is no previous point, then a moveTo(0,0)
+     * is inserted automatically.
+     *
+     * @param dx The amount to add to the x-coordinate of the previous point on
+     *           this contour, to specify a line
+     * @param dy The amount to add to the y-coordinate of the previous point on
+     *           this contour, to specify a line
+     */
+    private void rLineTo(float dx, float dy) {
+        if (isEmpty()) {
+            mPath.moveTo(mLastX = 0, mLastY = 0);
+        }
+        dx += mLastX;
+        dy += mLastY;
+        mPath.lineTo(mLastX = dx, mLastY = dy);
+    }
+
+    /**
+     * Add a quadratic bezier from the last point, approaching control point
+     * (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
+     * this contour, the first point is automatically set to (0,0).
+     *
+     * @param x1 The x-coordinate of the control point on a quadratic curve
+     * @param y1 The y-coordinate of the control point on a quadratic curve
+     * @param x2 The x-coordinate of the end point on a quadratic curve
+     * @param y2 The y-coordinate of the end point on a quadratic curve
+     */
+    private void quadTo(float x1, float y1, float x2, float y2) {
+        mPath.quadTo(x1, y1, mLastX = x2, mLastY = y2);
+    }
+
+    /**
+     * Same as quadTo, but the coordinates are considered relative to the last
+     * point on this contour. If there is no previous point, then a moveTo(0,0)
+     * is inserted automatically.
+     *
+     * @param dx1 The amount to add to the x-coordinate of the last point on
+     *            this contour, for the control point of a quadratic curve
+     * @param dy1 The amount to add to the y-coordinate of the last point on
+     *            this contour, for the control point of a quadratic curve
+     * @param dx2 The amount to add to the x-coordinate of the last point on
+     *            this contour, for the end point of a quadratic curve
+     * @param dy2 The amount to add to the y-coordinate of the last point on
+     *            this contour, for the end point of a quadratic curve
+     */
+    private void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
+        if (isEmpty()) {
+            mPath.moveTo(mLastX = 0, mLastY = 0);
+        }
+        dx1 += mLastX;
+        dy1 += mLastY;
+        dx2 += mLastX;
+        dy2 += mLastY;
+        mPath.quadTo(dx1, dy1, mLastX = dx2, mLastY = dy2);
+    }
+
+    /**
+     * Add a cubic bezier from the last point, approaching control points
+     * (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
+     * made for this contour, the first point is automatically set to (0,0).
+     *
+     * @param x1 The x-coordinate of the 1st control point on a cubic curve
+     * @param y1 The y-coordinate of the 1st control point on a cubic curve
+     * @param x2 The x-coordinate of the 2nd control point on a cubic curve
+     * @param y2 The y-coordinate of the 2nd control point on a cubic curve
+     * @param x3 The x-coordinate of the end point on a cubic curve
+     * @param y3 The y-coordinate of the end point on a cubic curve
+     */
+    private void cubicTo(float x1, float y1, float x2, float y2,
+                        float x3, float y3) {
+        mPath.curveTo(x1, y1, x2, y2, mLastX = x3, mLastY = y3);
+    }
+
+    /**
+     * Same as cubicTo, but the coordinates are considered relative to the
+     * current point on this contour. If there is no previous point, then a
+     * moveTo(0,0) is inserted automatically.
+     */
+    private void rCubicTo(float dx1, float dy1, float dx2, float dy2,
+                         float dx3, float dy3) {
+        if (isEmpty()) {
+            mPath.moveTo(mLastX = 0, mLastY = 0);
+        }
+        dx1 += mLastX;
+        dy1 += mLastY;
+        dx2 += mLastX;
+        dy2 += mLastY;
+        dx3 += mLastX;
+        dy3 += mLastY;
+        mPath.curveTo(dx1, dy1, dx2, dy2, mLastX = dx3, mLastY = dy3);
+    }
+
+    /**
+     * Append the specified arc to the path as a new contour. If the start of
+     * the path is different from the path's current last point, then an
+     * automatic lineTo() is added to connect the current contour to the
+     * start of the arc. However, if the path is empty, then we call moveTo()
+     * with the first point of the arc. The sweep angle is tread mod 360.
+     *
+     * @param oval        The bounds of oval defining shape and size of the arc
+     * @param startAngle  Starting angle (in degrees) where the arc begins
+     * @param sweepAngle  Sweep angle (in degrees) measured clockwise, treated
+     *                    mod 360.
+     * @param forceMoveTo If true, always begin a new contour with the arc
+     */
+    private void arcTo(RectF oval, float startAngle, float sweepAngle,
+                      boolean forceMoveTo) {
+        Arc2D arc = new Arc2D.Float(oval.left, oval.top, oval.width(), oval.height(), startAngle,
+                sweepAngle, Arc2D.OPEN);
+        mPath.append(arc, true /*connect*/);
+
+        resetLastPointFromPath();
+    }
+
+    /**
+     * Close the current contour. If the current point is not equal to the
+     * first point of the contour, a line segment is automatically added.
+     */
+    private void close() {
+        mPath.closePath();
+    }
+
+    private void resetLastPointFromPath() {
+        Point2D last = mPath.getCurrentPoint();
+        mLastX = (float) last.getX();
+        mLastY = (float) last.getY();
+    }
+
+    /**
+     * Add a closed rectangle contour to the path
+     *
+     * @param left   The left side of a rectangle to add to the path
+     * @param top    The top of a rectangle to add to the path
+     * @param right  The right side of a rectangle to add to the path
+     * @param bottom The bottom of a rectangle to add to the path
+     * @param dir    The direction to wind the rectangle's contour
+     */
+    private void addRect(float left, float top, float right, float bottom,
+                        int dir) {
+        moveTo(left, top);
+
+        Direction direction = getDirection(dir);
+
+        switch (direction) {
+            case CW:
+                lineTo(right, top);
+                lineTo(right, bottom);
+                lineTo(left, bottom);
+                break;
+            case CCW:
+                lineTo(left, bottom);
+                lineTo(right, bottom);
+                lineTo(right, top);
+                break;
+        }
+
+        close();
+
+        resetLastPointFromPath();
+    }
+
+    /**
+     * Offset the path by (dx,dy), returning true on success
+     *
+     * @param dx  The amount in the X direction to offset the entire path
+     * @param dy  The amount in the Y direction to offset the entire path
+     * @param dst The translated path is written here. If this is null, then
+     *            the original path is modified.
+     */
+    public void offset(float dx, float dy, Path_Delegate dst) {
+        GeneralPath newPath = new GeneralPath();
+
+        PathIterator iterator = mPath.getPathIterator(new AffineTransform(0, 0, dx, 0, 0, dy));
+
+        newPath.append(iterator, false /*connect*/);
+
+        if (dst != null) {
+            dst.mPath = newPath;
+        } else {
+            mPath = newPath;
+        }
+    }
+
+    /**
+     * Transform the points in this path by matrix, and write the answer
+     * into dst. If dst is null, then the the original path is modified.
+     *
+     * @param matrix The matrix to apply to the path
+     * @param dst    The transformed path is written here. If dst is null,
+     *               then the the original path is modified
+     */
+    public void transform(Matrix_Delegate matrix, Path_Delegate dst) {
+        if (matrix.hasPerspective()) {
+            assert false;
+            Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_AFFINE,
+                    "android.graphics.Path#transform() only " +
+                    "supports affine transformations.", null, null /*data*/);
+        }
+
+        GeneralPath newPath = new GeneralPath();
+
+        PathIterator iterator = mPath.getPathIterator(matrix.getAffineTransform());
+
+        newPath.append(iterator, false /*connect*/);
+
+        if (dst != null) {
+            dst.mPath = newPath;
+        } else {
+            mPath = newPath;
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/PixelXorXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PixelXorXfermode_Delegate.java
new file mode 100644
index 0000000..4ab044b
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/PixelXorXfermode_Delegate.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.awt.Composite;
+
+/**
+ * Delegate implementing the native methods of android.graphics.PixelXorXfermode
+ *
+ * Through the layoutlib_create tool, the original native methods of PixelXorXfermode have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original PixelXorXfermode class.
+ *
+ * Because this extends {@link Xfermode_Delegate}, there's no need to use a
+ * {@link DelegateManager}, as all the PathEffect classes will be added to the manager owned by
+ * {@link Xfermode_Delegate}.
+ *
+ * @see Xfermode_Delegate
+ */
+public class PixelXorXfermode_Delegate extends Xfermode_Delegate {
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public Composite getComposite(int alpha) {
+        // FIXME
+        return null;
+    }
+
+    @Override
+    public boolean isSupported() {
+        return false;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        return "Pixel XOR Xfermodes are not supported in Layout Preview mode.";
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreate(int opColor) {
+        PixelXorXfermode_Delegate newDelegate = new PixelXorXfermode_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
new file mode 100644
index 0000000..c45dbaa
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate implementing the native methods of android.graphics.PorterDuffColorFilter
+ *
+ * Through the layoutlib_create tool, the original native methods of PorterDuffColorFilter have
+ * been replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original PorterDuffColorFilter class.
+ *
+ * Because this extends {@link ColorFilter_Delegate}, there's no need to use a
+ * {@link DelegateManager}, as all the Shader classes will be added to the manager
+ * owned by {@link ColorFilter_Delegate}.
+ *
+ * @see ColorFilter_Delegate
+ *
+ */
+public class PorterDuffColorFilter_Delegate extends ColorFilter_Delegate {
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public boolean isSupported() {
+        return false;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        return "PorterDuff Color Filters are not supported.";
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int native_CreatePorterDuffFilter(int srcColor, int porterDuffMode) {
+        PorterDuffColorFilter_Delegate newDelegate = new PorterDuffColorFilter_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nCreatePorterDuffFilter(int nativeFilter, int srcColor,
+            int porterDuffMode) {
+        // pass
+        return 0;
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode.java
deleted file mode 100644
index 974ae49..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2008 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 android.graphics.PorterDuff.Mode;
-
-public class PorterDuffXfermode extends Xfermode {
-    private final Mode mMode;
-
-    /**
-     * Create an xfermode that uses the specified porter-duff mode.
-     *
-     * @param mode           The porter-duff mode that is applied
-     */
-    public PorterDuffXfermode(PorterDuff.Mode mode) {
-        mMode = mode;
-    }
-    
-    //---------- Custom Methods
-    
-    public PorterDuff.Mode getMode() {
-        return mMode;
-    }
-    
-    //----------
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
new file mode 100644
index 0000000..4301c1a
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2010 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 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 java.awt.AlphaComposite;
+import java.awt.Composite;
+
+/**
+ * Delegate implementing the native methods of android.graphics.PorterDuffXfermode
+ *
+ * Through the layoutlib_create tool, the original native methods of PorterDuffXfermode have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original PorterDuffXfermode class.
+ *
+ * Because this extends {@link Xfermode_Delegate}, there's no need to use a
+ * {@link DelegateManager}, as all the PathEffect classes will be added to the manager owned by
+ * {@link Xfermode_Delegate}.
+ *
+ */
+public class PorterDuffXfermode_Delegate extends Xfermode_Delegate {
+
+    // ---- delegate data ----
+
+    private final int mMode;
+
+    // ---- Public Helper methods ----
+
+    public PorterDuff.Mode getMode() {
+        return getPorterDuffMode(mMode);
+    }
+
+    @Override
+    public Composite getComposite(int alpha) {
+        return getComposite(getPorterDuffMode(mMode), alpha);
+    }
+
+    @Override
+    public boolean isSupported() {
+        return true;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        // no message since isSupported returns true;
+        return null;
+    }
+
+    public static PorterDuff.Mode getPorterDuffMode(int mode) {
+        for (PorterDuff.Mode m : PorterDuff.Mode.values()) {
+            if (m.nativeInt == mode) {
+                return m;
+            }
+        }
+
+        Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                String.format("Unknown PorterDuff.Mode: %d", mode), null /*data*/);
+        assert false;
+        return PorterDuff.Mode.SRC_OVER;
+    }
+
+    public static Composite getComposite(PorterDuff.Mode mode, int alpha) {
+        float falpha = alpha != 0xFF ? (float)alpha / 255.f : 1.f;
+        switch (mode) {
+            case CLEAR:
+                return AlphaComposite.getInstance(AlphaComposite.CLEAR, falpha);
+            case DARKEN:
+                break;
+            case DST:
+                return AlphaComposite.getInstance(AlphaComposite.DST, falpha);
+            case DST_ATOP:
+                return AlphaComposite.getInstance(AlphaComposite.DST_ATOP, falpha);
+            case DST_IN:
+                return AlphaComposite.getInstance(AlphaComposite.DST_IN, falpha);
+            case DST_OUT:
+                return AlphaComposite.getInstance(AlphaComposite.DST_OUT, falpha);
+            case DST_OVER:
+                return AlphaComposite.getInstance(AlphaComposite.DST_OVER, falpha);
+            case LIGHTEN:
+                break;
+            case MULTIPLY:
+                break;
+            case SCREEN:
+                break;
+            case SRC:
+                return AlphaComposite.getInstance(AlphaComposite.SRC, falpha);
+            case SRC_ATOP:
+                return AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, falpha);
+            case SRC_IN:
+                return AlphaComposite.getInstance(AlphaComposite.SRC_IN, falpha);
+            case SRC_OUT:
+                return AlphaComposite.getInstance(AlphaComposite.SRC_OUT, falpha);
+            case SRC_OVER:
+                return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha);
+            case XOR:
+                return AlphaComposite.getInstance(AlphaComposite.XOR, falpha);
+        }
+
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
+                String.format("Unsupported PorterDuff Mode: %s", mode.name()),
+                null, null /*data*/);
+
+        return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha);
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreateXfermode(int mode) {
+        PorterDuffXfermode_Delegate newDelegate = new PorterDuffXfermode_Delegate(mode);
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    private PorterDuffXfermode_Delegate(int mode) {
+        mMode = mode;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
deleted file mode 100644
index 4409a80..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2008 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;
-
-public class RadialGradient extends GradientShader {
-
-    private RadialGradientPaint mPaint;
-
-    /**
-     * Create a shader that draws a radial gradient given the center and radius.
-     *
-     * @param x The x-coordinate of the center of the radius
-     * @param y The y-coordinate of the center of the radius
-     * @param radius Must be positive. The radius of the circle for this
-     *            gradient
-     * @param colors The colors to be distributed between the center and edge of
-     *            the circle
-     * @param positions May be NULL. The relative position of each corresponding
-     *            color in the colors array. If this is NULL, the the colors are
-     *            distributed evenly between the center and edge of the circle.
-     * @param tile The Shader tiling mode
-     */
-    public RadialGradient(float x, float y, float radius, int colors[], float positions[],
-            TileMode tile) {
-        super(colors, positions);
-        if (radius <= 0) {
-            throw new IllegalArgumentException("radius must be > 0");
-        }
-
-        mPaint = new RadialGradientPaint(x, y, radius, mColors, mPositions, tile);
-    }
-
-    /**
-     * Create a shader that draws a radial gradient given the center and radius.
-     *
-     * @param x The x-coordinate of the center of the radius
-     * @param y The y-coordinate of the center of the radius
-     * @param radius Must be positive. The radius of the circle for this
-     *            gradient
-     * @param color0 The color at the center of the circle.
-     * @param color1 The color at the edge of the circle.
-     * @param tile The Shader tiling mode
-     */
-    public RadialGradient(float x, float y, float radius, int color0, int color1, TileMode tile) {
-        this(x, y, radius, new int[] { color0, color1 }, null /* positions */, tile);
-    }
-
-    @Override
-    java.awt.Paint getJavaPaint() {
-        return mPaint;
-    }
-
-    private static class RadialGradientPaint extends GradientPaint {
-
-        private final float mX;
-        private final float mY;
-        private final float mRadius;
-
-        public RadialGradientPaint(float x, float y, float radius, int[] colors, float[] positions, TileMode mode) {
-            super(colors, positions, mode);
-            mX = x;
-            mY = y;
-            mRadius = radius;
-        }
-
-        public java.awt.PaintContext createContext(
-                java.awt.image.ColorModel     colorModel,
-                java.awt.Rectangle            deviceBounds,
-                java.awt.geom.Rectangle2D     userBounds,
-                java.awt.geom.AffineTransform xform,
-                java.awt.RenderingHints       hints) {
-            precomputeGradientColors();
-            return new RadialGradientPaintContext(colorModel);
-        }
-
-        private class RadialGradientPaintContext implements java.awt.PaintContext {
-
-            private final java.awt.image.ColorModel mColorModel;
-
-            public RadialGradientPaintContext(java.awt.image.ColorModel colorModel) {
-                mColorModel = colorModel;
-            }
-
-            public void dispose() {
-            }
-
-            public java.awt.image.ColorModel getColorModel() {
-                return mColorModel;
-            }
-
-            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
-                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
-                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
-
-                int[] data = new int[w*h];
-
-                // compute distance from each point to the center, and figure out the distance from
-                // it.
-                int index = 0;
-                for (int iy = 0 ; iy < h ; iy++) {
-                    for (int ix = 0 ; ix < w ; ix++) {
-                        float _x = x + ix - mX;
-                        float _y = y + iy - mY;
-                        float distance = (float) Math.sqrt(_x * _x + _y * _y);
-
-                        data[index++] = getGradientColor(distance / mRadius);
-                    }
-                }
-
-                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
-
-                return image.getRaster();
-            }
-
-        }
-    }
-
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
new file mode 100644
index 0000000..9bf78b4
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2010 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 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.graphics.Shader.TileMode;
+
+/**
+ * Delegate implementing the native methods of android.graphics.RadialGradient
+ *
+ * Through the layoutlib_create tool, the original native methods of RadialGradient have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original RadialGradient class.
+ *
+ * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
+ * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
+ *
+ * @see Shader_Delegate
+ *
+ */
+public class RadialGradient_Delegate extends Gradient_Delegate {
+
+    // ---- delegate data ----
+    private java.awt.Paint mJavaPaint;
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public java.awt.Paint getJavaPaint() {
+        return mJavaPaint;
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreate1(float x, float y, float radius,
+            int colors[], float positions[], int tileMode) {
+        RadialGradient_Delegate newDelegate = new RadialGradient_Delegate(x, y, radius,
+                colors, positions, Shader_Delegate.getTileMode(tileMode));
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreate2(float x, float y, float radius,
+            int color0, int color1, int tileMode) {
+        return nativeCreate1(x, y, radius, new int[] { color0, color1 }, null /*positions*/,
+                tileMode);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativePostCreate1(int native_shader, float x, float y, float radius,
+            int colors[], float positions[], int tileMode) {
+        // nothing to be done here.
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativePostCreate2(int native_shader, float x, float y, float radius,
+            int color0, int color1, int tileMode) {
+        // nothing to be done here.
+        return 0;
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    /**
+     * Create a shader that draws a radial gradient given the center and radius.
+     *
+     * @param x The x-coordinate of the center of the radius
+     * @param y The y-coordinate of the center of the radius
+     * @param radius Must be positive. The radius of the circle for this
+     *            gradient
+     * @param colors The colors to be distributed between the center and edge of
+     *            the circle
+     * @param positions May be NULL. The relative position of each corresponding
+     *            color in the colors array. If this is NULL, the the colors are
+     *            distributed evenly between the center and edge of the circle.
+     * @param tile The Shader tiling mode
+     */
+    private RadialGradient_Delegate(float x, float y, float radius, int colors[], float positions[],
+            TileMode tile) {
+        super(colors, positions);
+        mJavaPaint = new RadialGradientPaint(x, y, radius, mColors, mPositions, tile);
+    }
+
+    private class RadialGradientPaint extends GradientPaint {
+
+        private final float mX;
+        private final float mY;
+        private final float mRadius;
+
+        public RadialGradientPaint(float x, float y, float radius,
+                int[] colors, float[] positions, TileMode mode) {
+            super(colors, positions, mode);
+            mX = x;
+            mY = y;
+            mRadius = radius;
+        }
+
+        public java.awt.PaintContext createContext(
+                java.awt.image.ColorModel     colorModel,
+                java.awt.Rectangle            deviceBounds,
+                java.awt.geom.Rectangle2D     userBounds,
+                java.awt.geom.AffineTransform xform,
+                java.awt.RenderingHints       hints) {
+            precomputeGradientColors();
+
+            java.awt.geom.AffineTransform canvasMatrix;
+            try {
+                canvasMatrix = xform.createInverse();
+            } catch (java.awt.geom.NoninvertibleTransformException e) {
+                Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
+                        "Unable to inverse matrix in RadialGradient", e, null /*data*/);
+                canvasMatrix = new java.awt.geom.AffineTransform();
+            }
+
+            java.awt.geom.AffineTransform localMatrix = getLocalMatrix();
+            try {
+                localMatrix = localMatrix.createInverse();
+            } catch (java.awt.geom.NoninvertibleTransformException e) {
+                Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
+                        "Unable to inverse matrix in RadialGradient", e, null /*data*/);
+                localMatrix = new java.awt.geom.AffineTransform();
+            }
+
+            return new RadialGradientPaintContext(canvasMatrix, localMatrix, colorModel);
+        }
+
+        private class RadialGradientPaintContext implements java.awt.PaintContext {
+
+            private final java.awt.geom.AffineTransform mCanvasMatrix;
+            private final java.awt.geom.AffineTransform mLocalMatrix;
+            private final java.awt.image.ColorModel mColorModel;
+
+            public RadialGradientPaintContext(
+                    java.awt.geom.AffineTransform canvasMatrix,
+                    java.awt.geom.AffineTransform localMatrix,
+                    java.awt.image.ColorModel colorModel) {
+                mCanvasMatrix = canvasMatrix;
+                mLocalMatrix = localMatrix;
+                mColorModel = colorModel;
+            }
+
+            public void dispose() {
+            }
+
+            public java.awt.image.ColorModel getColorModel() {
+                return mColorModel;
+            }
+
+            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
+                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
+                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
+
+                int[] data = new int[w*h];
+
+                // compute distance from each point to the center, and figure out the distance from
+                // it.
+                int index = 0;
+                float[] pt1 = new float[2];
+                float[] pt2 = new float[2];
+                for (int iy = 0 ; iy < h ; iy++) {
+                    for (int ix = 0 ; ix < w ; ix++) {
+                        // handle the canvas transform
+                        pt1[0] = x + ix;
+                        pt1[1] = y + iy;
+                        mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
+
+                        // handle the local matrix
+                        pt1[0] = pt2[0] - mX;
+                        pt1[1] = pt2[1] - mY;
+                        mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
+
+                        float _x = pt2[0];
+                        float _y = pt2[1];
+                        float distance = (float) Math.sqrt(_x * _x + _y * _y);
+
+                        data[index++] = getGradientColor(distance / mRadius);
+                    }
+                }
+
+                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
+
+                return image.getRaster();
+            }
+
+        }
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Rasterizer_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Rasterizer_Delegate.java
new file mode 100644
index 0000000..2812b6b
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Rasterizer_Delegate.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Rasterizer
+ *
+ * Through the layoutlib_create tool, the original native methods of Rasterizer have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Rasterizer class.
+ *
+ * This also serve as a base class for all Rasterizer delegate classes.
+ *
+ * @see DelegateManager
+ *
+ */
+public abstract class Rasterizer_Delegate {
+
+    // ---- delegate manager ----
+    protected static final DelegateManager<Rasterizer_Delegate> sManager =
+            new DelegateManager<Rasterizer_Delegate>(Rasterizer_Delegate.class);
+
+    // ---- delegate helper data ----
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    public static Rasterizer_Delegate getDelegate(int nativeShader) {
+        return sManager.getDelegate(nativeShader);
+    }
+
+    public abstract boolean isSupported();
+    public abstract String getSupportMessage();
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static void finalizer(int native_instance) {
+        sManager.removeJavaReferenceFor(native_instance);
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java
new file mode 100644
index 0000000..cb31b8f
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2010 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 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.os.Parcel;
+
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Region
+ *
+ * Through the layoutlib_create tool, the original native methods of Region have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Region class.
+ *
+ * This also serve as a base class for all Region delegate classes.
+ *
+ * @see DelegateManager
+ *
+ */
+public class Region_Delegate {
+
+    // ---- delegate manager ----
+    protected static final DelegateManager<Region_Delegate> sManager =
+            new DelegateManager<Region_Delegate>(Region_Delegate.class);
+
+    // ---- delegate helper data ----
+
+    // ---- delegate data ----
+    private Area mArea = new Area();
+
+    // ---- Public Helper methods ----
+
+    public static Region_Delegate getDelegate(int nativeShader) {
+        return sManager.getDelegate(nativeShader);
+    }
+
+    public Area getJavaArea() {
+        return mArea;
+    }
+
+    /**
+     * Combines two {@link Shape} into another one (actually an {@link Area}), according
+     * to the given {@link Region.Op}.
+     *
+     * If the Op is not one that combines two shapes, then this return null
+     *
+     * @param shape1 the firt shape to combine which can be null if there's no original clip.
+     * @param shape2 the 2nd shape to combine
+     * @param regionOp the operande for the combine
+     * @return a new area or null.
+     */
+    public static Area combineShapes(Shape shape1, Shape shape2, int regionOp) {
+        if (regionOp == Region.Op.DIFFERENCE.nativeInt) {
+            // if shape1 is null (empty), then the result is null.
+            if (shape1 == null) {
+                return null;
+            }
+
+            // result is always a new area.
+            Area result = new Area(shape1);
+            result.subtract(shape2 instanceof Area ? (Area) shape2 : new Area(shape2));
+            return result;
+
+        } else if (regionOp == Region.Op.INTERSECT.nativeInt) {
+            // if shape1 is null, then the result is simply shape2.
+            if (shape1 == null) {
+                return new Area(shape2);
+            }
+
+            // result is always a new area.
+            Area result = new Area(shape1);
+            result.intersect(shape2 instanceof Area ? (Area) shape2 : new Area(shape2));
+            return result;
+
+        } else if (regionOp == Region.Op.UNION.nativeInt) {
+            // if shape1 is null, then the result is simply shape2.
+            if (shape1 == null) {
+                return new Area(shape2);
+            }
+
+            // result is always a new area.
+            Area result = new Area(shape1);
+            result.add(shape2 instanceof Area ? (Area) shape2 : new Area(shape2));
+            return result;
+
+        } else if (regionOp == Region.Op.XOR.nativeInt) {
+            // if shape1 is null, then the result is simply shape2
+            if (shape1 == null) {
+                return new Area(shape2);
+            }
+
+            // result is always a new area.
+            Area result = new Area(shape1);
+            result.exclusiveOr(shape2 instanceof Area ? (Area) shape2 : new Area(shape2));
+            return result;
+
+        } else if (regionOp == Region.Op.REVERSE_DIFFERENCE.nativeInt) {
+            // result is always a new area.
+            Area result = new Area(shape2);
+
+            if (shape1 != null) {
+                result.subtract(shape1 instanceof Area ? (Area) shape1 : new Area(shape1));
+            }
+
+            return result;
+        }
+
+        return null;
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static boolean isEmpty(Region thisRegion) {
+        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
+        if (regionDelegate == null) {
+            return true;
+        }
+
+        return regionDelegate.mArea.isEmpty();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean isRect(Region thisRegion) {
+        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
+        if (regionDelegate == null) {
+            return true;
+        }
+
+        return regionDelegate.mArea.isRectangular();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean isComplex(Region thisRegion) {
+        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
+        if (regionDelegate == null) {
+            return true;
+        }
+
+        return regionDelegate.mArea.isSingular() == false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean contains(Region thisRegion, int x, int y) {
+        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
+        if (regionDelegate == null) {
+            return false;
+        }
+
+        return regionDelegate.mArea.contains(x, y);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean quickContains(Region thisRegion,
+            int left, int top, int right, int bottom) {
+        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
+        if (regionDelegate == null) {
+            return false;
+        }
+
+        return regionDelegate.mArea.isRectangular() &&
+                regionDelegate.mArea.contains(left, top, right - left, bottom - top);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean quickReject(Region thisRegion,
+            int left, int top, int right, int bottom) {
+        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
+        if (regionDelegate == null) {
+            return false;
+        }
+
+        return regionDelegate.mArea.isEmpty() ||
+                regionDelegate.mArea.intersects(left, top, right - left, bottom - top) == false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean quickReject(Region thisRegion, Region rgn) {
+        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
+        if (regionDelegate == null) {
+            return false;
+        }
+
+        Region_Delegate targetRegionDelegate = sManager.getDelegate(rgn.mNativeRegion);
+        if (targetRegionDelegate == null) {
+            return false;
+        }
+
+        return regionDelegate.mArea.isEmpty() ||
+                regionDelegate.mArea.getBounds().intersects(
+                        targetRegionDelegate.mArea.getBounds()) == false;
+
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void translate(Region thisRegion, int dx, int dy, Region dst) {
+        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
+        if (regionDelegate == null) {
+            return;
+        }
+
+        Region_Delegate targetRegionDelegate = sManager.getDelegate(dst.mNativeRegion);
+        if (targetRegionDelegate == null) {
+            return;
+        }
+
+        if (regionDelegate.mArea.isEmpty()) {
+            targetRegionDelegate.mArea = new Area();
+        } else {
+            targetRegionDelegate.mArea = new Area(regionDelegate.mArea);
+            AffineTransform mtx = new AffineTransform();
+            mtx.translate(dx, dy);
+            targetRegionDelegate.mArea.transform(mtx);
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void scale(Region thisRegion, float scale, Region dst) {
+        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
+        if (regionDelegate == null) {
+            return;
+        }
+
+        Region_Delegate targetRegionDelegate = sManager.getDelegate(dst.mNativeRegion);
+        if (targetRegionDelegate == null) {
+            return;
+        }
+
+        if (regionDelegate.mArea.isEmpty()) {
+            targetRegionDelegate.mArea = new Area();
+        } else {
+            targetRegionDelegate.mArea = new Area(regionDelegate.mArea);
+            AffineTransform mtx = new AffineTransform();
+            mtx.scale(scale, scale);
+            targetRegionDelegate.mArea.transform(mtx);
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeConstructor() {
+        Region_Delegate newDelegate = new Region_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeDestructor(int native_region) {
+        sManager.removeJavaReferenceFor(native_region);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nativeSetRegion(int native_dst, int native_src) {
+        Region_Delegate dstRegion = sManager.getDelegate(native_dst);
+        if (dstRegion == null) {
+            return true;
+        }
+
+        Region_Delegate srcRegion = sManager.getDelegate(native_src);
+        if (srcRegion == null) {
+            return true;
+        }
+
+        dstRegion.mArea.reset();
+        dstRegion.mArea.add(srcRegion.mArea);
+
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nativeSetRect(int native_dst,
+            int left, int top, int right, int bottom) {
+        Region_Delegate dstRegion = sManager.getDelegate(native_dst);
+        if (dstRegion == null) {
+            return true;
+        }
+
+        dstRegion.mArea = new Area(new Rectangle2D.Float(left, top, right - left, bottom - top));
+        return dstRegion.mArea.getBounds().isEmpty() == false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nativeSetPath(int native_dst, int native_path, int native_clip) {
+        Region_Delegate dstRegion = sManager.getDelegate(native_dst);
+        if (dstRegion == null) {
+            return true;
+        }
+
+        Path_Delegate path = Path_Delegate.getDelegate(native_path);
+        if (path == null) {
+            return true;
+        }
+
+        dstRegion.mArea = new Area(path.getJavaShape());
+
+        Region_Delegate clip = sManager.getDelegate(native_clip);
+        if (clip != null) {
+            dstRegion.mArea.subtract(clip.getJavaArea());
+        }
+
+        return dstRegion.mArea.getBounds().isEmpty() == false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nativeGetBounds(int native_region, Rect rect) {
+        Region_Delegate region = sManager.getDelegate(native_region);
+        if (region == null) {
+            return true;
+        }
+
+        Rectangle bounds = region.mArea.getBounds();
+        if (bounds.isEmpty()) {
+            rect.left = rect.top = rect.right = rect.bottom = 0;
+            return false;
+        }
+
+        rect.left = bounds.x;
+        rect.top = bounds.y;
+        rect.right = bounds.x + bounds.width;
+        rect.bottom = bounds.y + bounds.height;
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nativeGetBoundaryPath(int native_region, int native_path) {
+        Region_Delegate region = sManager.getDelegate(native_region);
+        if (region == null) {
+            return false;
+        }
+
+        Path_Delegate path = Path_Delegate.getDelegate(native_path);
+        if (path == null) {
+            return false;
+        }
+
+        if (region.mArea.isEmpty()) {
+            path.reset();
+            return false;
+        }
+
+        path.setPathIterator(region.mArea.getPathIterator(new AffineTransform()));
+        return true;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nativeOp(int native_dst,
+            int left, int top, int right, int bottom, int op) {
+        Region_Delegate region = sManager.getDelegate(native_dst);
+        if (region == null) {
+            return false;
+        }
+
+        region.mArea = combineShapes(region.mArea,
+                new Rectangle2D.Float(left, top, right - left, bottom - top), op);
+
+        assert region.mArea != null;
+        if (region.mArea != null) {
+            region.mArea = new Area();
+        }
+
+        return region.mArea.getBounds().isEmpty() == false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nativeOp(int native_dst, Rect rect, int native_region, int op) {
+        Region_Delegate region = sManager.getDelegate(native_dst);
+        if (region == null) {
+            return false;
+        }
+
+        region.mArea = combineShapes(region.mArea,
+                new Rectangle2D.Float(rect.left, rect.top, rect.width(), rect.height()), op);
+
+        assert region.mArea != null;
+        if (region.mArea != null) {
+            region.mArea = new Area();
+        }
+
+        return region.mArea.getBounds().isEmpty() == false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nativeOp(int native_dst,
+            int native_region1, int native_region2, int op) {
+        Region_Delegate dstRegion = sManager.getDelegate(native_dst);
+        if (dstRegion == null) {
+            return true;
+        }
+
+        Region_Delegate region1 = sManager.getDelegate(native_region1);
+        if (region1 == null) {
+            return false;
+        }
+
+        Region_Delegate region2 = sManager.getDelegate(native_region2);
+        if (region2 == null) {
+            return false;
+        }
+
+        dstRegion.mArea = combineShapes(region1.mArea, region2.mArea, op);
+
+        assert dstRegion.mArea != null;
+        if (dstRegion.mArea != null) {
+            dstRegion.mArea = new Area();
+        }
+
+        return dstRegion.mArea.getBounds().isEmpty() == false;
+
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreateFromParcel(Parcel p) {
+        // This is only called by Region.CREATOR (Parcelable.Creator<Region>), which is only
+        // used during aidl call so really this should not be called.
+        Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
+                "AIDL is not suppored, and therefore Regions cannot be created from parcels.",
+                null /*data*/);
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nativeWriteToParcel(int native_region,
+                                                      Parcel p) {
+        // This is only called when sending a region through aidl, so really this should not
+        // be called.
+        Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
+                "AIDL is not suppored, and therefore Regions cannot be written to parcels.",
+                null /*data*/);
+        return false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nativeEquals(int native_r1, int native_r2) {
+        Region_Delegate region1 = sManager.getDelegate(native_r1);
+        if (region1 == null) {
+            return false;
+        }
+
+        Region_Delegate region2 = sManager.getDelegate(native_r2);
+        if (region2 == null) {
+            return false;
+        }
+
+        return region1.mArea.equals(region2.mArea);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static String nativeToString(int native_region) {
+        Region_Delegate region = sManager.getDelegate(native_region);
+        if (region == null) {
+            return "not found";
+        }
+
+        return region.mArea.toString();
+    }
+
+    // ---- Private delegate/helper methods ----
+
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader.java b/tools/layoutlib/bridge/src/android/graphics/Shader.java
deleted file mode 100644
index 0cc5940..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Shader.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2008 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;
-
-
-
-/**
- * Shader is the based class for objects that return horizontal spans of colors
- * during drawing. A subclass of Shader is installed in a Paint calling
- * paint.setShader(shader). After that any object (other than a bitmap) that is
- * drawn with that paint will get its color(s) from the shader.
- */
-public abstract class Shader {
-
-    private final Matrix mMatrix = new Matrix();
-
-    public enum TileMode {
-        /**
-         * replicate the edge color if the shader draws outside of its
-         * original bounds
-         */
-        CLAMP   (0),
-        /**
-         * repeat the shader's image horizontally and vertically
-         */
-        REPEAT  (1),
-        /**
-         * repeat the shader's image horizontally and vertically, alternating
-         * mirror images so that adjacent images always seam
-         */
-        MIRROR  (2);
-
-        TileMode(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-    }
-
-    /**
-     * Return true if the shader has a non-identity local matrix.
-     * @param localM If not null, it is set to the shader's local matrix.
-     * @return true if the shader has a non-identity local matrix
-     */
-    public boolean getLocalMatrix(Matrix localM) {
-        if (localM != null) {
-            localM.set(mMatrix);
-        }
-
-        return !mMatrix.isIdentity();
-    }
-
-    /**
-     * Set the shader's local matrix. Passing null will reset the shader's
-     * matrix to identity
-     * @param localM The shader's new local matrix, or null to specify identity
-     */
-    public void setLocalMatrix(Matrix localM) {
-        if (localM != null) {
-            mMatrix.set(localM);
-        } else {
-            mMatrix.reset();
-        }
-    }
-
-    /**
-     * Returns a java.awt.Paint object matching this shader.
-     */
-    abstract java.awt.Paint getJavaPaint();
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
new file mode 100644
index 0000000..368c0384
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.graphics.Shader.TileMode;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Shader
+ *
+ * Through the layoutlib_create tool, the original native methods of Shader have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Shader class.
+ *
+ * This also serve as a base class for all Shader delegate classes.
+ *
+ * @see DelegateManager
+ *
+ */
+public abstract class Shader_Delegate {
+
+    // ---- delegate manager ----
+    protected static final DelegateManager<Shader_Delegate> sManager =
+            new DelegateManager<Shader_Delegate>(Shader_Delegate.class);
+
+    // ---- delegate helper data ----
+
+    // ---- delegate data ----
+    private Matrix_Delegate mLocalMatrix = null;
+
+    // ---- Public Helper methods ----
+
+    public static Shader_Delegate getDelegate(int nativeShader) {
+        return sManager.getDelegate(nativeShader);
+    }
+
+    /**
+     * Returns the {@link TileMode} matching the given int.
+     * @param tileMode the tile mode int value
+     * @return the TileMode enum.
+     */
+    public static TileMode getTileMode(int tileMode) {
+        for (TileMode tm : TileMode.values()) {
+            if (tm.nativeInt == tileMode) {
+                return tm;
+            }
+        }
+
+        assert false;
+        return TileMode.CLAMP;
+    }
+
+    public abstract java.awt.Paint getJavaPaint();
+    public abstract boolean isSupported();
+    public abstract String getSupportMessage();
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeDestructor(int native_shader, int native_skiaShader) {
+        sManager.removeJavaReferenceFor(native_shader);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeSetLocalMatrix(int native_shader, int native_skiaShader,
+            int matrix_instance) {
+        // get the delegate from the native int.
+        Shader_Delegate shaderDelegate = sManager.getDelegate(native_shader);
+        if (shaderDelegate == null) {
+            return;
+        }
+
+        shaderDelegate.mLocalMatrix = Matrix_Delegate.getDelegate(matrix_instance);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    protected java.awt.geom.AffineTransform getLocalMatrix() {
+        if (mLocalMatrix != null) {
+            return mLocalMatrix.getAffineTransform();
+        }
+
+        return new java.awt.geom.AffineTransform();
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/SumPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SumPathEffect_Delegate.java
new file mode 100644
index 0000000..410df0c
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/SumPathEffect_Delegate.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.awt.Stroke;
+
+/**
+ * Delegate implementing the native methods of android.graphics.SumPathEffect
+ *
+ * Through the layoutlib_create tool, the original native methods of SumPathEffect have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original SumPathEffect class.
+ *
+ * Because this extends {@link PathEffect_Delegate}, there's no need to use a {@link DelegateManager},
+ * as all the Shader classes will be added to the manager owned by {@link PathEffect_Delegate}.
+ *
+ * @see PathEffect_Delegate
+ *
+ */
+public class SumPathEffect_Delegate extends PathEffect_Delegate {
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public Stroke getStroke(Paint_Delegate paint) {
+        // FIXME
+        return null;
+    }
+
+    @Override
+    public boolean isSupported() {
+        return false;
+    }
+
+    @Override
+    public String getSupportMessage() {
+        return "Sum Path Effects are not supported in Layout Preview mode.";
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreate(int first, int second) {
+        SumPathEffect_Delegate newDelegate = new SumPathEffect_Delegate();
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    // ---- Private delegate/helper methods ----
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
deleted file mode 100644
index 87036ed..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2008 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;
-
-public class SweepGradient extends GradientShader {
-
-    private SweepGradientPaint mPaint;
-
-    /**
-     * A subclass of Shader that draws a sweep gradient around a center point.
-     *
-     * @param cx       The x-coordinate of the center
-     * @param cy       The y-coordinate of the center
-     * @param colors   The colors to be distributed between around the center.
-     *                 There must be at least 2 colors in the array.
-     * @param positions May be NULL. The relative position of
-     *                 each corresponding color in the colors array, beginning
-     *                 with 0 and ending with 1.0. If the values are not
-     *                 monotonic, the drawing may produce unexpected results.
-     *                 If positions is NULL, then the colors are automatically
-     *                 spaced evenly.
-     */
-    public SweepGradient(float cx, float cy,
-                         int colors[], float positions[]) {
-        super(colors, positions);
-
-        mPaint = new SweepGradientPaint(cx, cy, mColors, mPositions);
-    }
-
-    /**
-     * A subclass of Shader that draws a sweep gradient around a center point.
-     *
-     * @param cx       The x-coordinate of the center
-     * @param cy       The y-coordinate of the center
-     * @param color0   The color to use at the start of the sweep
-     * @param color1   The color to use at the end of the sweep
-     */
-    public SweepGradient(float cx, float cy, int color0, int color1) {
-        this(cx, cy, new int[] { color0, color1}, null /*positions*/);
-    }
-
-    @Override
-    java.awt.Paint getJavaPaint() {
-        return mPaint;
-    }
-
-    private static class SweepGradientPaint extends GradientPaint {
-
-        private final float mCx;
-        private final float mCy;
-
-        public SweepGradientPaint(float cx, float cy, int[] colors, float[] positions) {
-            super(colors, positions, null /*tileMode*/);
-            mCx = cx;
-            mCy = cy;
-        }
-
-        public java.awt.PaintContext createContext(
-                java.awt.image.ColorModel     colorModel,
-                java.awt.Rectangle            deviceBounds,
-                java.awt.geom.Rectangle2D     userBounds,
-                java.awt.geom.AffineTransform xform,
-                java.awt.RenderingHints       hints) {
-            precomputeGradientColors();
-            return new SweepGradientPaintContext(colorModel);
-        }
-
-        private class SweepGradientPaintContext implements java.awt.PaintContext {
-
-            private final java.awt.image.ColorModel mColorModel;
-
-            public SweepGradientPaintContext(java.awt.image.ColorModel colorModel) {
-                mColorModel = colorModel;
-            }
-
-            public void dispose() {
-            }
-
-            public java.awt.image.ColorModel getColorModel() {
-                return mColorModel;
-            }
-
-            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
-                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
-                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
-
-                int[] data = new int[w*h];
-
-                // compute angle from each point to the center, and figure out the distance from
-                // it.
-                int index = 0;
-                for (int iy = 0 ; iy < h ; iy++) {
-                    for (int ix = 0 ; ix < w ; ix++) {
-                        float dx = x + ix - mCx;
-                        float dy = y + iy - mCy;
-                        float angle;
-                        if (dx == 0) {
-                            angle = (float) (dy < 0 ? 3 * Math.PI / 2 : Math.PI / 2);
-                        } else if (dy == 0) {
-                            angle = (float) (dx < 0 ? Math.PI : 0);
-                        } else {
-                            angle = (float) Math.atan(dy / dx);
-                            if (dx > 0) {
-                                if (dy < 0) {
-                                    angle += Math.PI * 2;
-                                }
-                            } else {
-                                angle += Math.PI;
-                            }
-                        }
-
-                        // convert to 0-1. value and get color
-                        data[index++] = getGradientColor((float) (angle / (2 * Math.PI)));
-                    }
-                }
-
-                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
-
-                return image.getRaster();
-            }
-
-        }
-    }
-
-}
-
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
new file mode 100644
index 0000000..966e06e
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2010 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 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;
+
+/**
+ * Delegate implementing the native methods of android.graphics.SweepGradient
+ *
+ * Through the layoutlib_create tool, the original native methods of SweepGradient have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original SweepGradient class.
+ *
+ * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
+ * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
+ *
+ * @see Shader_Delegate
+ *
+ */
+public class SweepGradient_Delegate extends Gradient_Delegate {
+
+    // ---- delegate data ----
+    private java.awt.Paint mJavaPaint;
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public java.awt.Paint getJavaPaint() {
+        return mJavaPaint;
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreate1(float x, float y, int colors[], float positions[]) {
+        SweepGradient_Delegate newDelegate = new SweepGradient_Delegate(x, y, colors, positions);
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeCreate2(float x, float y, int color0, int color1) {
+        return nativeCreate1(x, y, new int[] { color0, color1 }, null /*positions*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativePostCreate1(int native_shader, float cx, float cy,
+            int[] colors, float[] positions) {
+        // nothing to be done here.
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativePostCreate2(int native_shader, float cx, float cy,
+            int color0, int color1) {
+        // nothing to be done here.
+        return 0;
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    /**
+     * A subclass of Shader that draws a sweep gradient around a center point.
+     *
+     * @param cx       The x-coordinate of the center
+     * @param cy       The y-coordinate of the center
+     * @param colors   The colors to be distributed between around the center.
+     *                 There must be at least 2 colors in the array.
+     * @param positions May be NULL. The relative position of
+     *                 each corresponding color in the colors array, beginning
+     *                 with 0 and ending with 1.0. If the values are not
+     *                 monotonic, the drawing may produce unexpected results.
+     *                 If positions is NULL, then the colors are automatically
+     *                 spaced evenly.
+     */
+    private SweepGradient_Delegate(float cx, float cy,
+                         int colors[], float positions[]) {
+        super(colors, positions);
+        mJavaPaint = new SweepGradientPaint(cx, cy, mColors, mPositions);
+    }
+
+    private class SweepGradientPaint extends GradientPaint {
+
+        private final float mCx;
+        private final float mCy;
+
+        public SweepGradientPaint(float cx, float cy, int[] colors,
+                float[] positions) {
+            super(colors, positions, null /*tileMode*/);
+            mCx = cx;
+            mCy = cy;
+        }
+
+        public java.awt.PaintContext createContext(
+                java.awt.image.ColorModel     colorModel,
+                java.awt.Rectangle            deviceBounds,
+                java.awt.geom.Rectangle2D     userBounds,
+                java.awt.geom.AffineTransform xform,
+                java.awt.RenderingHints       hints) {
+            precomputeGradientColors();
+
+            java.awt.geom.AffineTransform canvasMatrix;
+            try {
+                canvasMatrix = xform.createInverse();
+            } catch (java.awt.geom.NoninvertibleTransformException e) {
+                Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
+                        "Unable to inverse matrix in SweepGradient", e, null /*data*/);
+                canvasMatrix = new java.awt.geom.AffineTransform();
+            }
+
+            java.awt.geom.AffineTransform localMatrix = getLocalMatrix();
+            try {
+                localMatrix = localMatrix.createInverse();
+            } catch (java.awt.geom.NoninvertibleTransformException e) {
+                Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
+                        "Unable to inverse matrix in SweepGradient", e, null /*data*/);
+                localMatrix = new java.awt.geom.AffineTransform();
+            }
+
+            return new SweepGradientPaintContext(canvasMatrix, localMatrix, colorModel);
+        }
+
+        private class SweepGradientPaintContext implements java.awt.PaintContext {
+
+            private final java.awt.geom.AffineTransform mCanvasMatrix;
+            private final java.awt.geom.AffineTransform mLocalMatrix;
+            private final java.awt.image.ColorModel mColorModel;
+
+            public SweepGradientPaintContext(
+                    java.awt.geom.AffineTransform canvasMatrix,
+                    java.awt.geom.AffineTransform localMatrix,
+                    java.awt.image.ColorModel colorModel) {
+                mCanvasMatrix = canvasMatrix;
+                mLocalMatrix = localMatrix;
+                mColorModel = colorModel;
+            }
+
+            public void dispose() {
+            }
+
+            public java.awt.image.ColorModel getColorModel() {
+                return mColorModel;
+            }
+
+            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
+                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
+                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
+
+                int[] data = new int[w*h];
+
+                // compute angle from each point to the center, and figure out the distance from
+                // it.
+                int index = 0;
+                float[] pt1 = new float[2];
+                float[] pt2 = new float[2];
+                for (int iy = 0 ; iy < h ; iy++) {
+                    for (int ix = 0 ; ix < w ; ix++) {
+                        // handle the canvas transform
+                        pt1[0] = x + ix;
+                        pt1[1] = y + iy;
+                        mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
+
+                        // handle the local matrix
+                        pt1[0] = pt2[0] - mCx;
+                        pt1[1] = pt2[1] - mCy;
+                        mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
+
+                        float dx = pt2[0];
+                        float dy = pt2[1];
+
+                        float angle;
+                        if (dx == 0) {
+                            angle = (float) (dy < 0 ? 3 * Math.PI / 2 : Math.PI / 2);
+                        } else if (dy == 0) {
+                            angle = (float) (dx < 0 ? Math.PI : 0);
+                        } else {
+                            angle = (float) Math.atan(dy / dx);
+                            if (dx > 0) {
+                                if (dy < 0) {
+                                    angle += Math.PI * 2;
+                                }
+                            } else {
+                                angle += Math.PI;
+                            }
+                        }
+
+                        // convert to 0-1. value and get color
+                        data[index++] = getGradientColor((float) (angle / (2 * Math.PI)));
+                    }
+                }
+
+                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
+
+                return image.getRaster();
+            }
+
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface.java b/tools/layoutlib/bridge/src/android/graphics/Typeface.java
deleted file mode 100644
index af3adb5..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2008 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 com.android.layoutlib.bridge.FontLoader;
-
-import android.content.res.AssetManager;
-
-import java.awt.Font;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Re-implementation of Typeface over java.awt
- */
-public class Typeface {
-    private static final String DEFAULT_FAMILY = "sans-serif";
-    private static final int[] styleBuffer = new int[1];
-
-    /** The default NORMAL typeface object */
-    public static Typeface DEFAULT;
-    /**
-     * The default BOLD typeface object. Note: this may be not actually be
-     * bold, depending on what fonts are installed. Call getStyle() to know
-     * for sure.
-     */
-    public static Typeface DEFAULT_BOLD;
-    /** The NORMAL style of the default sans serif typeface. */
-    public static Typeface SANS_SERIF;
-    /** The NORMAL style of the default serif typeface. */
-    public static Typeface SERIF;
-    /** The NORMAL style of the default monospace typeface. */
-    public static Typeface MONOSPACE;
-
-    private static Typeface[] sDefaults;
-    private static FontLoader mFontLoader;
-
-    private final int mStyle;
-    private final List<Font> mFonts;
-    private final String mFamily;
-
-    // Style
-    public static final int NORMAL = _Original_Typeface.NORMAL;
-    public static final int BOLD = _Original_Typeface.BOLD;
-    public static final int ITALIC = _Original_Typeface.ITALIC;
-    public static final int BOLD_ITALIC = _Original_Typeface.BOLD_ITALIC;
-
-    /**
-     * Returns the underlying {@link Font} objects. The first item in the list is the real
-     * font. Any other items are fallback fonts for characters not found in the first one.
-     */
-    public List<Font> getFonts() {
-        return mFonts;
-    }
-
-    /** Returns the typeface's intrinsic style attributes */
-    public int getStyle() {
-        return mStyle;
-    }
-
-    /** Returns true if getStyle() has the BOLD bit set. */
-    public final boolean isBold() {
-        return (getStyle() & BOLD) != 0;
-    }
-
-    /** Returns true if getStyle() has the ITALIC bit set. */
-    public final boolean isItalic() {
-        return (getStyle() & ITALIC) != 0;
-    }
-
-    /**
-     * Create a typeface object given a family name, and option style information.
-     * If null is passed for the name, then the "default" font will be chosen.
-     * The resulting typeface object can be queried (getStyle()) to discover what
-     * its "real" style characteristics are.
-     *
-     * @param familyName May be null. The name of the font family.
-     * @param style  The style (normal, bold, italic) of the typeface.
-     *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
-     * @return The best matching typeface.
-     */
-    public static Typeface create(String familyName, int style) {
-        styleBuffer[0] = style;
-        Font font = mFontLoader.getFont(familyName, styleBuffer);
-        if (font != null) {
-            ArrayList<Font> list = new ArrayList<Font>();
-            list.add(font);
-            list.addAll(mFontLoader.getFallBackFonts());
-            return new Typeface(familyName, styleBuffer[0], list);
-        }
-
-        return null;
-    }
-
-    /**
-     * Create a typeface object that best matches the specified existing
-     * typeface and the specified Style. Use this call if you want to pick a new
-     * style from the same family of an existing typeface object. If family is
-     * null, this selects from the default font's family.
-     *
-     * @param family May be null. The name of the existing type face.
-     * @param style  The style (normal, bold, italic) of the typeface.
-     *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
-     * @return The best matching typeface.
-     */
-    public static Typeface create(Typeface family, int style) {
-        styleBuffer[0] = style;
-        Font font = mFontLoader.getFont(family.mFamily, styleBuffer);
-        if (font != null) {
-            ArrayList<Font> list = new ArrayList<Font>();
-            list.add(font);
-            list.addAll(mFontLoader.getFallBackFonts());
-            return new Typeface(family.mFamily, styleBuffer[0], list);
-        }
-
-        return null;
-    }
-
-    /**
-     * Returns one of the default typeface objects, based on the specified style
-     *
-     * @return the default typeface that corresponds to the style
-     */
-    public static Typeface defaultFromStyle(int style) {
-        return sDefaults[style];
-    }
-
-    /**
-     * Create a new typeface from the specified font data.
-     * @param mgr The application's asset manager
-     * @param path  The file name of the font data in the assets directory
-     * @return The new typeface.
-     */
-    public static Typeface createFromAsset(AssetManager mgr, String path) {
-        return null;
-        //return new Typeface(nativeCreateFromAsset(mgr, path));
-    }
-
-    // don't allow clients to call this directly
-    private Typeface(String family, int style, List<Font> fonts) {
-        mFamily = family;
-        mFonts = Collections.unmodifiableList(fonts);
-        mStyle = style;
-    }
-
-    public static void init(FontLoader fontLoader) {
-        mFontLoader = fontLoader;
-
-        DEFAULT = create(DEFAULT_FAMILY, NORMAL);
-        DEFAULT_BOLD = create(DEFAULT_FAMILY, BOLD);
-        SANS_SERIF = create("sans-serif", NORMAL);
-        SERIF = create("serif", NORMAL);
-        MONOSPACE = create("monospace", NORMAL);
-        sDefaults = new Typeface[] {
-                DEFAULT,
-                DEFAULT_BOLD,
-                create(DEFAULT_FAMILY, ITALIC),
-                create(DEFAULT_FAMILY, BOLD_ITALIC),
-        };
-
-        /*
-        DEFAULT         = create((String)null, 0);
-        DEFAULT_BOLD    = create((String)null, Typeface.BOLD);
-        SANS_SERIF      = create("sans-serif", 0);
-        SERIF           = create("serif", 0);
-        MONOSPACE       = create("monospace", 0);
-
-        sDefaults = new Typeface[] {
-            DEFAULT,
-            DEFAULT_BOLD,
-            create((String)null, Typeface.ITALIC),
-            create((String)null, Typeface.BOLD_ITALIC),
-        };*/
-    }
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
new file mode 100644
index 0000000..0f084f7
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2010 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 com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.layoutlib.bridge.impl.FontLoader;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.content.res.AssetManager;
+
+import java.awt.Font;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Typeface
+ *
+ * Through the layoutlib_create tool, the original native methods of Typeface have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Typeface class.
+ *
+ * @see DelegateManager
+ *
+ */
+public final class Typeface_Delegate {
+
+    // ---- delegate manager ----
+    private static final DelegateManager<Typeface_Delegate> sManager =
+            new DelegateManager<Typeface_Delegate>(Typeface_Delegate.class);
+
+    // ---- delegate helper data ----
+    private static final String DEFAULT_FAMILY = "sans-serif";
+    private static final int[] STYLE_BUFFER = new int[1];
+
+    private static FontLoader sFontLoader;
+    private static final List<Typeface_Delegate> sPostInitDelegate =
+            new ArrayList<Typeface_Delegate>();
+
+    // ---- delegate data ----
+
+    private final String mFamily;
+    private int mStyle;
+    private List<Font> mFonts;
+
+
+    // ---- Public Helper methods ----
+
+    public static synchronized void init(FontLoader fontLoader) {
+        sFontLoader = fontLoader;
+
+        for (Typeface_Delegate delegate : sPostInitDelegate) {
+            delegate.init();
+        }
+        sPostInitDelegate.clear();
+    }
+
+    public static Typeface_Delegate getDelegate(int nativeTypeface) {
+        return sManager.getDelegate(nativeTypeface);
+    }
+
+    public static List<Font> getFonts(Typeface typeface) {
+        return getFonts(typeface.native_instance);
+    }
+
+    public static List<Font> getFonts(int native_int) {
+        Typeface_Delegate delegate = sManager.getDelegate(native_int);
+        if (delegate == null) {
+            return null;
+        }
+
+        return delegate.getFonts();
+    }
+
+    public List<Font> getFonts() {
+        return mFonts;
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static synchronized int nativeCreate(String familyName, int style) {
+        if (familyName == null) {
+            familyName = DEFAULT_FAMILY;
+        }
+
+        Typeface_Delegate newDelegate = new Typeface_Delegate(familyName, style);
+        if (sFontLoader != null) {
+            newDelegate.init();
+        } else {
+            // font loader has not been initialized yet, add the delegate to a list of delegates
+            // to init when the font loader is initialized.
+            // There won't be any rendering before this happens anyway.
+            sPostInitDelegate.add(newDelegate);
+        }
+
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static synchronized int nativeCreateFromTypeface(int native_instance, int style) {
+        Typeface_Delegate delegate = sManager.getDelegate(native_instance);
+        if (delegate == null) {
+            return 0;
+        }
+
+        Typeface_Delegate newDelegate = new Typeface_Delegate(delegate.mFamily, style);
+        if (sFontLoader != null) {
+            newDelegate.init();
+        } else {
+            // font loader has not been initialized yet, add the delegate to a list of delegates
+            // to init when the font loader is initialized.
+            // There won't be any rendering before this happens anyway.
+            sPostInitDelegate.add(newDelegate);
+        }
+
+        return sManager.addNewDelegate(newDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static synchronized int nativeCreateFromAsset(AssetManager mgr, String path) {
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Typeface.createFromAsset() is not supported.", null /*throwable*/, null /*data*/);
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static synchronized int nativeCreateFromFile(String path) {
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Typeface.createFromFile() is not supported.", null /*throwable*/, null /*data*/);
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeUnref(int native_instance) {
+        sManager.removeJavaReferenceFor(native_instance);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeGetStyle(int native_instance) {
+        Typeface_Delegate delegate = sManager.getDelegate(native_instance);
+        if (delegate == null) {
+            return 0;
+        }
+
+        return delegate.mStyle;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void setGammaForText(float blackGamma, float whiteGamma) {
+        // This is for device testing only: pass
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    private Typeface_Delegate(String family, int style) {
+        mFamily = family;
+        mStyle = style;
+    }
+
+    private void init() {
+        STYLE_BUFFER[0] = mStyle;
+        Font font = sFontLoader.getFont(mFamily, STYLE_BUFFER);
+        if (font != null) {
+            List<Font> list = new ArrayList<Font>();
+            list.add(font);
+            list.addAll(sFontLoader.getFallBackFonts());
+            mFonts = Collections.unmodifiableList(list);
+            mStyle = STYLE_BUFFER[0];
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java
new file mode 100644
index 0000000..962d69c
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.awt.Composite;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Xfermode
+ *
+ * Through the layoutlib_create tool, the original native methods of Xfermode have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Xfermode class.
+ *
+ * This also serve as a base class for all Xfermode delegate classes.
+ *
+ * @see DelegateManager
+ *
+ */
+public abstract class Xfermode_Delegate {
+
+    // ---- delegate manager ----
+    protected static final DelegateManager<Xfermode_Delegate> sManager =
+            new DelegateManager<Xfermode_Delegate>(Xfermode_Delegate.class);
+
+    // ---- delegate helper data ----
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    public static Xfermode_Delegate getDelegate(int native_instance) {
+        return sManager.getDelegate(native_instance);
+    }
+
+    public abstract Composite getComposite(int alpha);
+    public abstract boolean isSupported();
+    public abstract String getSupportMessage();
+
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static void finalizer(int native_instance) {
+        sManager.removeJavaReferenceFor(native_instance);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+}
diff --git a/tools/layoutlib/bridge/src/android/os/Build_Delegate.java b/tools/layoutlib/bridge/src/android/os/Build_Delegate.java
new file mode 100644
index 0000000..ff82a5e
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/os/Build_Delegate.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011 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.os;
+
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.util.Map;
+
+/**
+ * Delegate implementing the native methods of android.os.Build
+ *
+ * Through the layoutlib_create tool, the original native methods of Build have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
+ * around to map int to instance of the delegate.
+ *
+ */
+public class Build_Delegate {
+
+    @LayoutlibDelegate
+    /*package*/ static String getString(String property) {
+        Map<String, String> properties = Bridge.getPlatformProperties();
+        String value = properties.get(property);
+        if (value != null) {
+            return value;
+        }
+
+        return Build.UNKNOWN;
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/android/os/Handler_Delegate.java b/tools/layoutlib/bridge/src/android/os/Handler_Delegate.java
new file mode 100644
index 0000000..2152c8a
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/os/Handler_Delegate.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 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.os;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+
+/**
+ * Delegate overriding selected methods of android.os.Handler
+ *
+ * Through the layoutlib_create tool, selected methods of Handler have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ *
+ */
+public class Handler_Delegate {
+
+    // -------- Delegate methods
+
+    @LayoutlibDelegate
+    /*package*/ static boolean sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
+        // get the callback
+        IHandlerCallback callback = sCallbacks.get();
+        if (callback != null) {
+            callback.sendMessageAtTime(handler, msg, uptimeMillis);
+        }
+        return true;
+    }
+
+    // -------- Delegate implementation
+
+    public interface IHandlerCallback {
+        void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis);
+    }
+
+    private final static ThreadLocal<IHandlerCallback> sCallbacks =
+        new ThreadLocal<IHandlerCallback>();
+
+    public static void setCallback(IHandlerCallback callback) {
+        sCallbacks.set(callback);
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
new file mode 100644
index 0000000..63711a7
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 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.os;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate implementing the native methods of android.os.SystemClock
+ *
+ * Through the layoutlib_create tool, the original native methods of SystemClock have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
+ * around to map int to instance of the delegate.
+ *
+ */
+public class SystemClock_Delegate {
+    private static long sBootTime = System.currentTimeMillis();
+
+    @LayoutlibDelegate
+    /*package*/ static boolean setCurrentTimeMillis(long millis) {
+        return true;
+    }
+
+    /**
+     * Returns milliseconds since boot, not counting time spent in deep sleep.
+     * <b>Note:</b> This value may get reset occasionally (before it would
+     * otherwise wrap around).
+     *
+     * @return milliseconds of non-sleep uptime since boot.
+     */
+    @LayoutlibDelegate
+    /*package*/ static long uptimeMillis() {
+        return System.currentTimeMillis() - sBootTime;
+    }
+
+    /**
+     * Returns milliseconds since boot, including time spent in sleep.
+     *
+     * @return elapsed milliseconds since boot.
+     */
+    @LayoutlibDelegate
+    /*package*/ static long elapsedRealtime() {
+        return System.currentTimeMillis() - sBootTime;
+    }
+
+    /**
+     * Returns milliseconds running in the current thread.
+     *
+     * @return elapsed milliseconds in the thread
+     */
+    @LayoutlibDelegate
+    /*package*/ static long currentThreadTimeMillis() {
+        return System.currentTimeMillis();
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/util/FloatMath.java b/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java
similarity index 67%
rename from tools/layoutlib/bridge/src/android/util/FloatMath.java
rename to tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java
index aae44f2..1df78c2 100644
--- a/tools/layoutlib/bridge/src/android/util/FloatMath.java
+++ b/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java
@@ -16,20 +16,23 @@
 
 package android.util;
 
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
 /**
- * Reimplements _Original_FloatMath with the standard libraries.
- * 
- * Math routines similar to those found in {@link java.lang.Math}. Performs
- * computations on {@code float} values directly without incurring the overhead
- * of conversions to and from {@code double}.
+ * Delegate implementing the native methods of android.util.FloatMath
  *
- * <p>On one platform, {@code FloatMath.sqrt(100)} executes in one third of the
- * time required by {@code java.lang.Math.sqrt(100)}.</p>
+ * Through the layoutlib_create tool, the original native methods of FloatMath have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
+ * around to map int to instance of the delegate.
+ *
  */
-public class FloatMath {
+/*package*/ final class FloatMath_Delegate {
 
     /** Prevents instantiation. */
-    private FloatMath() {}
+    private FloatMath_Delegate() {}
 
     /**
      * Returns the float conversion of the most positive (i.e. closest to
@@ -38,7 +41,8 @@
      * @param value to be converted
      * @return the floor of value
      */
-    public static float floor(float value) {
+    @LayoutlibDelegate
+    /*package*/ static float floor(float value) {
         return (float)Math.floor(value);
     }
 
@@ -49,7 +53,8 @@
      * @param value to be converted
      * @return the ceiling of value
      */
-    public static float ceil(float value) {
+    @LayoutlibDelegate
+    /*package*/ static float ceil(float value) {
         return (float)Math.ceil(value);
     }
 
@@ -59,7 +64,8 @@
      * @param angle to compute the cosine of, in radians
      * @return the sine of angle
      */
-    public static  float sin(float angle) {
+    @LayoutlibDelegate
+    /*package*/ static  float sin(float angle) {
         return (float)Math.sin(angle);
     }
 
@@ -69,7 +75,8 @@
      * @param angle to compute the cosine of, in radians
      * @return the cosine of angle
      */
-    public static float cos(float angle) {
+    @LayoutlibDelegate
+    /*package*/ static float cos(float angle) {
         return (float)Math.cos(angle);
     }
 
@@ -80,7 +87,8 @@
      * @param value to compute sqrt of
      * @return the square root of value
      */
-    public static float sqrt(float value) {
+    @LayoutlibDelegate
+    /*package*/ static float sqrt(float value) {
         return (float)Math.sqrt(value);
     }
 }
diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
new file mode 100644
index 0000000..0f3cf571
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 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.layoutlib.bridge.android.BridgeInflater;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.util.AttributeSet;
+
+import java.io.IOException;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link LayoutInflater}
+ *
+ * Through the layoutlib_create tool, the original  methods of LayoutInflater have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ */
+public class LayoutInflater_Delegate {
+
+    /**
+     * Recursive method used to descend down the xml hierarchy and instantiate
+     * views, instantiate their children, and then call onFinishInflate().
+     */
+    @LayoutlibDelegate
+    /*package*/ static void rInflate(LayoutInflater thisInflater,
+            XmlPullParser parser, View parent, final AttributeSet attrs,
+            boolean finishInflate) throws XmlPullParserException, IOException {
+
+        if (finishInflate == false) {
+            // this is a merge rInflate!
+            if (thisInflater instanceof BridgeInflater) {
+                ((BridgeInflater) thisInflater).setIsInMerge(true);
+            }
+        }
+
+        // ---- START DEFAULT IMPLEMENTATION.
+
+        final int depth = parser.getDepth();
+        int type;
+
+        while (((type = parser.next()) != XmlPullParser.END_TAG ||
+                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final String name = parser.getName();
+
+            if (LayoutInflater.TAG_REQUEST_FOCUS.equals(name)) {
+                thisInflater.parseRequestFocus(parser, parent);
+            } else if (LayoutInflater.TAG_INCLUDE.equals(name)) {
+                if (parser.getDepth() == 0) {
+                    throw new InflateException("<include /> cannot be the root element");
+                }
+                thisInflater.parseInclude(parser, parent, attrs);
+            } else if (LayoutInflater.TAG_MERGE.equals(name)) {
+                throw new InflateException("<merge /> must be the root element");
+            } else {
+                final View view = thisInflater.createViewFromTag(parent, name, attrs);
+                final ViewGroup viewGroup = (ViewGroup) parent;
+                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
+                thisInflater.rInflate(parser, view, attrs, true);
+                viewGroup.addView(view, params);
+            }
+        }
+
+        if (finishInflate) parent.onFinishInflate();
+
+        // ---- END DEFAULT IMPLEMENTATION.
+
+        if (finishInflate == false) {
+            // this is a merge rInflate!
+            if (thisInflater instanceof BridgeInflater) {
+                ((BridgeInflater) thisInflater).setIsInMerge(false);
+            }
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/View_Delegate.java b/tools/layoutlib/bridge/src/android/view/View_Delegate.java
new file mode 100644
index 0000000..8215f7c
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/View_Delegate.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 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 View}
+ *
+ * Through the layoutlib_create tool, the original  methods of View have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ */
+public class View_Delegate {
+
+    @LayoutlibDelegate
+    /*package*/ static boolean isInEditMode(View thisView) {
+        return true;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/internal/util/XmlUtils_Delegate.java b/tools/layoutlib/bridge/src/com/android/internal/util/XmlUtils_Delegate.java
new file mode 100644
index 0000000..bf998b8
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/internal/util/XmlUtils_Delegate.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011 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.internal.util;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link XmlUtils}
+ *
+ * Through the layoutlib_create tool, the original  methods of XmlUtils have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ */
+public class XmlUtils_Delegate {
+
+    @LayoutlibDelegate
+    /*package*/ static final int convertValueToInt(CharSequence charSeq, int defaultValue) {
+        if (null == charSeq)
+            return defaultValue;
+
+        String nm = charSeq.toString();
+
+        // This code is copied from the original implementation. The issue is that
+        // The Dalvik libraries are able to handle Integer.parse("XXXXXXXX", 16) where XXXXXXX
+        // is > 80000000 but the Java VM cannot.
+
+        int sign = 1;
+        int index = 0;
+        int len = nm.length();
+        int base = 10;
+
+        if ('-' == nm.charAt(0)) {
+            sign = -1;
+            index++;
+        }
+
+        if ('0' == nm.charAt(index)) {
+            //  Quick check for a zero by itself
+            if (index == (len - 1))
+                return 0;
+
+            char c = nm.charAt(index + 1);
+
+            if ('x' == c || 'X' == c) {
+                index += 2;
+                base = 16;
+            } else {
+                index++;
+                base = 8;
+            }
+        }
+        else if ('#' == nm.charAt(index)) {
+            index++;
+            base = 16;
+        }
+
+        return ((int)Long.parseLong(nm.substring(index), base)) * sign;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index f91f601..acc7379 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -16,68 +16,47 @@
 
 package com.android.layoutlib.bridge;
 
-import com.android.internal.util.XmlUtils;
-import com.android.layoutlib.api.ILayoutBridge;
-import com.android.layoutlib.api.ILayoutLog;
-import com.android.layoutlib.api.ILayoutResult;
-import com.android.layoutlib.api.IProjectCallback;
-import com.android.layoutlib.api.IResourceValue;
-import com.android.layoutlib.api.IStyleResourceValue;
-import com.android.layoutlib.api.IXmlPullParser;
-import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo;
-import com.android.layoutlib.bridge.LayoutResult.LayoutViewInfo;
-import com.android.ninepatch.NinePatch;
+import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN;
+import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
+
+import com.android.ide.common.rendering.api.Capability;
+import com.android.ide.common.rendering.api.DrawableParams;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.Result;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.layoutlib.bridge.android.BridgeAssetManager;
+import com.android.layoutlib.bridge.impl.FontLoader;
+import com.android.layoutlib.bridge.impl.RenderDrawable;
+import com.android.layoutlib.bridge.impl.RenderSessionImpl;
+import com.android.ninepatch.NinePatchChunk;
+import com.android.resources.ResourceType;
 import com.android.tools.layoutlib.create.MethodAdapter;
 import com.android.tools.layoutlib.create.OverrideMethod;
+import com.android.util.Pair;
 
-import android.content.res.Configuration;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.Region;
 import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
+import android.graphics.Typeface_Delegate;
 import android.os.Looper;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.view.BridgeInflater;
-import android.view.InputChannel;
-import android.view.IWindow;
-import android.view.IWindowSession;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.SurfaceView;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.View.AttachInfo;
-import android.view.View.MeasureSpec;
-import android.view.WindowManager.LayoutParams;
-import android.widget.FrameLayout;
-import android.widget.TabHost;
-import android.widget.TabWidget;
 
+import java.io.File;
 import java.lang.ref.SoftReference;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
-import java.util.Collection;
+import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.locks.ReentrantLock;
 
 /**
  * Main entry point of the LayoutLib Bridge.
  * <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call
- * {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}.
+ * {@link #createScene(SceneParams)}
  */
-public final class Bridge implements ILayoutBridge {
-
-    private static final int DEFAULT_TITLE_BAR_HEIGHT = 25;
-    private static final int DEFAULT_STATUS_BAR_HEIGHT = 25;
+public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
 
     public static class StaticMethodNotImplementedException extends RuntimeException {
         private static final long serialVersionUID = 1L;
@@ -88,84 +67,139 @@
     }
 
     /**
-     * Maps from id to resource name/type. This is for android.R only.
+     * Lock to ensure only one rendering/inflating happens at a time.
+     * This is due to some singleton in the Android framework.
      */
-    private final static Map<Integer, String[]> sRMap = new HashMap<Integer, String[]>();
+    private final static ReentrantLock sLock = new ReentrantLock();
+
+    /**
+     * Maps from id to resource type/name. This is for android.R only.
+     */
+    private final static Map<Integer, Pair<ResourceType, String>> sRMap =
+        new HashMap<Integer, Pair<ResourceType, String>>();
+
     /**
      * Same as sRMap except for int[] instead of int resources. This is for android.R only.
      */
-    private final static Map<int[], String> sRArrayMap = new HashMap<int[], String>();
+    private final static Map<IntArray, String> sRArrayMap = new HashMap<IntArray, String>();
     /**
      * Reverse map compared to sRMap, resource type -> (resource name -> id).
      * This is for android.R only.
      */
-    private final static Map<String, Map<String, Integer>> sRFullMap =
-        new HashMap<String, Map<String,Integer>>();
+    private final static Map<ResourceType, Map<String, Integer>> sRFullMap =
+        new EnumMap<ResourceType, Map<String,Integer>>(ResourceType.class);
 
     private final static Map<Object, Map<String, SoftReference<Bitmap>>> sProjectBitmapCache =
         new HashMap<Object, Map<String, SoftReference<Bitmap>>>();
-    private final static Map<Object, Map<String, SoftReference<NinePatch>>> sProject9PatchCache =
-        new HashMap<Object, Map<String, SoftReference<NinePatch>>>();
+    private final static Map<Object, Map<String, SoftReference<NinePatchChunk>>> sProject9PatchCache =
+        new HashMap<Object, Map<String, SoftReference<NinePatchChunk>>>();
 
     private final static Map<String, SoftReference<Bitmap>> sFrameworkBitmapCache =
         new HashMap<String, SoftReference<Bitmap>>();
-    private final static Map<String, SoftReference<NinePatch>> sFramework9PatchCache =
-        new HashMap<String, SoftReference<NinePatch>>();
+    private final static Map<String, SoftReference<NinePatchChunk>> sFramework9PatchCache =
+        new HashMap<String, SoftReference<NinePatchChunk>>();
 
     private static Map<String, Map<String, Integer>> sEnumValueMap;
+    private static Map<String, String> sPlatformProperties;
 
     /**
-     * A default logger than prints to stdout/stderr.
+     * int[] wrapper to use as keys in maps.
      */
-    private final static ILayoutLog sDefaultLogger = new ILayoutLog() {
-        public void error(String message) {
+    private final static class IntArray {
+        private int[] mArray;
+
+        private IntArray() {
+            // do nothing
+        }
+
+        private IntArray(int[] a) {
+            mArray = a;
+        }
+
+        private void set(int[] a) {
+            mArray = a;
+        }
+
+        @Override
+        public int hashCode() {
+            return Arrays.hashCode(mArray);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            if (obj == null) return false;
+            if (getClass() != obj.getClass()) return false;
+
+            IntArray other = (IntArray) obj;
+            if (!Arrays.equals(mArray, other.mArray)) return false;
+            return true;
+        }
+    }
+
+    /** Instance of IntArrayWrapper to be reused in {@link #resolveResourceId(int[])}. */
+    private final static IntArray sIntArrayWrapper = new IntArray();
+
+    /**
+     * A default log than prints to stdout/stderr.
+     */
+    private final static LayoutLog sDefaultLog = new LayoutLog() {
+        @Override
+        public void error(String tag, String message, Object data) {
             System.err.println(message);
         }
 
-        public void error(Throwable t) {
-            String message = t.getMessage();
-            if (message == null) {
-                message = t.getClass().getName();
-            }
-
+        @Override
+        public void error(String tag, String message, Throwable throwable, Object data) {
             System.err.println(message);
         }
 
-        public void warning(String message) {
+        @Override
+        public void warning(String tag, String message, Object data) {
             System.out.println(message);
         }
     };
 
     /**
-     * Logger defined during a compute layout operation.
-     * <p/>
-     * This logger is generally set to {@link #sDefaultLogger} except during rendering
-     * operations when it might be set to a specific provided logger.
-     * <p/>
-     * To change this value, use a block synchronized on {@link #sDefaultLogger}.
+     * Current log.
      */
-    private static ILayoutLog sLogger = sDefaultLogger;
+    private static LayoutLog sCurrentLog = sDefaultLog;
 
-    /*
-     * (non-Javadoc)
-     * @see com.android.layoutlib.api.ILayoutBridge#getApiLevel()
-     */
+    private EnumSet<Capability> mCapabilities;
+
+    @Override
     public int getApiLevel() {
-        return API_CURRENT;
+        return com.android.ide.common.rendering.api.Bridge.API_CURRENT;
     }
 
-    /*
-     * (non-Javadoc)
-     * @see com.android.layoutlib.api.ILayoutLibBridge#init(java.lang.String, java.util.Map)
-     */
-    public boolean init(
-            String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap) {
-
-        return sinit(fontOsLocation, enumValueMap);
+    @Override
+    public EnumSet<Capability> getCapabilities() {
+        return mCapabilities;
     }
 
-    private static synchronized boolean sinit(String fontOsLocation,
-            Map<String, Map<String, Integer>> enumValueMap) {
+    @Override
+    public boolean init(Map<String,String> platformProperties,
+            File fontLocation,
+            Map<String, Map<String, Integer>> enumValueMap,
+            LayoutLog log) {
+        sPlatformProperties = platformProperties;
+        sEnumValueMap = enumValueMap;
+
+        // don't use EnumSet.allOf(), because the bridge doesn't come with its specific version
+        // of layoutlib_api. It is provided by the client which could have a more recent version
+        // with newer, unsupported capabilities.
+        mCapabilities = EnumSet.of(
+                Capability.UNBOUND_RENDERING,
+                Capability.CUSTOM_BACKGROUND_COLOR,
+                Capability.RENDER,
+                Capability.LAYOUT_ONLY,
+                Capability.EMBEDDED_LAYOUT,
+                Capability.VIEW_MANIPULATION,
+                Capability.PLAY_ANIMATION,
+                Capability.ANIMATED_VIEW_MANIPULATION);
+
+
+        BridgeAssetManager.initSystem();
 
         // When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener
         // on static (native) methods which prints the signature on the console and
@@ -182,12 +216,8 @@
             OverrideMethod.setDefaultListener(new MethodAdapter() {
                 @Override
                 public void onInvokeV(String signature, boolean isNative, Object caller) {
-                    if (sLogger != null) {
-                        synchronized (sDefaultLogger) {
-                            sLogger.error("Missing Stub: " + signature +
-                                    (isNative ? " (native)" : ""));
-                        }
-                    }
+                    sDefaultLog.error(null, "Missing Stub: " + signature +
+                            (isNative ? " (native)" : ""), null /*data*/);
 
                     if (debug.equalsIgnoreCase("throw")) {
                         // Throwing this exception doesn't seem that useful. It breaks
@@ -200,293 +230,138 @@
             });
         }
 
-        // Override View.isInEditMode to return true.
-        //
-        // This allows custom views that are drawn in the Graphical Layout Editor to adapt their
-        // rendering for preview. Most important this let custom views know that they can't expect
-        // the rest of their activities to be alive.
-        OverrideMethod.setMethodListener("android.view.View#isInEditMode()Z",
-            new MethodAdapter() {
-                @Override
-                public int onInvokeI(String signature, boolean isNative, Object caller) {
-                    return 1;
-                }
-            }
-        );
-
         // load the fonts.
-        FontLoader fontLoader = FontLoader.create(fontOsLocation);
+        FontLoader fontLoader = FontLoader.create(fontLocation.getAbsolutePath());
         if (fontLoader != null) {
-            Typeface.init(fontLoader);
+            Typeface_Delegate.init(fontLoader);
         } else {
             return false;
         }
 
-        sEnumValueMap = enumValueMap;
-
         // now parse com.android.internal.R (and only this one as android.R is a subset of
         // the internal version), and put the content in the maps.
         try {
-            // WARNING: this only works because the class is already loaded, and therefore
-            // the objects returned by Field.get() are the same as the ones used by
-            // the code accessing the R class.
-            // int[] does not implement equals/hashCode, and if the parsing used a different class
-            // loader for the R class, this would NOT work.
             Class<?> r = com.android.internal.R.class;
 
             for (Class<?> inner : r.getDeclaredClasses()) {
-                String resType = inner.getSimpleName();
+                String resTypeName = inner.getSimpleName();
+                ResourceType resType = ResourceType.getEnum(resTypeName);
+                if (resType != null) {
+                    Map<String, Integer> fullMap = new HashMap<String, Integer>();
+                    sRFullMap.put(resType, fullMap);
 
-                Map<String, Integer> fullMap = new HashMap<String, Integer>();
-                sRFullMap.put(resType, fullMap);
-
-                for (Field f : inner.getDeclaredFields()) {
-                    // only process static final fields. Since the final attribute may have
-                    // been altered by layoutlib_create, we only check static
-                    int modifiers = f.getModifiers();
-                    if (Modifier.isStatic(modifiers)) {
-                        Class<?> type = f.getType();
-                        if (type.isArray() && type.getComponentType() == int.class) {
-                            // if the object is an int[] we put it in sRArrayMap
-                            sRArrayMap.put((int[]) f.get(null), f.getName());
-                        } else if (type == int.class) {
-                            Integer value = (Integer) f.get(null);
-                            sRMap.put(value, new String[] { f.getName(), resType });
-                            fullMap.put(f.getName(), value);
-                        } else {
-                            assert false;
+                    for (Field f : inner.getDeclaredFields()) {
+                        // only process static final fields. Since the final attribute may have
+                        // been altered by layoutlib_create, we only check static
+                        int modifiers = f.getModifiers();
+                        if (Modifier.isStatic(modifiers)) {
+                            Class<?> type = f.getType();
+                            if (type.isArray() && type.getComponentType() == int.class) {
+                                // if the object is an int[] we put it in sRArrayMap using an IntArray
+                                // wrapper that properly implements equals and hashcode for the array
+                                // objects, as required by the map contract.
+                                sRArrayMap.put(new IntArray((int[]) f.get(null)), f.getName());
+                            } else if (type == int.class) {
+                                Integer value = (Integer) f.get(null);
+                                sRMap.put(value, Pair.of(resType, f.getName()));
+                                fullMap.put(f.getName(), value);
+                            } else {
+                                assert false;
+                            }
                         }
                     }
                 }
             }
-        } catch (IllegalArgumentException e) {
-            // FIXME: log/return the error (there's no logger object at this point!)
-            e.printStackTrace();
-            return false;
-        } catch (IllegalAccessException e) {
-            e.printStackTrace();
+        } catch (Throwable throwable) {
+            if (log != null) {
+                log.error(LayoutLog.TAG_BROKEN,
+                        "Failed to load com.android.internal.R from the layout library jar",
+                        throwable);
+            }
             return false;
         }
 
         return true;
     }
 
-    /*
-     * For compatilibty purposes, we implement the old deprecated version of computeLayout.
-     * (non-Javadoc)
-     * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog)
-     */
-    @Deprecated
-    public ILayoutResult computeLayout(IXmlPullParser layoutDescription,
-            Object projectKey,
-            int screenWidth, int screenHeight, String themeName,
-            Map<String, Map<String, IResourceValue>> projectResources,
-            Map<String, Map<String, IResourceValue>> frameworkResources,
-            IProjectCallback customViewLoader, ILayoutLog logger) {
-        boolean isProjectTheme = false;
-        if (themeName.charAt(0) == '*') {
-            themeName = themeName.substring(1);
-            isProjectTheme = true;
-        }
+    @Override
+    public boolean dispose() {
+        BridgeAssetManager.clearSystem();
 
-        return computeLayout(layoutDescription, projectKey,
-                screenWidth, screenHeight, DisplayMetrics.DENSITY_DEFAULT,
-                DisplayMetrics.DENSITY_DEFAULT, DisplayMetrics.DENSITY_DEFAULT,
-                themeName, isProjectTheme,
-                projectResources, frameworkResources, customViewLoader, logger);
+        // dispose of the default typeface.
+        Typeface.sDefaults = null;
+
+        return true;
     }
 
-    /*
-     * For compatilibty purposes, we implement the old deprecated version of computeLayout.
-     * (non-Javadoc)
-     * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog)
+    /**
+     * Starts a layout session by inflating and rendering it. The method returns a
+     * {@link RenderSession} on which further actions can be taken.
+     *
+     * @param params the {@link SessionParams} object with all the information necessary to create
+     *           the scene.
+     * @return a new {@link RenderSession} object that contains the result of the layout.
+     * @since 5
      */
-    @Deprecated
-    public ILayoutResult computeLayout(IXmlPullParser layoutDescription, Object projectKey,
-            int screenWidth, int screenHeight, String themeName, boolean isProjectTheme,
-            Map<String, Map<String, IResourceValue>> projectResources,
-            Map<String, Map<String, IResourceValue>> frameworkResources,
-            IProjectCallback customViewLoader, ILayoutLog logger) {
-        return computeLayout(layoutDescription, projectKey,
-                screenWidth, screenHeight, DisplayMetrics.DENSITY_DEFAULT,
-                DisplayMetrics.DENSITY_DEFAULT, DisplayMetrics.DENSITY_DEFAULT,
-                themeName, isProjectTheme,
-                projectResources, frameworkResources, customViewLoader, logger);
-    }
-
-    /*
-     * For compatilibty purposes, we implement the old deprecated version of computeLayout.
-     * (non-Javadoc)
-     * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, int, float, float, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog)
-     */
-    public ILayoutResult computeLayout(IXmlPullParser layoutDescription, Object projectKey,
-            int screenWidth, int screenHeight, int density, float xdpi, float ydpi,
-            String themeName, boolean isProjectTheme,
-            Map<String, Map<String, IResourceValue>> projectResources,
-            Map<String, Map<String, IResourceValue>> frameworkResources,
-            IProjectCallback customViewLoader, ILayoutLog logger) {
-        return computeLayout(layoutDescription, projectKey,
-                screenWidth, screenHeight, false /* renderFullSize */,
-                density, xdpi, ydpi, themeName, isProjectTheme,
-                projectResources, frameworkResources, customViewLoader, logger);
-    }
-
-    /*
-     * (non-Javadoc)
-     * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, boolean, int, float, float, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog)
-     */
-    public ILayoutResult computeLayout(IXmlPullParser layoutDescription, Object projectKey,
-            int screenWidth, int screenHeight, boolean renderFullSize,
-            int density, float xdpi, float ydpi,
-            String themeName, boolean isProjectTheme,
-            Map<String, Map<String, IResourceValue>> projectResources,
-            Map<String, Map<String, IResourceValue>> frameworkResources,
-            IProjectCallback customViewLoader, ILayoutLog logger) {
-        if (logger == null) {
-            logger = sDefaultLogger;
-        }
-
-        synchronized (sDefaultLogger) {
-            sLogger = logger;
-        }
-
-        // find the current theme and compute the style inheritance map
-        Map<IStyleResourceValue, IStyleResourceValue> styleParentMap =
-            new HashMap<IStyleResourceValue, IStyleResourceValue>();
-
-        IStyleResourceValue currentTheme = computeStyleMaps(themeName, isProjectTheme,
-                projectResources.get(BridgeConstants.RES_STYLE),
-                frameworkResources.get(BridgeConstants.RES_STYLE), styleParentMap);
-
-        BridgeContext context = null;
+    @Override
+    public RenderSession createSession(SessionParams params) {
         try {
-            // setup the display Metrics.
-            DisplayMetrics metrics = new DisplayMetrics();
-            metrics.densityDpi = density;
-            metrics.density = density / (float) DisplayMetrics.DENSITY_DEFAULT;
-            metrics.scaledDensity = metrics.density;
-            metrics.widthPixels = screenWidth;
-            metrics.heightPixels = screenHeight;
-            metrics.xdpi = xdpi;
-            metrics.ydpi = ydpi;
-
-            context = new BridgeContext(projectKey, metrics, currentTheme, projectResources,
-                    frameworkResources, styleParentMap, customViewLoader, logger);
-            BridgeInflater inflater = new BridgeInflater(context, customViewLoader);
-            context.setBridgeInflater(inflater);
-
-            IResourceValue windowBackground = null;
-            int screenOffset = 0;
-            if (currentTheme != null) {
-                windowBackground = context.findItemInStyle(currentTheme, "windowBackground");
-                windowBackground = context.resolveResValue(windowBackground);
-
-                screenOffset = getScreenOffset(frameworkResources, currentTheme, context);
-            }
-
-            // we need to make sure the Looper has been initialized for this thread.
-            // this is required for View that creates Handler objects.
-            if (Looper.myLooper() == null) {
-                Looper.prepare();
-            }
-
-            BridgeXmlBlockParser parser = new BridgeXmlBlockParser(layoutDescription,
-                    context, false /* platformResourceFlag */);
-
-            ViewGroup root = new FrameLayout(context);
-
-            View view = inflater.inflate(parser, root);
-
-            // post-inflate process. For now this supports TabHost/TabWidget
-            postInflateProcess(view, customViewLoader);
-
-            // set the AttachInfo on the root view.
-            AttachInfo info = new AttachInfo(new WindowSession(), new Window(),
-                    new Handler(), null);
-            info.mHasWindowFocus = true;
-            info.mWindowVisibility = View.VISIBLE;
-            info.mInTouchMode = false; // this is so that we can display selections.
-            root.dispatchAttachedToWindow(info, 0);
-
-            // get the background drawable
-            if (windowBackground != null) {
-                Drawable d = ResourceHelper.getDrawable(windowBackground,
-                        context, true /* isFramework */);
-                root.setBackgroundDrawable(d);
-            }
-
-            // measure the views
-            int w_spec, h_spec;
-
-            if (renderFullSize) {
-                // measure the full size needed by the layout.
-                w_spec = MeasureSpec.makeMeasureSpec(screenWidth,
-                        MeasureSpec.UNSPECIFIED); // this lets us know the actual needed size
-                h_spec = MeasureSpec.makeMeasureSpec(screenHeight - screenOffset,
-                        MeasureSpec.UNSPECIFIED); // this lets us know the actual needed size
-                view.measure(w_spec, h_spec);
-
-                int neededWidth = root.getChildAt(0).getMeasuredWidth();
-                if (neededWidth > screenWidth) {
-                    screenWidth = neededWidth;
+            Result lastResult = SUCCESS.createResult();
+            RenderSessionImpl scene = new RenderSessionImpl(params);
+            try {
+                prepareThread();
+                lastResult = scene.init(params.getTimeout());
+                if (lastResult.isSuccess()) {
+                    lastResult = scene.inflate();
+                    if (lastResult.isSuccess()) {
+                        lastResult = scene.render(true /*freshRender*/);
+                    }
                 }
-
-                int neededHeight = root.getChildAt(0).getMeasuredHeight();
-                if (neededHeight > screenHeight - screenOffset) {
-                    screenHeight = neededHeight + screenOffset;
-                }
+            } finally {
+                scene.release();
+                cleanupThread();
             }
 
-            // remeasure with only the size we need
-            // This must always be done before the call to layout
-            w_spec = MeasureSpec.makeMeasureSpec(screenWidth, MeasureSpec.EXACTLY);
-            h_spec = MeasureSpec.makeMeasureSpec(screenHeight - screenOffset,
-                    MeasureSpec.EXACTLY);
-            view.measure(w_spec, h_spec);
-
-            // now do the layout.
-            view.layout(0, screenOffset, screenWidth, screenHeight);
-
-            // draw the views
-            Canvas canvas = new Canvas(screenWidth, screenHeight - screenOffset, logger);
-
-            root.draw(canvas);
-            canvas.dispose();
-
-            return new LayoutResult(visit(((ViewGroup)view).getChildAt(0), context),
-                    canvas.getImage());
-        } catch (PostInflateException e) {
-            return new LayoutResult(ILayoutResult.ERROR, "Error during post inflation process:\n"
-                    + e.getMessage());
-        } catch (Throwable e) {
+            return new BridgeRenderSession(scene, lastResult);
+        } catch (Throwable t) {
             // get the real cause of the exception.
-            Throwable t = e;
-            while (t.getCause() != null) {
-                t = t.getCause();
+            Throwable t2 = t;
+            while (t2.getCause() != null) {
+                t2 = t.getCause();
             }
-
-            // log it
-            logger.error(t);
-
-            // then return with an ERROR status and the message from the real exception
-            return new LayoutResult(ILayoutResult.ERROR,
-                    t.getClass().getSimpleName() + ": " + t.getMessage());
-        } finally {
-            // Make sure to remove static references, otherwise we could not unload the lib
-            BridgeResources.clearSystem();
-            BridgeAssetManager.clearSystem();
-
-            // Remove the global logger
-            synchronized (sDefaultLogger) {
-                sLogger = sDefaultLogger;
-            }
+            return new BridgeRenderSession(null,
+                    ERROR_UNKNOWN.createResult(t2.getMessage(), t));
         }
     }
 
-    /*
-     * (non-Javadoc)
-     * @see com.android.layoutlib.api.ILayoutLibBridge#clearCaches(java.lang.Object)
-     */
+    @Override
+    public Result renderDrawable(DrawableParams params) {
+        try {
+            Result lastResult = SUCCESS.createResult();
+            RenderDrawable action = new RenderDrawable(params);
+            try {
+                prepareThread();
+                lastResult = action.init(params.getTimeout());
+                if (lastResult.isSuccess()) {
+                    lastResult = action.render();
+                }
+            } finally {
+                action.release();
+                cleanupThread();
+            }
+
+            return lastResult;
+        } catch (Throwable t) {
+            // get the real cause of the exception.
+            Throwable t2 = t;
+            while (t2.getCause() != null) {
+                t2 = t.getCause();
+            }
+            return ERROR_UNKNOWN.createResult(t2.getMessage(), t);
+        }
+    }
+
+    @Override
     public void clearCaches(Object projectKey) {
         if (projectKey != null) {
             sProjectBitmapCache.remove(projectKey);
@@ -495,22 +370,71 @@
     }
 
     /**
+     * Returns the lock for the bridge
+     */
+    public static ReentrantLock getLock() {
+        return sLock;
+    }
+
+    /**
+     * Prepares the current thread for rendering.
+     *
+     * Note that while this can be called several time, the first call to {@link #cleanupThread()}
+     * will do the clean-up, and make the thread unable to do further scene actions.
+     */
+    public static void prepareThread() {
+        // we need to make sure the Looper has been initialized for this thread.
+        // this is required for View that creates Handler objects.
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+    }
+
+    /**
+     * Cleans up thread-specific data. After this, the thread cannot be used for scene actions.
+     * <p>
+     * Note that it doesn't matter how many times {@link #prepareThread()} was called, a single
+     * call to this will prevent the thread from doing further scene actions
+     */
+    public static void cleanupThread() {
+        // clean up the looper
+        Looper.sThreadLocal.remove();
+    }
+
+    public static LayoutLog getLog() {
+        return sCurrentLog;
+    }
+
+    public static void setLog(LayoutLog log) {
+        // check only the thread currently owning the lock can do this.
+        if (sLock.isHeldByCurrentThread() == false) {
+            throw new IllegalStateException("scene must be acquired first. see #acquire(long)");
+        }
+
+        if (log != null) {
+            sCurrentLog = log;
+        } else {
+            sCurrentLog = sDefaultLog;
+        }
+    }
+
+    /**
      * Returns details of a framework resource from its integer value.
      * @param value the integer value
-     * @return an array of 2 strings containing the resource name and type, or null if the id
-     * does not match any resource.
+     * @return a Pair containing the resource type and name, or null if the id
+     *     does not match any resource.
      */
-    public static String[] resolveResourceValue(int value) {
+    public static Pair<ResourceType, String> resolveResourceId(int value) {
         return sRMap.get(value);
-
     }
 
     /**
      * Returns the name of a framework resource whose value is an int array.
      * @param array
      */
-    public static String resolveResourceValue(int[] array) {
-        return sRArrayMap.get(array);
+    public static String resolveResourceId(int[] array) {
+        sIntArrayWrapper.set(array);
+        return sRArrayMap.get(sIntArrayWrapper);
     }
 
     /**
@@ -519,7 +443,7 @@
      * @param name the name of the resource.
      * @return an {@link Integer} containing the resource id, or null if no resource were found.
      */
-    public static Integer getResourceValue(String type, String name) {
+    public static Integer getResourceId(ResourceType type, String name) {
         Map<String, Integer> map = sRFullMap.get(type);
         if (map != null) {
             return map.get(name);
@@ -528,7 +452,10 @@
         return null;
     }
 
-    static Map<String, Integer> getEnumValues(String attributeName) {
+    /**
+     * Returns the list of possible enums for a given attribute name.
+     */
+    public static Map<String, Integer> getEnumValues(String attributeName) {
         if (sEnumValueMap != null) {
             return sEnumValueMap.get(attributeName);
         }
@@ -537,348 +464,10 @@
     }
 
     /**
-     * Visits a View and its children and generate a {@link ILayoutViewInfo} containing the
-     * bounds of all the views.
-     * @param view the root View
-     * @param context the context.
+     * Returns the platform build properties.
      */
-    private ILayoutViewInfo visit(View view, BridgeContext context) {
-        if (view == null) {
-            return null;
-        }
-
-        LayoutViewInfo result = new LayoutViewInfo(view.getClass().getName(),
-                context.getViewKey(view),
-                view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
-
-        if (view instanceof ViewGroup) {
-            ViewGroup group = ((ViewGroup) view);
-            int n = group.getChildCount();
-            ILayoutViewInfo[] children = new ILayoutViewInfo[n];
-            for (int i = 0; i < group.getChildCount(); i++) {
-                children[i] = visit(group.getChildAt(i), context);
-            }
-            result.setChildren(children);
-        }
-
-        return result;
-    }
-
-    /**
-     * Compute style information from the given list of style for the project and framework.
-     * @param themeName the name of the current theme.  In order to differentiate project and
-     * platform themes sharing the same name, all project themes must be prepended with
-     * a '*' character.
-     * @param isProjectTheme Is this a project theme
-     * @param inProjectStyleMap the project style map
-     * @param inFrameworkStyleMap the framework style map
-     * @param outInheritanceMap the map of style inheritance. This is filled by the method
-     * @return the {@link IStyleResourceValue} matching <var>themeName</var>
-     */
-    private IStyleResourceValue computeStyleMaps(
-            String themeName, boolean isProjectTheme, Map<String,
-            IResourceValue> inProjectStyleMap, Map<String, IResourceValue> inFrameworkStyleMap,
-            Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) {
-
-        if (inProjectStyleMap != null && inFrameworkStyleMap != null) {
-            // first, get the theme
-            IResourceValue theme = null;
-
-            // project theme names have been prepended with a *
-            if (isProjectTheme) {
-                theme = inProjectStyleMap.get(themeName);
-            } else {
-                theme = inFrameworkStyleMap.get(themeName);
-            }
-
-            if (theme instanceof IStyleResourceValue) {
-                // compute the inheritance map for both the project and framework styles
-                computeStyleInheritance(inProjectStyleMap.values(), inProjectStyleMap,
-                        inFrameworkStyleMap, outInheritanceMap);
-
-                // Compute the style inheritance for the framework styles/themes.
-                // Since, for those, the style parent values do not contain 'android:'
-                // we want to force looking in the framework style only to avoid using
-                // similarly named styles from the project.
-                // To do this, we pass null in lieu of the project style map.
-                computeStyleInheritance(inFrameworkStyleMap.values(), null /*inProjectStyleMap */,
-                        inFrameworkStyleMap, outInheritanceMap);
-
-                return (IStyleResourceValue)theme;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Compute the parent style for all the styles in a given list.
-     * @param styles the styles for which we compute the parent.
-     * @param inProjectStyleMap the map of project styles.
-     * @param inFrameworkStyleMap the map of framework styles.
-     * @param outInheritanceMap the map of style inheritance. This is filled by the method.
-     */
-    private void computeStyleInheritance(Collection<IResourceValue> styles,
-            Map<String, IResourceValue> inProjectStyleMap,
-            Map<String, IResourceValue> inFrameworkStyleMap,
-            Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) {
-        for (IResourceValue value : styles) {
-            if (value instanceof IStyleResourceValue) {
-                IStyleResourceValue style = (IStyleResourceValue)value;
-                IStyleResourceValue parentStyle = null;
-
-                // first look for a specified parent.
-                String parentName = style.getParentStyle();
-
-                // no specified parent? try to infer it from the name of the style.
-                if (parentName == null) {
-                    parentName = getParentName(value.getName());
-                }
-
-                if (parentName != null) {
-                    parentStyle = getStyle(parentName, inProjectStyleMap, inFrameworkStyleMap);
-
-                    if (parentStyle != null) {
-                        outInheritanceMap.put(style, parentStyle);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Searches for and returns the {@link IStyleResourceValue} from a given name.
-     * <p/>The format of the name can be:
-     * <ul>
-     * <li>[android:]&lt;name&gt;</li>
-     * <li>[android:]style/&lt;name&gt;</li>
-     * <li>@[android:]style/&lt;name&gt;</li>
-     * </ul>
-     * @param parentName the name of the style.
-     * @param inProjectStyleMap the project style map. Can be <code>null</code>
-     * @param inFrameworkStyleMap the framework style map.
-     * @return The matching {@link IStyleResourceValue} object or <code>null</code> if not found.
-     */
-    private IStyleResourceValue getStyle(String parentName,
-            Map<String, IResourceValue> inProjectStyleMap,
-            Map<String, IResourceValue> inFrameworkStyleMap) {
-        boolean frameworkOnly = false;
-
-        String name = parentName;
-
-        // remove the useless @ if it's there
-        if (name.startsWith(BridgeConstants.PREFIX_RESOURCE_REF)) {
-            name = name.substring(BridgeConstants.PREFIX_RESOURCE_REF.length());
-        }
-
-        // check for framework identifier.
-        if (name.startsWith(BridgeConstants.PREFIX_ANDROID)) {
-            frameworkOnly = true;
-            name = name.substring(BridgeConstants.PREFIX_ANDROID.length());
-        }
-
-        // at this point we could have the format <type>/<name>. we want only the name as long as
-        // the type is style.
-        if (name.startsWith(BridgeConstants.REFERENCE_STYLE)) {
-            name = name.substring(BridgeConstants.REFERENCE_STYLE.length());
-        } else if (name.indexOf('/') != -1) {
-            return null;
-        }
-
-        IResourceValue parent = null;
-
-        // if allowed, search in the project resources.
-        if (frameworkOnly == false && inProjectStyleMap != null) {
-            parent = inProjectStyleMap.get(name);
-        }
-
-        // if not found, then look in the framework resources.
-        if (parent == null) {
-            parent = inFrameworkStyleMap.get(name);
-        }
-
-        // make sure the result is the proper class type and return it.
-        if (parent instanceof IStyleResourceValue) {
-            return (IStyleResourceValue)parent;
-        }
-
-        sLogger.error(String.format("Unable to resolve parent style name: %s", parentName));
-
-        return null;
-    }
-
-    /**
-     * Computes the name of the parent style, or <code>null</code> if the style is a root style.
-     */
-    private String getParentName(String styleName) {
-        int index = styleName.lastIndexOf('.');
-        if (index != -1) {
-            return styleName.substring(0, index);
-        }
-
-        return null;
-    }
-
-    /**
-     * Returns the top screen offset. This depends on whether the current theme defines the user
-     * of the title and status bars.
-     * @param frameworkResources The framework resources
-     * @param currentTheme The current theme
-     * @param context The context
-     * @return the pixel height offset
-     */
-    private int getScreenOffset(Map<String, Map<String, IResourceValue>> frameworkResources,
-            IStyleResourceValue currentTheme, BridgeContext context) {
-        int offset = 0;
-
-        // get the title bar flag from the current theme.
-        IResourceValue value = context.findItemInStyle(currentTheme, "windowNoTitle");
-
-        // because it may reference something else, we resolve it.
-        value = context.resolveResValue(value);
-
-        // if there's a value and it's true (default is false)
-        if (value == null || value.getValue() == null ||
-                XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) {
-            // default size of the window title bar
-            int defaultOffset = DEFAULT_TITLE_BAR_HEIGHT;
-
-            // get value from the theme.
-            value = context.findItemInStyle(currentTheme, "windowTitleSize");
-
-            // resolve it
-            value = context.resolveResValue(value);
-
-            if (value != null) {
-                // get the numerical value, if available
-                TypedValue typedValue = ResourceHelper.getValue(value.getValue());
-                if (typedValue != null) {
-                    // compute the pixel value based on the display metrics
-                    defaultOffset = (int)typedValue.getDimension(context.getResources().mMetrics);
-                }
-            }
-
-            offset += defaultOffset;
-        }
-
-        // get the fullscreen flag from the current theme.
-        value = context.findItemInStyle(currentTheme, "windowFullscreen");
-
-        // because it may reference something else, we resolve it.
-        value = context.resolveResValue(value);
-
-        if (value == null || value.getValue() == null ||
-                XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) {
-
-            // default value
-            int defaultOffset = DEFAULT_STATUS_BAR_HEIGHT;
-
-            // get the real value, first the list of Dimensions from the framework map
-            Map<String, IResourceValue> dimens = frameworkResources.get(BridgeConstants.RES_DIMEN);
-
-            // now get the value
-            value = dimens.get("status_bar_height");
-            if (value != null) {
-                TypedValue typedValue = ResourceHelper.getValue(value.getValue());
-                if (typedValue != null) {
-                    // compute the pixel value based on the display metrics
-                    defaultOffset = (int)typedValue.getDimension(context.getResources().mMetrics);
-                }
-            }
-
-            // add the computed offset.
-            offset += defaultOffset;
-        }
-
-        return offset;
-    }
-
-    /**
-     * Post process on a view hierachy that was just inflated.
-     * <p/>At the moment this only support TabHost: If {@link TabHost} is detected, look for the
-     * {@link TabWidget}, and the corresponding {@link FrameLayout} and make new tabs automatically
-     * based on the content of the {@link FrameLayout}.
-     * @param view the root view to process.
-     * @param projectCallback callback to the project.
-     */
-    private void postInflateProcess(View view, IProjectCallback projectCallback)
-            throws PostInflateException {
-        if (view instanceof TabHost) {
-            setupTabHost((TabHost)view, projectCallback);
-        } else if (view instanceof ViewGroup) {
-            ViewGroup group = (ViewGroup)view;
-            final int count = group.getChildCount();
-            for (int c = 0 ; c < count ; c++) {
-                View child = group.getChildAt(c);
-                postInflateProcess(child, projectCallback);
-            }
-        }
-    }
-
-    /**
-     * Sets up a {@link TabHost} object.
-     * @param tabHost the TabHost to setup.
-     * @param projectCallback The project callback object to access the project R class.
-     * @throws PostInflateException
-     */
-    private void setupTabHost(TabHost tabHost, IProjectCallback projectCallback)
-            throws PostInflateException {
-        // look for the TabWidget, and the FrameLayout. They have their own specific names
-        View v = tabHost.findViewById(android.R.id.tabs);
-
-        if (v == null) {
-            throw new PostInflateException(
-                    "TabHost requires a TabWidget with id \"android:id/tabs\".\n");
-        }
-
-        if ((v instanceof TabWidget) == false) {
-            throw new PostInflateException(String.format(
-                    "TabHost requires a TabWidget with id \"android:id/tabs\".\n" +
-                    "View found with id 'tabs' is '%s'", v.getClass().getCanonicalName()));
-        }
-
-        v = tabHost.findViewById(android.R.id.tabcontent);
-
-        if (v == null) {
-            // TODO: see if we can fake tabs even without the FrameLayout (same below when the framelayout is empty)
-            throw new PostInflateException(
-                    "TabHost requires a FrameLayout with id \"android:id/tabcontent\".");
-        }
-
-        if ((v instanceof FrameLayout) == false) {
-            throw new PostInflateException(String.format(
-                    "TabHost requires a FrameLayout with id \"android:id/tabcontent\".\n" +
-                    "View found with id 'tabcontent' is '%s'", v.getClass().getCanonicalName()));
-        }
-
-        FrameLayout content = (FrameLayout)v;
-
-        // now process the content of the framelayout and dynamically create tabs for it.
-        final int count = content.getChildCount();
-
-        if (count == 0) {
-            throw new PostInflateException(
-                    "The FrameLayout for the TabHost has no content. Rendering failed.\n");
-        }
-
-        // this must be called before addTab() so that the TabHost searches its TabWidget
-        // and FrameLayout.
-        tabHost.setup();
-
-        // for each child of the framelayout, add a new TabSpec
-        for (int i = 0 ; i < count ; i++) {
-            View child = content.getChildAt(i);
-            String tabSpec = String.format("tab_spec%d", i+1);
-            int id = child.getId();
-            String[] resource = projectCallback.resolveResourceValue(id);
-            String name;
-            if (resource != null) {
-                name = resource[0]; // 0 is resource name, 1 is resource type.
-            } else {
-                name = String.format("Tab %d", i+1); // default name if id is unresolved.
-            }
-            tabHost.addTab(tabHost.newTabSpec(tabSpec).setIndicator(name).setContent(id));
-        }
+    public static Map<String, String> getPlatformProperties() {
+        return sPlatformProperties;
     }
 
     /**
@@ -888,7 +477,7 @@
      * @param projectKey the key of the project, or null to query the framework cache.
      * @return the cached Bitmap or null if not found.
      */
-    static Bitmap getCachedBitmap(String value, Object projectKey) {
+    public static Bitmap getCachedBitmap(String value, Object projectKey) {
         if (projectKey != null) {
             Map<String, SoftReference<Bitmap>> map = sProjectBitmapCache.get(projectKey);
             if (map != null) {
@@ -913,7 +502,7 @@
      * @param bmp the Bitmap object
      * @param projectKey the key of the project, or null to put the bitmap in the framework cache.
      */
-    static void setCachedBitmap(String value, Bitmap bmp, Object projectKey) {
+    public static void setCachedBitmap(String value, Bitmap bmp, Object projectKey) {
         if (projectKey != null) {
             Map<String, SoftReference<Bitmap>> map = sProjectBitmapCache.get(projectKey);
 
@@ -929,24 +518,24 @@
     }
 
     /**
-     * Returns the 9 patch for a specific path, from a specific project cache, or from the
+     * Returns the 9 patch chunk for a specific path, from a specific project cache, or from the
      * framework cache.
      * @param value the path of the 9 patch
      * @param projectKey the key of the project, or null to query the framework cache.
      * @return the cached 9 patch or null if not found.
      */
-    static NinePatch getCached9Patch(String value, Object projectKey) {
+    public static NinePatchChunk getCached9Patch(String value, Object projectKey) {
         if (projectKey != null) {
-            Map<String, SoftReference<NinePatch>> map = sProject9PatchCache.get(projectKey);
+            Map<String, SoftReference<NinePatchChunk>> map = sProject9PatchCache.get(projectKey);
 
             if (map != null) {
-                SoftReference<NinePatch> ref = map.get(value);
+                SoftReference<NinePatchChunk> ref = map.get(value);
                 if (ref != null) {
                     return ref.get();
                 }
             }
         } else {
-            SoftReference<NinePatch> ref = sFramework9PatchCache.get(value);
+            SoftReference<NinePatchChunk> ref = sFramework9PatchCache.get(value);
             if (ref != null) {
                 return ref.get();
             }
@@ -956,224 +545,25 @@
     }
 
     /**
-     * Sets a 9 patch in a project cache or in the framework cache.
+     * Sets a 9 patch chunk in a project cache or in the framework cache.
      * @param value the path of the 9 patch
      * @param ninePatch the 9 patch object
      * @param projectKey the key of the project, or null to put the bitmap in the framework cache.
      */
-    static void setCached9Patch(String value, NinePatch ninePatch, Object projectKey) {
+    public static void setCached9Patch(String value, NinePatchChunk ninePatch, Object projectKey) {
         if (projectKey != null) {
-            Map<String, SoftReference<NinePatch>> map = sProject9PatchCache.get(projectKey);
+            Map<String, SoftReference<NinePatchChunk>> map = sProject9PatchCache.get(projectKey);
 
             if (map == null) {
-                map = new HashMap<String, SoftReference<NinePatch>>();
+                map = new HashMap<String, SoftReference<NinePatchChunk>>();
                 sProject9PatchCache.put(projectKey, map);
             }
 
-            map.put(value, new SoftReference<NinePatch>(ninePatch));
+            map.put(value, new SoftReference<NinePatchChunk>(ninePatch));
         } else {
-            sFramework9PatchCache.put(value, new SoftReference<NinePatch>(ninePatch));
+            sFramework9PatchCache.put(value, new SoftReference<NinePatchChunk>(ninePatch));
         }
     }
 
-    private static final class PostInflateException extends Exception {
-        private static final long serialVersionUID = 1L;
-
-        public PostInflateException(String message) {
-            super(message);
-        }
-    }
-
-    /**
-     * Implementation of {@link IWindowSession} so that mSession is not null in
-     * the {@link SurfaceView}.
-     */
-    private static final class WindowSession implements IWindowSession {
-
-        @SuppressWarnings("unused")
-        public int add(IWindow arg0, LayoutParams arg1, int arg2, Rect arg3,
-                InputChannel outInputchannel)
-                throws RemoteException {
-            // pass for now.
-            return 0;
-        }
-
-        @SuppressWarnings("unused")
-        public int addWithoutInputChannel(IWindow arg0, LayoutParams arg1, int arg2, Rect arg3)
-                throws RemoteException {
-            // pass for now.
-            return 0;
-        }
-        
-        @SuppressWarnings("unused")
-        public void finishDrawing(IWindow arg0) throws RemoteException {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void finishKey(IWindow arg0) throws RemoteException {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public boolean getInTouchMode() throws RemoteException {
-            // pass for now.
-            return false;
-        }
-
-        @SuppressWarnings("unused")
-        public boolean performHapticFeedback(IWindow window, int effectId, boolean always) {
-            // pass for now.
-            return false;
-        }
-
-        @SuppressWarnings("unused")
-        public MotionEvent getPendingPointerMove(IWindow arg0) throws RemoteException {
-            // pass for now.
-            return null;
-        }
-
-        @SuppressWarnings("unused")
-        public MotionEvent getPendingTrackballMove(IWindow arg0) throws RemoteException {
-            // pass for now.
-            return null;
-        }
-
-        @SuppressWarnings("unused")
-        public int relayout(IWindow arg0, LayoutParams arg1, int arg2, int arg3, int arg4,
-                boolean arg4_5, Rect arg5, Rect arg6, Rect arg7, Configuration arg7b, Surface arg8)
-                throws RemoteException {
-            // pass for now.
-            return 0;
-        }
-
-        public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void remove(IWindow arg0) throws RemoteException {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void setInTouchMode(boolean arg0) throws RemoteException {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void setTransparentRegion(IWindow arg0, Region arg1) throws RemoteException {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void setInsets(IWindow window, int touchable, Rect contentInsets,
-                Rect visibleInsets) {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void setWallpaperPosition(IBinder window, float x, float y,
-            float xStep, float yStep) {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void wallpaperOffsetsComplete(IBinder window) {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
-                int z, Bundle extras, boolean sync) {
-            // pass for now.
-            return null;
-        }
-
-        @SuppressWarnings("unused")
-        public void wallpaperCommandComplete(IBinder window, Bundle result) {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void closeSystemDialogs(String reason) {
-            // pass for now.
-        }
-
-        public IBinder asBinder() {
-            // pass for now.
-            return null;
-        }
-    }
-
-    /**
-     * Implementation of {@link IWindow} to pass to the {@link AttachInfo}.
-     */
-    private static final class Window implements IWindow {
-
-        @SuppressWarnings("unused")
-        public void dispatchAppVisibility(boolean arg0) throws RemoteException {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void dispatchGetNewSurface() throws RemoteException {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void dispatchKey(KeyEvent arg0) throws RemoteException {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void dispatchPointer(MotionEvent arg0, long arg1, boolean arg2) throws RemoteException {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void dispatchTrackball(MotionEvent arg0, long arg1, boolean arg2) throws RemoteException {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void executeCommand(String arg0, String arg1, ParcelFileDescriptor arg2)
-                throws RemoteException {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void resized(int arg0, int arg1, Rect arg2, Rect arg3, boolean arg4, Configuration arg5)
-                throws RemoteException {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void windowFocusChanged(boolean arg0, boolean arg1) throws RemoteException {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
-                boolean sync) {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void dispatchWallpaperCommand(String action, int x, int y,
-                int z, Bundle extras, boolean sync) {
-            // pass for now.
-        }
-
-        @SuppressWarnings("unused")
-        public void closeSystemDialogs(String reason) {
-            // pass for now.
-        }
-
-        public IBinder asBinder() {
-            // pass for now.
-            return null;
-        }
-    }
 
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java
index 791e53b..112af1e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java
@@ -41,24 +41,6 @@
 
     public final static String R = "com.android.internal.R";
 
-    public final static String PREFIX_ANDROID_RESOURCE_REF = "@android:";
-    public final static String PREFIX_RESOURCE_REF = "@";
-    public final static String PREFIX_ANDROID_THEME_REF = "?android:";
-    public final static String PREFIX_THEME_REF = "?";
-
-    public final static String PREFIX_ANDROID = "android:";
-
-    public final static String RES_STYLE = "style";
-    public final static String RES_ATTR = "attr";
-    public final static String RES_DIMEN = "dimen";
-    public final static String RES_DRAWABLE = "drawable";
-    public final static String RES_COLOR = "color";
-    public final static String RES_LAYOUT = "layout";
-    public final static String RES_STRING = "string";
-    public final static String RES_ID = "id";
-
-    public final static String REFERENCE_STYLE = RES_STYLE + "/";
-    public final static String REFERENCE_NULL = "@null";
 
     public final static String MATCH_PARENT = "match_parent";
     public final static String FILL_PARENT = "fill_parent";
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
deleted file mode 100644
index 20ccc0b..0000000
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2009 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;
-
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.IContentProvider;
-import android.content.OperationApplicationException;
-import android.content.res.AssetFileDescriptor;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.CursorWindow;
-import android.database.IBulkCursor;
-import android.database.IContentObserver;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-
-/**
- * A mock content resolver for the LayoutLib Bridge.
- * <p/>
- * It won't serve any actual data but it's good enough for all
- * the widgets which expect to have a content resolver available via
- * {@link BridgeContext#getContentResolver()}.
- */
-public class BridgeContentResolver extends ContentResolver {
-
-    private BridgeContentProvider mProvider = null;
-
-    public static final class BridgeContentProvider implements IContentProvider {
-
-        public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> arg0)
-                throws RemoteException, OperationApplicationException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        public int bulkInsert(Uri arg0, ContentValues[] arg1) throws RemoteException {
-            // TODO Auto-generated method stub
-            return 0;
-        }
-
-        public IBulkCursor bulkQuery(Uri arg0, String[] arg1, String arg2, String[] arg3,
-                String arg4, IContentObserver arg5, CursorWindow arg6) throws RemoteException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        public Bundle call(String arg0, String arg1, Bundle arg2) throws RemoteException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        public int delete(Uri arg0, String arg1, String[] arg2) throws RemoteException {
-            // TODO Auto-generated method stub
-            return 0;
-        }
-
-        public String getType(Uri arg0) throws RemoteException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        public Uri insert(Uri arg0, ContentValues arg1) throws RemoteException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        public AssetFileDescriptor openAssetFile(Uri arg0, String arg1) throws RemoteException,
-                FileNotFoundException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        public ParcelFileDescriptor openFile(Uri arg0, String arg1) throws RemoteException,
-                FileNotFoundException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4)
-                throws RemoteException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3)
-                throws RemoteException {
-            // TODO Auto-generated method stub
-            return 0;
-        }
-
-        public IBinder asBinder() {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-    }
-
-    public BridgeContentResolver(Context context) {
-        super(context);
-    }
-
-    @Override
-    public IContentProvider acquireProvider(Context c, String name) {
-        if (mProvider == null) {
-            mProvider = new BridgeContentProvider();
-        }
-
-        return mProvider;
-    }
-
-    @Override
-    public IContentProvider acquireExistingProvider(Context c, String name) {
-        if (mProvider == null) {
-            mProvider = new BridgeContentProvider();
-        }
-
-        return mProvider;
-    }
-
-    @Override
-    public boolean releaseProvider(IContentProvider icp) {
-        // ignore
-        return false;
-    }
-
-    /**
-     * Stub for the layoutlib bridge content resolver.
-     */
-    @Override
-    public void registerContentObserver(Uri uri, boolean notifyForDescendents,
-            ContentObserver observer) {
-        // pass
-    }
-
-    /**
-     * Stub for the layoutlib bridge content resolver.
-     */
-    @Override
-    public void unregisterContentObserver(ContentObserver observer) {
-        // pass
-    }
-
-    /**
-     * Stub for the layoutlib bridge content resolver.
-     */
-    @Override
-    public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
-        // pass
-    }
-
-    /**
-     * Stub for the layoutlib bridge content resolver.
-     */
-    @Override
-    public void startSync(Uri uri, Bundle extras) {
-        // pass
-    }
-
-    /**
-     * Stub for the layoutlib bridge content resolver.
-     */
-    @Override
-    public void cancelSync(Uri uri) {
-        // pass
-    }
-}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
deleted file mode 100644
index ecd22e2..0000000
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
+++ /dev/null
@@ -1,1207 +0,0 @@
-/*
- * Copyright (C) 2008 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;
-
-import com.android.layoutlib.api.ILayoutLog;
-import com.android.layoutlib.api.IProjectCallback;
-import com.android.layoutlib.api.IResourceValue;
-import com.android.layoutlib.api.IStyleResourceValue;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.content.ServiceConnection;
-import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.res.AssetManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.Resources.Theme;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteDatabase.CursorFactory;
-import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.view.BridgeInflater;
-import android.view.View;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.Map.Entry;
-
-/**
- * Custom implementation of Context to handle non compiled resources.
- */
-public final class BridgeContext extends Context {
-
-    private final Resources mResources;
-    private final Theme mTheme;
-    private final HashMap<View, Object> mViewKeyMap = new HashMap<View, Object>();
-    private final IStyleResourceValue mThemeValues;
-    private final Object mProjectKey;
-    private final Map<String, Map<String, IResourceValue>> mProjectResources;
-    private final Map<String, Map<String, IResourceValue>> mFrameworkResources;
-    private final Map<IStyleResourceValue, IStyleResourceValue> mStyleInheritanceMap;
-
-    // maps for dynamically generated id representing style objects (IStyleResourceValue)
-    private Map<Integer, IStyleResourceValue> mDynamicIdToStyleMap;
-    private Map<IStyleResourceValue, Integer> mStyleToDynamicIdMap;
-    private int mDynamicIdGenerator = 0x01030000; // Base id for framework R.style
-
-    // cache for TypedArray generated from IStyleResourceValue object
-    private Map<int[], Map<Integer, TypedArray>> mTypedArrayCache;
-    private BridgeInflater mInflater;
-
-    private final IProjectCallback mProjectCallback;
-    private final ILayoutLog mLogger;
-    private BridgeContentResolver mContentResolver;
-
-    /**
-     * @param projectKey An Object identifying the project. This is used for the cache mechanism.
-     * @param metrics the {@link DisplayMetrics}.
-     * @param themeName The name of the theme to use.
-     * @param projectResources the resources of the project. The map contains (String, map) pairs
-     * where the string is the type of the resource reference used in the layout file, and the
-     * map contains (String, {@link IResourceValue}) pairs where the key is the resource name,
-     * and the value is the resource value.
-     * @param frameworkResources the framework resources. The map contains (String, map) pairs
-     * where the string is the type of the resource reference used in the layout file, and the map
-     * contains (String, {@link IResourceValue}) pairs where the key is the resource name, and the
-     * value is the resource value.
-     * @param styleInheritanceMap
-     * @param customViewLoader
-     */
-    public BridgeContext(Object projectKey, DisplayMetrics metrics,
-            IStyleResourceValue currentTheme,
-            Map<String, Map<String, IResourceValue>> projectResources,
-            Map<String, Map<String, IResourceValue>> frameworkResources,
-            Map<IStyleResourceValue, IStyleResourceValue> styleInheritanceMap,
-            IProjectCallback customViewLoader, ILayoutLog logger) {
-        mProjectKey = projectKey;
-        mProjectCallback = customViewLoader;
-        mLogger = logger;
-        Configuration config = new Configuration();
-
-        AssetManager assetManager = BridgeAssetManager.initSystem();
-        mResources = BridgeResources.initSystem(
-                this,
-                assetManager,
-                metrics,
-                config,
-                customViewLoader);
-
-        mTheme = mResources.newTheme();
-
-        mThemeValues = currentTheme;
-        mProjectResources = projectResources;
-        mFrameworkResources = frameworkResources;
-        mStyleInheritanceMap = styleInheritanceMap;
-    }
-
-    public void setBridgeInflater(BridgeInflater inflater) {
-        mInflater = inflater;
-    }
-
-    public void addViewKey(View view, Object viewKey) {
-        mViewKeyMap.put(view, viewKey);
-    }
-
-    public Object getViewKey(View view) {
-        return mViewKeyMap.get(view);
-    }
-
-    public Object getProjectKey() {
-        return mProjectKey;
-    }
-
-    public IProjectCallback getProjectCallback() {
-        return mProjectCallback;
-    }
-
-    public ILayoutLog getLogger() {
-        return mLogger;
-    }
-
-    // ------------ Context methods
-
-    @Override
-    public Resources getResources() {
-        return mResources;
-    }
-
-    @Override
-    public Theme getTheme() {
-        return mTheme;
-    }
-
-    @Override
-    public ClassLoader getClassLoader() {
-        return this.getClass().getClassLoader();
-    }
-
-    @Override
-    public Object getSystemService(String service) {
-        if (LAYOUT_INFLATER_SERVICE.equals(service)) {
-            return mInflater;
-        }
-
-        // AutoCompleteTextView and MultiAutoCompleteTextView want a window
-        // service. We don't have any but it's not worth an exception.
-        if (WINDOW_SERVICE.equals(service)) {
-            return null;
-        }
-
-        throw new UnsupportedOperationException("Unsupported Service: " + service);
-    }
-
-
-    @Override
-    public final TypedArray obtainStyledAttributes(int[] attrs) {
-        return createStyleBasedTypedArray(mThemeValues, attrs);
-    }
-
-    @Override
-    public final TypedArray obtainStyledAttributes(int resid, int[] attrs)
-            throws Resources.NotFoundException {
-        // get the IStyleResourceValue based on the resId;
-        IStyleResourceValue style = getStyleByDynamicId(resid);
-
-        if (style == null) {
-            throw new Resources.NotFoundException();
-        }
-
-        if (mTypedArrayCache == null) {
-            mTypedArrayCache = new HashMap<int[], Map<Integer,TypedArray>>();
-
-            Map<Integer, TypedArray> map = new HashMap<Integer, TypedArray>();
-            mTypedArrayCache.put(attrs, map);
-
-            BridgeTypedArray ta = createStyleBasedTypedArray(style, attrs);
-            map.put(resid, ta);
-
-            return ta;
-        }
-
-        // get the 2nd map
-        Map<Integer, TypedArray> map = mTypedArrayCache.get(attrs);
-        if (map == null) {
-            map = new HashMap<Integer, TypedArray>();
-            mTypedArrayCache.put(attrs, map);
-        }
-
-        // get the array from the 2nd map
-        TypedArray ta = map.get(resid);
-
-        if (ta == null) {
-            ta = createStyleBasedTypedArray(style, attrs);
-            map.put(resid, ta);
-        }
-
-        return ta;
-    }
-
-    @Override
-    public final TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs) {
-        return obtainStyledAttributes(set, attrs, 0, 0);
-    }
-
-    @Override
-    public TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs,
-            int defStyleAttr, int defStyleRes) {
-
-        // Hint: for XmlPullParser, attach source //DEVICE_SRC/dalvik/libcore/xml/src/java
-        BridgeXmlBlockParser parser = null;
-        if (set instanceof BridgeXmlBlockParser) {
-            parser = (BridgeXmlBlockParser)set;
-        } else if (set != null) { // null parser is ok
-            // really this should not be happening since its instantiated in Bridge
-            mLogger.error("Parser is not a BridgeXmlBlockParser!");
-            return null;
-        }
-
-        boolean[] frameworkAttributes = new boolean[1];
-        TreeMap<Integer, String> styleNameMap = searchAttrs(attrs, frameworkAttributes);
-
-        BridgeTypedArray ta = ((BridgeResources) mResources).newTypeArray(attrs.length,
-                parser != null ? parser.isPlatformFile() : true);
-
-        // resolve the defStyleAttr value into a IStyleResourceValue
-        IStyleResourceValue defStyleValues = null;
-
-        // look for a custom style.
-        String customStyle = null;
-        if (parser != null) {
-            customStyle = parser.getAttributeValue(null /* namespace*/, "style");
-        }
-        if (customStyle != null) {
-            IResourceValue item = findResValue(customStyle, false /*forceFrameworkOnly*/);
-
-            if (item instanceof IStyleResourceValue) {
-                defStyleValues = (IStyleResourceValue)item;
-            }
-        }
-
-        if (defStyleValues == null && defStyleAttr != 0) {
-            // get the name from the int.
-            String defStyleName = searchAttr(defStyleAttr);
-
-            // look for the style in the current theme, and its parent:
-            if (mThemeValues != null) {
-                IResourceValue item = findItemInStyle(mThemeValues, defStyleName);
-
-                if (item != null) {
-                    // item is a reference to a style entry. Search for it.
-                    item = findResValue(item.getValue(), false /*forceFrameworkOnly*/);
-
-                    if (item instanceof IStyleResourceValue) {
-                        defStyleValues = (IStyleResourceValue)item;
-                    }
-                } else {
-                    // TODO: log the error properly
-                    System.out.println("Failed to find defStyle: " + defStyleName);
-                }
-            }
-        }
-
-        if (defStyleRes != 0) {
-            // FIXME: See what we need to do with this.
-            throw new UnsupportedOperationException();
-        }
-
-        String namespace = BridgeConstants.NS_RESOURCES;
-        if (frameworkAttributes[0] == false) {
-            // need to use the application namespace
-            namespace = mProjectCallback.getNamespace();
-        }
-
-        if (styleNameMap != null) {
-            for (Entry<Integer, String> styleAttribute : styleNameMap.entrySet()) {
-                int index = styleAttribute.getKey().intValue();
-
-                String name = styleAttribute.getValue();
-                String value = null;
-                if (parser != null) {
-                    value = parser.getAttributeValue(namespace, name);
-                }
-
-                // if there's no direct value for this attribute in the XML, we look for default
-                // values in the widget defStyle, and then in the theme.
-                if (value == null) {
-                    IResourceValue resValue = null;
-
-                    // look for the value in the defStyle first (and its parent if needed)
-                    if (defStyleValues != null) {
-                        resValue = findItemInStyle(defStyleValues, name);
-                    }
-
-                    // if the item is not present in the defStyle, we look in the main theme (and
-                    // its parent themes)
-                    if (resValue == null && mThemeValues != null) {
-                        resValue = findItemInStyle(mThemeValues, name);
-                    }
-
-                    // if we found a value, we make sure this doesn't reference another value.
-                    // So we resolve it.
-                    if (resValue != null) {
-                        resValue = resolveResValue(resValue);
-                    }
-
-                    ta.bridgeSetValue(index, name, resValue);
-                } else {
-                    // there is a value in the XML, but we need to resolve it in case it's
-                    // referencing another resource or a theme value.
-                    ta.bridgeSetValue(index, name, resolveValue(null, name, value));
-                }
-            }
-        }
-
-        ta.sealArray();
-
-        return ta;
-    }
-
-    @Override
-    public Looper getMainLooper() {
-        return Looper.myLooper();
-    }
-
-
-    // ------------- private new methods
-
-    /**
-     * Creates a {@link BridgeTypedArray} by filling the values defined by the int[] with the
-     * values found in the given style.
-     * @see #obtainStyledAttributes(int, int[])
-     */
-    private BridgeTypedArray createStyleBasedTypedArray(IStyleResourceValue style, int[] attrs)
-            throws Resources.NotFoundException {
-        TreeMap<Integer, String> styleNameMap = searchAttrs(attrs, null);
-
-        BridgeTypedArray ta = ((BridgeResources) mResources).newTypeArray(attrs.length,
-                false /* platformResourceFlag */);
-
-        // loop through all the values in the style map, and init the TypedArray with
-        // the style we got from the dynamic id
-        for (Entry<Integer, String> styleAttribute : styleNameMap.entrySet()) {
-            int index = styleAttribute.getKey().intValue();
-
-            String name = styleAttribute.getValue();
-
-            // get the value from the style, or its parent styles.
-            IResourceValue resValue = findItemInStyle(style, name);
-
-            // resolve it to make sure there are no references left.
-            ta.bridgeSetValue(index, name, resolveResValue(resValue));
-        }
-
-        ta.sealArray();
-
-        return ta;
-    }
-
-
-    /**
-     * Resolves the value of a resource, if the value references a theme or resource value.
-     * <p/>
-     * This method ensures that it returns a {@link IResourceValue} object that does not
-     * reference another resource.
-     * If the resource cannot be resolved, it returns <code>null</code>.
-     * <p/>
-     * If a value that does not need to be resolved is given, the method will return a new
-     * instance of IResourceValue that contains the input value.
-     *
-     * @param type the type of the resource
-     * @param name the name of the attribute containing this value.
-     * @param value the resource value, or reference to resolve
-     * @return the resolved resource value or <code>null</code> if it failed to resolve it.
-     */
-    private IResourceValue resolveValue(String type, String name, String value) {
-        if (value == null) {
-            return null;
-        }
-
-        // get the IResourceValue referenced by this value
-        IResourceValue resValue = findResValue(value, false /*forceFrameworkOnly*/);
-
-        // if resValue is null, but value is not null, this means it was not a reference.
-        // we return the name/value wrapper in a IResourceValue
-        if (resValue == null) {
-            return new ResourceValue(type, name, value);
-        }
-
-        // we resolved a first reference, but we need to make sure this isn't a reference also.
-        return resolveResValue(resValue);
-    }
-
-    /**
-     * Returns the {@link IResourceValue} referenced by the value of <var>value</var>.
-     * <p/>
-     * This method ensures that it returns a {@link IResourceValue} object that does not
-     * reference another resource.
-     * If the resource cannot be resolved, it returns <code>null</code>.
-     * <p/>
-     * If a value that does not need to be resolved is given, the method will return the input
-     * value.
-     *
-     * @param value the value containing the reference to resolve.
-     * @return a {@link IResourceValue} object or <code>null</code>
-     */
-    IResourceValue resolveResValue(IResourceValue value) {
-        if (value == null) {
-            return null;
-        }
-
-        // if the resource value is a style, we simply return it.
-        if (value instanceof IStyleResourceValue) {
-            return value;
-        }
-
-        // else attempt to find another IResourceValue referenced by this one.
-        IResourceValue resolvedValue = findResValue(value.getValue(), value.isFramework());
-
-        // if the value did not reference anything, then we simply return the input value
-        if (resolvedValue == null) {
-            return value;
-        }
-
-        // otherwise, we attempt to resolve this new value as well
-        return resolveResValue(resolvedValue);
-    }
-
-    /**
-     * Searches for, and returns a {@link IResourceValue} by its reference.
-     * <p/>
-     * The reference format can be:
-     * <pre>@resType/resName</pre>
-     * <pre>@android:resType/resName</pre>
-     * <pre>@resType/android:resName</pre>
-     * <pre>?resType/resName</pre>
-     * <pre>?android:resType/resName</pre>
-     * <pre>?resType/android:resName</pre>
-     * Any other string format will return <code>null</code>.
-     * <p/>
-     * The actual format of a reference is <pre>@[namespace:]resType/resName</pre> but this method
-     * only support the android namespace.
-     *
-     * @param reference the resource reference to search for.
-     * @param forceFrameworkOnly if true all references are considered to be toward framework
-     *      resource even if the reference does not include the android: prefix.
-     * @return a {@link IResourceValue} or <code>null</code>.
-     */
-    IResourceValue findResValue(String reference, boolean forceFrameworkOnly) {
-        if (reference == null) {
-            return null;
-        }
-        if (reference.startsWith(BridgeConstants.PREFIX_THEME_REF)) {
-            // no theme? no need to go further!
-            if (mThemeValues == null) {
-                return null;
-            }
-
-            boolean frameworkOnly = false;
-
-            // eleminate the prefix from the string
-            if (reference.startsWith(BridgeConstants.PREFIX_ANDROID_THEME_REF)) {
-                frameworkOnly = true;
-                reference = reference.substring(BridgeConstants.PREFIX_ANDROID_THEME_REF.length());
-            } else {
-                reference = reference.substring(BridgeConstants.PREFIX_THEME_REF.length());
-            }
-
-            // at this point, value can contain type/name (drawable/foo for instance).
-            // split it to make sure.
-            String[] segments = reference.split("\\/");
-
-            // we look for the referenced item name.
-            String referenceName = null;
-
-            if (segments.length == 2) {
-                // there was a resType in the reference. If it's attr, we ignore it
-                // else, we assert for now.
-                if (BridgeConstants.RES_ATTR.equals(segments[0])) {
-                    referenceName = segments[1];
-                } else {
-                    // At this time, no support for ?type/name where type is not "attr"
-                    return null;
-                }
-            } else {
-                // it's just an item name.
-                referenceName = segments[0];
-            }
-
-            // now we look for android: in the referenceName in order to support format
-            // such as: ?attr/android:name
-            if (referenceName.startsWith(BridgeConstants.PREFIX_ANDROID)) {
-                frameworkOnly = true;
-                referenceName = referenceName.substring(BridgeConstants.PREFIX_ANDROID.length());
-            }
-
-            // Now look for the item in the theme, starting with the current one.
-            if (frameworkOnly) {
-                // FIXME for now we do the same as if it didn't specify android:
-                return findItemInStyle(mThemeValues, referenceName);
-            }
-
-            return findItemInStyle(mThemeValues, referenceName);
-        } else if (reference.startsWith(BridgeConstants.PREFIX_RESOURCE_REF)) {
-            boolean frameworkOnly = false;
-
-            // check for the specific null reference value.
-            if (BridgeConstants.REFERENCE_NULL.equals(reference)) {
-                return null;
-            }
-
-            // Eliminate the prefix from the string.
-            if (reference.startsWith(BridgeConstants.PREFIX_ANDROID_RESOURCE_REF)) {
-                frameworkOnly = true;
-                reference = reference.substring(
-                        BridgeConstants.PREFIX_ANDROID_RESOURCE_REF.length());
-            } else {
-                reference = reference.substring(BridgeConstants.PREFIX_RESOURCE_REF.length());
-            }
-
-            // at this point, value contains type/[android:]name (drawable/foo for instance)
-            String[] segments = reference.split("\\/");
-
-            // now we look for android: in the resource name in order to support format
-            // such as: @drawable/android:name
-            if (segments[1].startsWith(BridgeConstants.PREFIX_ANDROID)) {
-                frameworkOnly = true;
-                segments[1] = segments[1].substring(BridgeConstants.PREFIX_ANDROID.length());
-            }
-
-            return findResValue(segments[0], segments[1],
-                    forceFrameworkOnly ? true :frameworkOnly);
-        }
-
-        // Looks like the value didn't reference anything. Return null.
-        return null;
-    }
-
-    /**
-     * Searches for, and returns a {@link IResourceValue} by its name, and type.
-     * @param resType the type of the resource
-     * @param resName  the name of the resource
-     * @param frameworkOnly if <code>true</code>, the method does not search in the
-     * project resources
-     */
-    private IResourceValue findResValue(String resType, String resName, boolean frameworkOnly) {
-        // map of IResouceValue for the given type
-        Map<String, IResourceValue> typeMap;
-
-        // if allowed, search in the project resources first.
-        if (frameworkOnly == false) {
-            typeMap = mProjectResources.get(resType);
-            if (typeMap != null) {
-                IResourceValue item = typeMap.get(resName);
-                if (item != null) {
-                    return item;
-                }
-            }
-        }
-
-        // now search in the framework resources.
-        typeMap = mFrameworkResources.get(resType);
-        if (typeMap != null) {
-            IResourceValue item = typeMap.get(resName);
-            if (item != null) {
-                return item;
-            }
-        }
-
-        // didn't find the resource anywhere.
-        return null;
-    }
-
-    /**
-     * Returns a framework resource by type and name. The returned resource is resolved.
-     * @param resourceType the type of the resource
-     * @param resourceName the name of the resource
-     */
-    public IResourceValue getFrameworkResource(String resourceType, String resourceName) {
-        return getResource(resourceType, resourceName, mFrameworkResources);
-    }
-
-    /**
-     * Returns a project resource by type and name. The returned resource is resolved.
-     * @param resourceType the type of the resource
-     * @param resourceName the name of the resource
-     */
-    public IResourceValue getProjectResource(String resourceType, String resourceName) {
-        return getResource(resourceType, resourceName, mProjectResources);
-    }
-
-    IResourceValue getResource(String resourceType, String resourceName,
-            Map<String, Map<String, IResourceValue>> resourceRepository) {
-        Map<String, IResourceValue> typeMap = resourceRepository.get(resourceType);
-        if (typeMap != null) {
-            IResourceValue item = typeMap.get(resourceName);
-            if (item != null) {
-                item = resolveResValue(item);
-                return item;
-            }
-        }
-
-        // didn't find the resource anywhere.
-        return null;
-
-    }
-
-    /**
-     * Returns the {@link IResourceValue} matching a given name in a given style. If the
-     * item is not directly available in the style, the method looks in its parent style.
-     * @param style the style to search in
-     * @param itemName the name of the item to search for.
-     * @return the {@link IResourceValue} object or <code>null</code>
-     */
-    IResourceValue findItemInStyle(IStyleResourceValue style, String itemName) {
-        IResourceValue item = style.findItem(itemName);
-
-        // if we didn't find it, we look in the parent style (if applicable)
-        if (item == null && mStyleInheritanceMap != null) {
-            IStyleResourceValue parentStyle = mStyleInheritanceMap.get(style);
-            if (parentStyle != null) {
-                return findItemInStyle(parentStyle, itemName);
-            }
-        }
-
-        return item;
-    }
-
-    /**
-     * The input int[] attrs is one of com.android.internal.R.styleable fields where the name
-     * of the field is the style being referenced and the array contains one index per attribute.
-     * <p/>
-     * searchAttrs() finds all the names of the attributes referenced so for example if
-     * attrs == com.android.internal.R.styleable.View, this returns the list of the "xyz" where
-     * there's a field com.android.internal.R.styleable.View_xyz and the field value is the index
-     * that is used to reference the attribute later in the TypedArray.
-     *
-     * @param attrs An attribute array reference given to obtainStyledAttributes.
-     * @return A sorted map Attribute-Value to Attribute-Name for all attributes declared by the
-     *         attribute array. Returns null if nothing is found.
-     */
-    private TreeMap<Integer,String> searchAttrs(int[] attrs, boolean[] outFrameworkFlag) {
-        // get the name of the array from the framework resources
-        String arrayName = Bridge.resolveResourceValue(attrs);
-        if (arrayName != null) {
-            // if we found it, get the name of each of the int in the array.
-            TreeMap<Integer,String> attributes = new TreeMap<Integer, String>();
-            for (int i = 0 ; i < attrs.length ; i++) {
-                String[] info = Bridge.resolveResourceValue(attrs[i]);
-                if (info != null) {
-                    attributes.put(i, info[0]);
-                } else {
-                    // FIXME Not sure what we should be doing here...
-                    attributes.put(i, null);
-                }
-            }
-
-            if (outFrameworkFlag != null) {
-                outFrameworkFlag[0] = true;
-            }
-
-            return attributes;
-        }
-
-        // if the name was not found in the framework resources, look in the project
-        // resources
-        arrayName = mProjectCallback.resolveResourceValue(attrs);
-        if (arrayName != null) {
-            TreeMap<Integer,String> attributes = new TreeMap<Integer, String>();
-            for (int i = 0 ; i < attrs.length ; i++) {
-                String[] info = mProjectCallback.resolveResourceValue(attrs[i]);
-                if (info != null) {
-                    attributes.put(i, info[0]);
-                } else {
-                    // FIXME Not sure what we should be doing here...
-                    attributes.put(i, null);
-                }
-            }
-
-            if (outFrameworkFlag != null) {
-                outFrameworkFlag[0] = false;
-            }
-
-            return attributes;
-        }
-
-        return null;
-    }
-
-    /**
-     * Searches for the attribute referenced by its internal id.
-     *
-     * @param attr An attribute reference given to obtainStyledAttributes such as defStyle.
-     * @return The unique name of the attribute, if found, e.g. "buttonStyle". Returns null
-     *         if nothing is found.
-     */
-    public String searchAttr(int attr) {
-        String[] info = Bridge.resolveResourceValue(attr);
-        if (info != null) {
-            return info[0];
-        }
-
-        info = mProjectCallback.resolveResourceValue(attr);
-        if (info != null) {
-            return info[0];
-        }
-
-        return null;
-    }
-
-    int getDynamicIdByStyle(IStyleResourceValue resValue) {
-        if (mDynamicIdToStyleMap == null) {
-            // create the maps.
-            mDynamicIdToStyleMap = new HashMap<Integer, IStyleResourceValue>();
-            mStyleToDynamicIdMap = new HashMap<IStyleResourceValue, Integer>();
-        }
-
-        // look for an existing id
-        Integer id = mStyleToDynamicIdMap.get(resValue);
-
-        if (id == null) {
-            // generate a new id
-            id = Integer.valueOf(++mDynamicIdGenerator);
-
-            // and add it to the maps.
-            mDynamicIdToStyleMap.put(id, resValue);
-            mStyleToDynamicIdMap.put(resValue, id);
-        }
-
-        return id;
-    }
-
-    private IStyleResourceValue getStyleByDynamicId(int i) {
-        if (mDynamicIdToStyleMap != null) {
-            return mDynamicIdToStyleMap.get(i);
-        }
-
-        return null;
-    }
-
-    int getFrameworkIdValue(String idName, int defValue) {
-        Integer value = Bridge.getResourceValue(BridgeConstants.RES_ID, idName);
-        if (value != null) {
-            return value.intValue();
-        }
-
-        return defValue;
-    }
-
-    int getProjectIdValue(String idName, int defValue) {
-        if (mProjectCallback != null) {
-            Integer value = mProjectCallback.getResourceValue(BridgeConstants.RES_ID, idName);
-            if (value != null) {
-                return value.intValue();
-            }
-        }
-
-        return defValue;
-    }
-
-    //------------ NOT OVERRIDEN --------------------
-
-    @Override
-    public boolean bindService(Intent arg0, ServiceConnection arg1, int arg2) {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public int checkCallingOrSelfPermission(String arg0) {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    @Override
-    public int checkCallingOrSelfUriPermission(Uri arg0, int arg1) {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    @Override
-    public int checkCallingPermission(String arg0) {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    @Override
-    public int checkCallingUriPermission(Uri arg0, int arg1) {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    @Override
-    public int checkPermission(String arg0, int arg1, int arg2) {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    @Override
-    public int checkUriPermission(Uri arg0, int arg1, int arg2, int arg3) {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    @Override
-    public int checkUriPermission(Uri arg0, String arg1, String arg2, int arg3,
-            int arg4, int arg5) {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    @Override
-    public void clearWallpaper() {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public Context createPackageContext(String arg0, int arg1) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public String[] databaseList() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public boolean deleteDatabase(String arg0) {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public boolean deleteFile(String arg0) {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public void enforceCallingOrSelfPermission(String arg0, String arg1) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void enforceCallingOrSelfUriPermission(Uri arg0, int arg1,
-            String arg2) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void enforceCallingPermission(String arg0, String arg1) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void enforceCallingUriPermission(Uri arg0, int arg1, String arg2) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void enforcePermission(String arg0, int arg1, int arg2, String arg3) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void enforceUriPermission(Uri arg0, int arg1, int arg2, int arg3,
-            String arg4) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void enforceUriPermission(Uri arg0, String arg1, String arg2,
-            int arg3, int arg4, int arg5, String arg6) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public String[] fileList() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public AssetManager getAssets() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public File getCacheDir() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public File getExternalCacheDir() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public ContentResolver getContentResolver() {
-        if (mContentResolver == null) {
-            mContentResolver = new BridgeContentResolver(this);
-        }
-        return mContentResolver;
-    }
-
-    @Override
-    public File getDatabasePath(String arg0) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public File getDir(String arg0, int arg1) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public File getFileStreamPath(String arg0) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public File getFilesDir() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public File getExternalFilesDir(String type) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public String getPackageCodePath() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public PackageManager getPackageManager() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public String getPackageName() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public ApplicationInfo getApplicationInfo() {
-        return new ApplicationInfo();
-    }
-
-    @Override
-    public String getPackageResourcePath() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public File getSharedPrefsFile(String name) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public SharedPreferences getSharedPreferences(String arg0, int arg1) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public Drawable getWallpaper() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public int getWallpaperDesiredMinimumWidth() {
-        return -1;
-    }
-
-    @Override
-    public int getWallpaperDesiredMinimumHeight() {
-        return -1;
-    }
-
-    @Override
-    public void grantUriPermission(String arg0, Uri arg1, int arg2) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @SuppressWarnings("unused")
-    @Override
-    public FileInputStream openFileInput(String arg0)
-            throws FileNotFoundException {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @SuppressWarnings("unused")
-    @Override
-    public FileOutputStream openFileOutput(String arg0, int arg1)
-            throws FileNotFoundException {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public SQLiteDatabase openOrCreateDatabase(String arg0, int arg1,
-            CursorFactory arg2) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public Drawable peekWallpaper() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1,
-            String arg2, Handler arg3) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public void removeStickyBroadcast(Intent arg0) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void revokeUriPermission(Uri arg0, int arg1) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void sendBroadcast(Intent arg0) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void sendBroadcast(Intent arg0, String arg1) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void sendOrderedBroadcast(Intent arg0, String arg1) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void sendOrderedBroadcast(Intent arg0, String arg1,
-            BroadcastReceiver arg2, Handler arg3, int arg4, String arg5,
-            Bundle arg6) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void sendStickyBroadcast(Intent arg0) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void sendStickyOrderedBroadcast(Intent intent,
-            BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData,
-           Bundle initialExtras) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void setTheme(int arg0) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @SuppressWarnings("unused")
-    @Override
-    public void setWallpaper(Bitmap arg0) throws IOException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @SuppressWarnings("unused")
-    @Override
-    public void setWallpaper(InputStream arg0) throws IOException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void startActivity(Intent arg0) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void startIntentSender(IntentSender intent,
-            Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
-            throws IntentSender.SendIntentException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public boolean startInstrumentation(ComponentName arg0, String arg1,
-            Bundle arg2) {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public ComponentName startService(Intent arg0) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public boolean stopService(Intent arg0) {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public void unbindService(ServiceConnection arg0) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void unregisterReceiver(BroadcastReceiver arg0) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public Context getApplicationContext() {
-        throw new UnsupportedOperationException();
-    }
-}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
new file mode 100644
index 0000000..765fd99
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2010 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;
+
+import com.android.ide.common.rendering.api.IAnimationListener;
+import com.android.ide.common.rendering.api.ILayoutPullParser;
+import com.android.ide.common.rendering.api.RenderParams;
+import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.Result;
+import com.android.ide.common.rendering.api.ViewInfo;
+import com.android.ide.common.rendering.api.Result.Status;
+import com.android.layoutlib.bridge.impl.RenderSessionImpl;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+
+import java.awt.image.BufferedImage;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An implementation of {@link RenderSession}.
+ *
+ * This is a pretty basic class that does almost nothing. All of the work is done in
+ * {@link RenderSessionImpl}.
+ *
+ */
+public class BridgeRenderSession extends RenderSession {
+
+    private final RenderSessionImpl mSession;
+    private Result mLastResult;
+
+    @Override
+    public Result getResult() {
+        return mLastResult;
+    }
+
+    @Override
+    public BufferedImage getImage() {
+        return mSession.getImage();
+    }
+
+    @Override
+    public boolean isAlphaChannelImage() {
+        return mSession.isAlphaChannelImage();
+    }
+
+    @Override
+    public List<ViewInfo> getRootViews() {
+        return mSession.getViewInfos();
+    }
+
+    @Override
+    public Map<String, String> getDefaultProperties(Object viewObject) {
+        return mSession.getDefaultProperties(viewObject);
+    }
+
+    @Override
+    public Result getProperty(Object objectView, String propertyName) {
+        // TODO Auto-generated method stub
+        return super.getProperty(objectView, propertyName);
+    }
+
+    @Override
+    public Result setProperty(Object objectView, String propertyName, String propertyValue) {
+        // TODO Auto-generated method stub
+        return super.setProperty(objectView, propertyName, propertyValue);
+    }
+
+    @Override
+    public Result getViewParent(Object viewObject) {
+        if (viewObject instanceof View) {
+            return Status.SUCCESS.createResult(((View)viewObject).getParent());
+        }
+
+        throw new IllegalArgumentException("viewObject is not a View");
+    }
+
+    @Override
+    public Result getViewIndex(Object viewObject) {
+        if (viewObject instanceof View) {
+            View view = (View) viewObject;
+            ViewParent parentView = view.getParent();
+
+            if (parentView instanceof ViewGroup) {
+                Status.SUCCESS.createResult(((ViewGroup) parentView).indexOfChild(view));
+            }
+
+            return Status.SUCCESS.createResult();
+        }
+
+        throw new IllegalArgumentException("viewObject is not a View");
+    }
+
+    @Override
+    public Result render(long timeout) {
+        try {
+            Bridge.prepareThread();
+            mLastResult = mSession.acquire(timeout);
+            if (mLastResult.isSuccess()) {
+                mLastResult = mSession.render(false /*freshRender*/);
+            }
+        } finally {
+            mSession.release();
+            Bridge.cleanupThread();
+        }
+
+        return mLastResult;
+    }
+
+    @Override
+    public Result animate(Object targetObject, String animationName,
+            boolean isFrameworkAnimation, IAnimationListener listener) {
+        try {
+            Bridge.prepareThread();
+            mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
+            if (mLastResult.isSuccess()) {
+                mLastResult = mSession.animate(targetObject, animationName, isFrameworkAnimation,
+                        listener);
+            }
+        } finally {
+            mSession.release();
+            Bridge.cleanupThread();
+        }
+
+        return mLastResult;
+    }
+
+    @Override
+    public Result insertChild(Object parentView, ILayoutPullParser childXml, int index,
+            IAnimationListener listener) {
+        if (parentView instanceof ViewGroup == false) {
+            throw new IllegalArgumentException("parentView is not a ViewGroup");
+        }
+
+        try {
+            Bridge.prepareThread();
+            mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
+            if (mLastResult.isSuccess()) {
+                mLastResult = mSession.insertChild((ViewGroup) parentView, childXml, index,
+                        listener);
+            }
+        } finally {
+            mSession.release();
+            Bridge.cleanupThread();
+        }
+
+        return mLastResult;
+    }
+
+
+    @Override
+    public Result moveChild(Object parentView, Object childView, int index,
+            Map<String, String> layoutParams, IAnimationListener listener) {
+        if (parentView instanceof ViewGroup == false) {
+            throw new IllegalArgumentException("parentView is not a ViewGroup");
+        }
+        if (childView instanceof View == false) {
+            throw new IllegalArgumentException("childView is not a View");
+        }
+
+        try {
+            Bridge.prepareThread();
+            mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
+            if (mLastResult.isSuccess()) {
+                mLastResult = mSession.moveChild((ViewGroup) parentView, (View) childView, index,
+                        layoutParams, listener);
+            }
+        } finally {
+            mSession.release();
+            Bridge.cleanupThread();
+        }
+
+        return mLastResult;
+    }
+
+    @Override
+    public Result removeChild(Object childView, IAnimationListener listener) {
+        if (childView instanceof View == false) {
+            throw new IllegalArgumentException("childView is not a View");
+        }
+
+        try {
+            Bridge.prepareThread();
+            mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
+            if (mLastResult.isSuccess()) {
+                mLastResult = mSession.removeChild((View) childView, listener);
+            }
+        } finally {
+            mSession.release();
+            Bridge.cleanupThread();
+        }
+
+        return mLastResult;
+    }
+
+    @Override
+    public void dispose() {
+    }
+
+    /*package*/ BridgeRenderSession(RenderSessionImpl scene, Result lastResult) {
+        mSession = scene;
+        if (scene != null) {
+            mSession.setScene(this);
+        }
+        mLastResult = lastResult;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/LayoutResult.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/LayoutResult.java
deleted file mode 100644
index c4c5225..0000000
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/LayoutResult.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2008 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;
-
-import com.android.layoutlib.api.ILayoutResult;
-
-import java.awt.image.BufferedImage;
-
-/**
- * Implementation of {@link ILayoutResult}
- */
-public final class LayoutResult implements ILayoutResult {
-
-    private final ILayoutViewInfo mRootView;
-    private final BufferedImage mImage;
-    private final int mSuccess;
-    private final String mErrorMessage;
-
-    /**
-     * Creates a {@link #SUCCESS} {@link ILayoutResult} with the specified params
-     * @param rootView
-     * @param image
-     */
-    public LayoutResult(ILayoutViewInfo rootView, BufferedImage image) {
-        mSuccess = SUCCESS;
-        mErrorMessage = null;
-        mRootView = rootView;
-        mImage = image;
-    }
-    
-    /**
-     * Creates a LayoutResult with a specific success code and associated message
-     * @param code
-     * @param message
-     */
-    public LayoutResult(int code, String message) {
-        mSuccess = code;
-        mErrorMessage = message;
-        mRootView = null;
-        mImage = null;
-    }
-
-    public int getSuccess() {
-        return mSuccess;
-    }
-
-    public String getErrorMessage() {
-        return mErrorMessage;
-    }
-
-    public BufferedImage getImage() {
-        return mImage;
-    }
-
-    public ILayoutViewInfo getRootView() {
-        return mRootView;
-    }
-    
-    /**
-     * Implementation of {@link ILayoutResult.ILayoutViewInfo}
-     */
-    public static final class LayoutViewInfo implements ILayoutViewInfo {
-        private final Object mKey;
-        private final String mName;
-        private final int mLeft;
-        private final int mRight;
-        private final int mTop;
-        private final int mBottom;
-        private ILayoutViewInfo[] mChildren;
-
-        public LayoutViewInfo(String name, Object key, int left, int top, int right, int bottom) {
-            mName = name;
-            mKey = key;
-            mLeft = left;
-            mRight = right;
-            mTop = top;
-            mBottom = bottom;
-        }
-        
-        public void setChildren(ILayoutViewInfo[] children) {
-            mChildren = children;
-        }
-
-        public ILayoutViewInfo[] getChildren() {
-            return mChildren;
-        }
-
-        public Object getViewKey() {
-            return mKey;
-        }
-
-        public String getName() {
-            return mName;
-        }
-
-        public int getLeft() {
-            return mLeft;
-        }
-
-        public int getTop() {
-            return mTop;
-        }
-
-        public int getRight() {
-            return mRight;
-        }
-
-        public int getBottom() {
-            return mBottom;
-        }
-    }
-}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java
deleted file mode 100644
index abbf2f0..0000000
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2008 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;
-
-import com.android.ninepatch.NinePatch;
-
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-
-public class NinePatchDrawable extends Drawable {
-
-    private NinePatch m9Patch;
-
-    NinePatchDrawable(NinePatch ninePatch) {
-        m9Patch = ninePatch;
-    }
-
-    @Override
-    public int getMinimumWidth() {
-        return m9Patch.getWidth();
-    }
-
-    @Override
-    public int getMinimumHeight() {
-        return m9Patch.getHeight();
-    }
-
-    /**
-     * Return the intrinsic width of the underlying drawable object.  Returns
-     * -1 if it has no intrinsic width, such as with a solid color.
-     */
-    @Override
-    public int getIntrinsicWidth() {
-        return m9Patch.getWidth();
-    }
-
-    /**
-     * Return the intrinsic height of the underlying drawable object. Returns
-     * -1 if it has no intrinsic height, such as with a solid color.
-     */
-    @Override
-    public int getIntrinsicHeight() {
-        return m9Patch.getHeight();
-    }
-
-    /**
-     * Return in padding the insets suggested by this Drawable for placing
-     * content inside the drawable's bounds. Positive values move toward the
-     * center of the Drawable (set Rect.inset). Returns true if this drawable
-     * actually has a padding, else false. When false is returned, the padding
-     * is always set to 0.
-     */
-    @Override
-    public boolean getPadding(Rect padding) {
-        int[] padd = new int[4];
-        m9Patch.getPadding(padd);
-        padding.left = padd[0];
-        padding.top = padd[1];
-        padding.right = padd[2];
-        padding.bottom = padd[3];
-        return true;
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        Rect r = getBounds();
-        m9Patch.draw(canvas.getGraphics2d(), r.left, r.top, r.width(), r.height());
-
-        return;
-    }
-
-
-    // ----------- Not implemented methods ---------------
-
-
-    @Override
-    public int getOpacity() {
-        // FIXME
-        return 0xFF;
-    }
-
-    @Override
-    public void setAlpha(int arg0) {
-        // FIXME !
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter arg0) {
-        // FIXME
-    }
-}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java
deleted file mode 100644
index f624753..0000000
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Copyright (C) 2008 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;
-
-import com.android.layoutlib.api.IDensityBasedResourceValue;
-import com.android.layoutlib.api.IResourceValue;
-import com.android.layoutlib.api.IDensityBasedResourceValue.Density;
-import com.android.ninepatch.NinePatch;
-
-import org.kxml2.io.KXmlParser;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.util.TypedValue;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Helper class to provide various convertion method used in handling android resources.
- */
-public final class ResourceHelper {
-
-    private final static Pattern sFloatPattern = Pattern.compile("(-?[0-9]+(?:\\.[0-9]+)?)(.*)");
-    private final static float[] sFloatOut = new float[1];
-
-    private final static TypedValue mValue = new TypedValue();
-
-    /**
-     * Returns the color value represented by the given string value
-     * @param value the color value
-     * @return the color as an int
-     * @throw NumberFormatException if the conversion failed.
-     */
-    static int getColor(String value) {
-        if (value != null) {
-            if (value.startsWith("#") == false) {
-                throw new NumberFormatException();
-            }
-
-            value = value.substring(1);
-
-            // make sure it's not longer than 32bit
-            if (value.length() > 8) {
-                throw new NumberFormatException();
-            }
-
-            if (value.length() == 3) { // RGB format
-                char[] color = new char[8];
-                color[0] = color[1] = 'F';
-                color[2] = color[3] = value.charAt(0);
-                color[4] = color[5] = value.charAt(1);
-                color[6] = color[7] = value.charAt(2);
-                value = new String(color);
-            } else if (value.length() == 4) { // ARGB format
-                char[] color = new char[8];
-                color[0] = color[1] = value.charAt(0);
-                color[2] = color[3] = value.charAt(1);
-                color[4] = color[5] = value.charAt(2);
-                color[6] = color[7] = value.charAt(3);
-                value = new String(color);
-            } else if (value.length() == 6) {
-                value = "FF" + value;
-            }
-
-            // this is a RRGGBB or AARRGGBB value
-
-            // Integer.parseInt will fail to parse strings like "ff191919", so we use
-            // a Long, but cast the result back into an int, since we know that we're only
-            // dealing with 32 bit values.
-            return (int)Long.parseLong(value, 16);
-        }
-
-        throw new NumberFormatException();
-    }
-
-    /**
-     * Returns a drawable from the given value.
-     * @param value The value that contains a path to a 9 patch, a bitmap or a xml based drawable,
-     * or an hexadecimal color
-     * @param context
-     * @param isFramework indicates whether the resource is a framework resources.
-     * Framework resources are cached, and loaded only once.
-     */
-    public static Drawable getDrawable(IResourceValue value, BridgeContext context, boolean isFramework) {
-        Drawable d = null;
-
-        String stringValue = value.getValue();
-
-        String lowerCaseValue = stringValue.toLowerCase();
-
-        if (lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) {
-            File file = new File(stringValue);
-            if (file.isFile()) {
-                NinePatch ninePatch = Bridge.getCached9Patch(stringValue,
-                        isFramework ? null : context.getProjectKey());
-
-                if (ninePatch == null) {
-                    try {
-                        ninePatch = NinePatch.load(file.toURL(), false /* convert */);
-
-                        Bridge.setCached9Patch(stringValue, ninePatch,
-                                isFramework ? null : context.getProjectKey());
-                    } catch (MalformedURLException e) {
-                        // URL is wrong, we'll return null below
-                    } catch (IOException e) {
-                        // failed to read the file, we'll return null below.
-                    }
-                }
-
-                if (ninePatch != null) {
-                    return new NinePatchDrawable(ninePatch);
-                }
-            }
-
-            return null;
-        } else if (lowerCaseValue.endsWith(".xml")) {
-            // create a blockparser for the file
-            File f = new File(stringValue);
-            if (f.isFile()) {
-                try {
-                    // let the framework inflate the Drawable from the XML file.
-                    KXmlParser parser = new KXmlParser();
-                    parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                    parser.setInput(new FileReader(f));
-
-                    d = Drawable.createFromXml(context.getResources(),
-                            new BridgeXmlBlockParser(parser, context, isFramework));
-                    return d;
-                } catch (XmlPullParserException e) {
-                    context.getLogger().error(e);
-                } catch (FileNotFoundException e) {
-                    // will not happen, since we pre-check
-                } catch (IOException e) {
-                    context.getLogger().error(e);
-                }
-            }
-
-            return null;
-        } else {
-            File bmpFile = new File(stringValue);
-            if (bmpFile.isFile()) {
-                try {
-                    Bitmap bitmap = Bridge.getCachedBitmap(stringValue,
-                            isFramework ? null : context.getProjectKey());
-
-                    if (bitmap == null) {
-                        bitmap = new Bitmap(bmpFile);
-                        try {
-                            bitmap.setDensity(Density.MEDIUM.getValue());
-                        } catch (NoClassDefFoundError error) {
-                            // look like we're running in an older version of ADT that doesn't
-                            // include the new layoutlib_api. Let's just ignore this, the drawing
-                            // will just be wrong.
-                        }
-                        Bridge.setCachedBitmap(stringValue, bitmap,
-                                isFramework ? null : context.getProjectKey());
-                    }
-
-                    try {
-                        if (value instanceof IDensityBasedResourceValue) {
-                            Density density = ((IDensityBasedResourceValue)value).getDensity();
-                            if (density != Density.MEDIUM) {
-                                // create a copy of the bitmap
-                                bitmap = Bitmap.createBitmap(bitmap);
-
-                                // apply the density
-                                bitmap.setDensity(density.getValue());
-                            }
-                        }
-                    } catch (NoClassDefFoundError error) {
-                        // look like we're running in an older version of ADT that doesn't include
-                        // the new layoutlib_api. Let's just ignore this, the drawing will just be
-                        // wrong.
-                    }
-
-                    return new BitmapDrawable(context.getResources(), bitmap);
-                } catch (IOException e) {
-                    // we'll return null below
-                    // TODO: log the error.
-                }
-            } else {
-                // attempt to get a color from the value
-                try {
-                    int color = getColor(stringValue);
-                    return new ColorDrawable(color);
-                } catch (NumberFormatException e) {
-                    // we'll return null below.
-                    // TODO: log the error
-                }
-            }
-        }
-
-        return null;
-    }
-
-
-    // ------- TypedValue stuff
-    // This is taken from //device/libs/utils/ResourceTypes.cpp
-
-    private static final class UnitEntry {
-        String name;
-        int type;
-        int unit;
-        float scale;
-
-        UnitEntry(String name, int type, int unit, float scale) {
-            this.name = name;
-            this.type = type;
-            this.unit = unit;
-            this.scale = scale;
-        }
-    }
-
-    private final static UnitEntry[] sUnitNames = new UnitEntry[] {
-        new UnitEntry("px", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_PX, 1.0f),
-        new UnitEntry("dip", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_DIP, 1.0f),
-        new UnitEntry("dp", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_DIP, 1.0f),
-        new UnitEntry("sp", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_SP, 1.0f),
-        new UnitEntry("pt", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_PT, 1.0f),
-        new UnitEntry("in", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_IN, 1.0f),
-        new UnitEntry("mm", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_MM, 1.0f),
-        new UnitEntry("%", TypedValue.TYPE_FRACTION, TypedValue.COMPLEX_UNIT_FRACTION, 1.0f/100),
-        new UnitEntry("%p", TypedValue.TYPE_FRACTION, TypedValue.COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100),
-    };
-
-    /**
-     * Returns the raw value from the given string.
-     * This object is only valid until the next call on to {@link ResourceHelper}.
-     */
-    public static TypedValue getValue(String s) {
-        if (stringToFloat(s, mValue)) {
-            return mValue;
-        }
-
-        return null;
-    }
-
-    /**
-     * Convert the string into a {@link TypedValue}.
-     * @param s
-     * @param outValue
-     * @return true if success.
-     */
-    public static boolean stringToFloat(String s, TypedValue outValue) {
-        // remove the space before and after
-        s.trim();
-        int len = s.length();
-
-        if (len <= 0) {
-            return false;
-        }
-
-        // check that there's no non ascii characters.
-        char[] buf = s.toCharArray();
-        for (int i = 0 ; i < len ; i++) {
-            if (buf[i] > 255) {
-                return false;
-            }
-        }
-
-        // check the first character
-        if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.') {
-            return false;
-        }
-
-        // now look for the string that is after the float...
-        Matcher m = sFloatPattern.matcher(s);
-        if (m.matches()) {
-            String f_str = m.group(1);
-            String end = m.group(2);
-
-            float f;
-            try {
-                f = Float.parseFloat(f_str);
-            } catch (NumberFormatException e) {
-                // this shouldn't happen with the regexp above.
-                return false;
-            }
-
-            if (end.length() > 0 && end.charAt(0) != ' ') {
-                // Might be a unit...
-                if (parseUnit(end, outValue, sFloatOut)) {
-
-                    f *= sFloatOut[0];
-                    boolean neg = f < 0;
-                    if (neg) {
-                        f = -f;
-                    }
-                    long bits = (long)(f*(1<<23)+.5f);
-                    int radix;
-                    int shift;
-                    if ((bits&0x7fffff) == 0) {
-                        // Always use 23p0 if there is no fraction, just to make
-                        // things easier to read.
-                        radix = TypedValue.COMPLEX_RADIX_23p0;
-                        shift = 23;
-                    } else if ((bits&0xffffffffff800000L) == 0) {
-                        // Magnitude is zero -- can fit in 0 bits of precision.
-                        radix = TypedValue.COMPLEX_RADIX_0p23;
-                        shift = 0;
-                    } else if ((bits&0xffffffff80000000L) == 0) {
-                        // Magnitude can fit in 8 bits of precision.
-                        radix = TypedValue.COMPLEX_RADIX_8p15;
-                        shift = 8;
-                    } else if ((bits&0xffffff8000000000L) == 0) {
-                        // Magnitude can fit in 16 bits of precision.
-                        radix = TypedValue.COMPLEX_RADIX_16p7;
-                        shift = 16;
-                    } else {
-                        // Magnitude needs entire range, so no fractional part.
-                        radix = TypedValue.COMPLEX_RADIX_23p0;
-                        shift = 23;
-                    }
-                    int mantissa = (int)(
-                        (bits>>shift) & TypedValue.COMPLEX_MANTISSA_MASK);
-                    if (neg) {
-                        mantissa = (-mantissa) & TypedValue.COMPLEX_MANTISSA_MASK;
-                    }
-                    outValue.data |=
-                        (radix<<TypedValue.COMPLEX_RADIX_SHIFT)
-                        | (mantissa<<TypedValue.COMPLEX_MANTISSA_SHIFT);
-                    return true;
-                }
-                return false;
-            }
-
-            // make sure it's only spaces at the end.
-            end = end.trim();
-
-            if (end.length() == 0) {
-                if (outValue != null) {
-                    outValue.type = TypedValue.TYPE_FLOAT;
-                    outValue.data = Float.floatToIntBits(f);
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    private static boolean parseUnit(String str, TypedValue outValue, float[] outScale) {
-        str = str.trim();
-
-        for (UnitEntry unit : sUnitNames) {
-            if (unit.name.equals(str)) {
-                outValue.type = unit.type;
-                outValue.data = unit.unit << TypedValue.COMPLEX_UNIT_SHIFT;
-                outScale[0] = unit.scale;
-
-                return true;
-            }
-        }
-
-        return false;
-    }
-}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceValue.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceValue.java
deleted file mode 100644
index 01a4871..0000000
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceValue.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2008 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;
-
-import com.android.layoutlib.api.IResourceValue;
-
-/**
- * Basic implementation of IResourceValue.
- */
-class ResourceValue implements IResourceValue {
-    private final String mType;
-    private final String mName;
-    private String mValue = null;
-    
-    ResourceValue(String name) {
-        mType = null;
-        mName = name;
-    }
-
-    public ResourceValue(String type, String name, String value) {
-        mType = type;
-        mName = name;
-        mValue = value;
-    }
-
-    public String getType() {
-        return mType;
-    }
-
-    public final String getName() {
-        return mName;
-    }
-    
-    public final String getValue() {
-        return mValue;
-    }
-    
-    public final void setValue(String value) {
-        mValue = value;
-    }
-    
-    public void replaceWith(ResourceValue value) {
-        mValue = value.mValue;
-    }
-
-    public boolean isFramework() {
-        // ResourceValue object created directly in the framework are used to describe
-        // non resolvable coming from the XML. Since they will never be cached (as they can't
-        // be a value pointing to a bitmap, or they'd be resolvable.), the return value deoes
-        // not matter.
-        return false;
-    }
-}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeAssetManager.java
similarity index 89%
rename from tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
rename to tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeAssetManager.java
index 71803fc..a825060 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeAssetManager.java
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.layoutlib.bridge;
+package com.android.layoutlib.bridge.android;
+
+import com.android.layoutlib.bridge.Bridge;
 
 import android.content.res.AssetManager;
 
@@ -28,7 +30,7 @@
      * <p/>
      * {@link Bridge} calls this method after setting up a new bridge.
      */
-    /*package*/ static AssetManager initSystem() {
+    /*package*/ public static AssetManager initSystem() {
         if (!(AssetManager.sSystem instanceof BridgeAssetManager)) {
             // Note that AssetManager() creates a system AssetManager and we override it
             // with our BridgeAssetManager.
@@ -42,7 +44,7 @@
      * Clears the static {@link AssetManager#sSystem} to make sure we don't leave objects
      * around that would prevent us from unloading the library.
      */
-    /*package*/ static void clearSystem() {
+    public static void clearSystem() {
         AssetManager.sSystem = null;
     }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
new file mode 100644
index 0000000..3835378
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2010 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.android;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentValues;
+import android.content.IContentProvider;
+import android.content.OperationApplicationException;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.database.CursorWindow;
+import android.database.IBulkCursor;
+import android.database.IContentObserver;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+
+/**
+ * Mock implementation of {@link IContentProvider}.
+ *
+ * TODO: never return null when the method is not supposed to. Return fake data instead.
+ */
+public final class BridgeContentProvider implements IContentProvider {
+
+    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> arg0)
+            throws RemoteException, OperationApplicationException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public int bulkInsert(Uri arg0, ContentValues[] arg1) throws RemoteException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public IBulkCursor bulkQuery(Uri arg0, String[] arg1, String arg2, String[] arg3,
+            String arg4, IContentObserver arg5, CursorWindow arg6) throws RemoteException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public Bundle call(String arg0, String arg1, Bundle arg2) throws RemoteException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public int delete(Uri arg0, String arg1, String[] arg2) throws RemoteException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public String getType(Uri arg0) throws RemoteException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public Uri insert(Uri arg0, ContentValues arg1) throws RemoteException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public AssetFileDescriptor openAssetFile(Uri arg0, String arg1) throws RemoteException,
+            FileNotFoundException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public ParcelFileDescriptor openFile(Uri arg0, String arg1) throws RemoteException,
+            FileNotFoundException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4)
+            throws RemoteException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3)
+            throws RemoteException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public IBinder asBinder() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public String[] getStreamTypes(Uri arg0, String arg1) throws RemoteException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public AssetFileDescriptor openTypedAssetFile(Uri arg0, String arg1, Bundle arg2)
+            throws RemoteException, FileNotFoundException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentResolver.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentResolver.java
new file mode 100644
index 0000000..0257686
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentResolver.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2009 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.android;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.IContentProvider;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Bundle;
+
+/**
+ * A mock content resolver for the LayoutLib Bridge.
+ * <p/>
+ * It won't serve any actual data but it's good enough for all
+ * the widgets which expect to have a content resolver available via
+ * {@link BridgeContext#getContentResolver()}.
+ */
+public class BridgeContentResolver extends ContentResolver {
+
+    private BridgeContentProvider mProvider = null;
+
+    public BridgeContentResolver(Context context) {
+        super(context);
+    }
+
+    @Override
+    public IContentProvider acquireProvider(Context c, String name) {
+        if (mProvider == null) {
+            mProvider = new BridgeContentProvider();
+        }
+
+        return mProvider;
+    }
+
+    @Override
+    public IContentProvider acquireExistingProvider(Context c, String name) {
+        if (mProvider == null) {
+            mProvider = new BridgeContentProvider();
+        }
+
+        return mProvider;
+    }
+
+    @Override
+    public boolean releaseProvider(IContentProvider icp) {
+        // ignore
+        return false;
+    }
+
+    /**
+     * Stub for the layoutlib bridge content resolver.
+     */
+    @Override
+    public void registerContentObserver(Uri uri, boolean notifyForDescendents,
+            ContentObserver observer) {
+        // pass
+    }
+
+    /**
+     * Stub for the layoutlib bridge content resolver.
+     */
+    @Override
+    public void unregisterContentObserver(ContentObserver observer) {
+        // pass
+    }
+
+    /**
+     * Stub for the layoutlib bridge content resolver.
+     */
+    @Override
+    public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+        // pass
+    }
+
+    /**
+     * Stub for the layoutlib bridge content resolver.
+     */
+    @Override
+    public void startSync(Uri uri, Bundle extras) {
+        // pass
+    }
+
+    /**
+     * Stub for the layoutlib bridge content resolver.
+     */
+    @Override
+    public void cancelSync(Uri uri) {
+        // pass
+    }
+}
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
new file mode 100644
index 0000000..7d794bd
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -0,0 +1,1149 @@
+/*
+ * Copyright (C) 2008 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.android;
+
+import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.StyleResourceValue;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.BridgeConstants;
+import com.android.layoutlib.bridge.impl.Stack;
+import com.android.resources.ResourceType;
+import com.android.util.Pair;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.Resources.Theme;
+import android.database.DatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+
+/**
+ * Custom implementation of Context/Activity to handle non compiled resources.
+ */
+public final class BridgeContext extends Activity {
+
+    private Resources mSystemResources;
+    private final HashMap<View, Object> mViewKeyMap = new HashMap<View, Object>();
+    private final Object mProjectKey;
+    private final DisplayMetrics mMetrics;
+    private final RenderResources mRenderResources;
+    private final ApplicationInfo mApplicationInfo;
+
+    private final Map<Object, Map<String, String>> mDefaultPropMaps =
+        new IdentityHashMap<Object, Map<String,String>>();
+
+    // maps for dynamically generated id representing style objects (StyleResourceValue)
+    private Map<Integer, StyleResourceValue> mDynamicIdToStyleMap;
+    private Map<StyleResourceValue, Integer> mStyleToDynamicIdMap;
+    private int mDynamicIdGenerator = 0x01030000; // Base id for framework R.style
+
+    // cache for TypedArray generated from IStyleResourceValue object
+    private Map<int[], Map<Integer, TypedArray>> mTypedArrayCache;
+    private BridgeInflater mBridgeInflater;
+
+    private final IProjectCallback mProjectCallback;
+    private BridgeContentResolver mContentResolver;
+
+    private final Stack<BridgeXmlBlockParser> mParserStack = new Stack<BridgeXmlBlockParser>();
+
+    /**
+     * @param projectKey An Object identifying the project. This is used for the cache mechanism.
+     * @param metrics the {@link DisplayMetrics}.
+     * @param themeName The name of the theme to use.
+     * @param projectResources the resources of the project. The map contains (String, map) pairs
+     * where the string is the type of the resource reference used in the layout file, and the
+     * map contains (String, {@link }) pairs where the key is the resource name,
+     * and the value is the resource value.
+     * @param frameworkResources the framework resources. The map contains (String, map) pairs
+     * where the string is the type of the resource reference used in the layout file, and the map
+     * contains (String, {@link ResourceValue}) pairs where the key is the resource name, and the
+     * value is the resource value.
+     * @param styleInheritanceMap
+     * @param projectCallback
+     * @param targetSdkVersion the targetSdkVersion of the application.
+     */
+    public BridgeContext(Object projectKey, DisplayMetrics metrics,
+            RenderResources renderResources,
+            IProjectCallback projectCallback,
+            int targetSdkVersion) {
+        mProjectKey = projectKey;
+        mMetrics = metrics;
+        mProjectCallback = projectCallback;
+
+        mRenderResources = renderResources;
+
+        mFragments.mCurState = Fragment.CREATED;
+        mFragments.mActivity = this;
+
+        mApplicationInfo = new ApplicationInfo();
+        mApplicationInfo.targetSdkVersion = targetSdkVersion;
+    }
+
+    /**
+     * Initializes the {@link Resources} singleton to be linked to this {@link Context}, its
+     * {@link DisplayMetrics}, {@link Configuration}, and {@link IProjectCallback}.
+     *
+     * @see #disposeResources()
+     */
+    public void initResources() {
+        AssetManager assetManager = AssetManager.getSystem();
+        Configuration config = new Configuration();
+
+        mSystemResources = BridgeResources.initSystem(
+                this,
+                assetManager,
+                mMetrics,
+                config,
+                mProjectCallback);
+        mTheme = mSystemResources.newTheme();
+    }
+
+    /**
+     * Disposes the {@link Resources} singleton.
+     */
+    public void disposeResources() {
+        BridgeResources.disposeSystem();
+    }
+
+    public void setBridgeInflater(BridgeInflater inflater) {
+        mBridgeInflater = inflater;
+    }
+
+    public void addViewKey(View view, Object viewKey) {
+        mViewKeyMap.put(view, viewKey);
+    }
+
+    public Object getViewKey(View view) {
+        return mViewKeyMap.get(view);
+    }
+
+    public Object getProjectKey() {
+        return mProjectKey;
+    }
+
+    public DisplayMetrics getMetrics() {
+        return mMetrics;
+    }
+
+    public IProjectCallback getProjectCallback() {
+        return mProjectCallback;
+    }
+
+    public RenderResources getRenderResources() {
+        return mRenderResources;
+    }
+
+    public Map<String, String> getDefaultPropMap(Object key) {
+        return mDefaultPropMaps.get(key);
+    }
+
+    /**
+     * Adds a parser to the stack.
+     * @param parser the parser to add.
+     */
+    public void pushParser(BridgeXmlBlockParser parser) {
+        mParserStack.push(parser);
+    }
+
+    /**
+     * Removes the parser at the top of the stack
+     */
+    public void popParser() {
+        mParserStack.pop();
+    }
+
+    /**
+     * Returns the current parser at the top the of the stack.
+     * @return a parser or null.
+     */
+    public BridgeXmlBlockParser getCurrentParser() {
+        return mParserStack.peek();
+    }
+
+    /**
+     * Returns the previous parser.
+     * @return a parser or null if there isn't any previous parser
+     */
+    public BridgeXmlBlockParser getPreviousParser() {
+        if (mParserStack.size() < 2) {
+            return null;
+        }
+        return mParserStack.get(mParserStack.size() - 2);
+    }
+
+    public boolean resolveThemeAttribute(int resid, TypedValue outValue, boolean resolveRefs) {
+        Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(resid);
+        if (resourceInfo == null) {
+            resourceInfo = mProjectCallback.resolveResourceId(resid);
+        }
+
+        if (resourceInfo == null) {
+            return false;
+        }
+
+        ResourceValue value = mRenderResources.findItemInTheme(resourceInfo.getSecond());
+        if (resolveRefs) {
+            value = mRenderResources.resolveResValue(value);
+        }
+
+        // check if this is a style resource
+        if (value instanceof StyleResourceValue) {
+            // get the id that will represent this style.
+            outValue.resourceId = getDynamicIdByStyle((StyleResourceValue)value);
+            return true;
+        }
+
+
+        int a;
+        // if this is a framework value.
+        if (value.isFramework()) {
+            // look for idName in the android R classes.
+            // use 0 a default res value as it's not a valid id value.
+            a = getFrameworkResourceValue(value.getResourceType(), value.getName(), 0 /*defValue*/);
+        } else {
+            // look for idName in the project R class.
+            // use 0 a default res value as it's not a valid id value.
+            a = getProjectResourceValue(value.getResourceType(), value.getName(), 0 /*defValue*/);
+        }
+
+        if (a != 0) {
+            outValue.resourceId = a;
+            return true;
+        }
+
+        return false;
+    }
+
+
+    // ------------- Activity Methods
+
+    @Override
+    public LayoutInflater getLayoutInflater() {
+        return mBridgeInflater;
+    }
+
+    // ------------ Context methods
+
+    @Override
+    public Resources getResources() {
+        return mSystemResources;
+    }
+
+    @Override
+    public Theme getTheme() {
+        return mTheme;
+    }
+
+    @Override
+    public ClassLoader getClassLoader() {
+        return this.getClass().getClassLoader();
+    }
+
+    @Override
+    public Object getSystemService(String service) {
+        if (LAYOUT_INFLATER_SERVICE.equals(service)) {
+            return mBridgeInflater;
+        }
+
+        // AutoCompleteTextView and MultiAutoCompleteTextView want a window
+        // service. We don't have any but it's not worth an exception.
+        if (WINDOW_SERVICE.equals(service)) {
+            return null;
+        }
+
+        // needed by SearchView
+        if (INPUT_METHOD_SERVICE.equals(service)) {
+            return null;
+        }
+
+        throw new UnsupportedOperationException("Unsupported Service: " + service);
+    }
+
+
+    @Override
+    public final TypedArray obtainStyledAttributes(int[] attrs) {
+        return createStyleBasedTypedArray(mRenderResources.getCurrentTheme(), attrs);
+    }
+
+    @Override
+    public final TypedArray obtainStyledAttributes(int resid, int[] attrs)
+            throws Resources.NotFoundException {
+        // get the StyleResourceValue based on the resId;
+        StyleResourceValue style = getStyleByDynamicId(resid);
+
+        if (style == null) {
+            throw new Resources.NotFoundException();
+        }
+
+        if (mTypedArrayCache == null) {
+            mTypedArrayCache = new HashMap<int[], Map<Integer,TypedArray>>();
+
+            Map<Integer, TypedArray> map = new HashMap<Integer, TypedArray>();
+            mTypedArrayCache.put(attrs, map);
+
+            BridgeTypedArray ta = createStyleBasedTypedArray(style, attrs);
+            map.put(resid, ta);
+
+            return ta;
+        }
+
+        // get the 2nd map
+        Map<Integer, TypedArray> map = mTypedArrayCache.get(attrs);
+        if (map == null) {
+            map = new HashMap<Integer, TypedArray>();
+            mTypedArrayCache.put(attrs, map);
+        }
+
+        // get the array from the 2nd map
+        TypedArray ta = map.get(resid);
+
+        if (ta == null) {
+            ta = createStyleBasedTypedArray(style, attrs);
+            map.put(resid, ta);
+        }
+
+        return ta;
+    }
+
+    @Override
+    public final TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs) {
+        return obtainStyledAttributes(set, attrs, 0, 0);
+    }
+
+    @Override
+    public TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs,
+            int defStyleAttr, int defStyleRes) {
+
+        Map<String, String> defaultPropMap = null;
+        boolean isPlatformFile = true;
+
+        // Hint: for XmlPullParser, attach source //DEVICE_SRC/dalvik/libcore/xml/src/java
+        if (set instanceof BridgeXmlBlockParser) {
+            BridgeXmlBlockParser parser = null;
+            parser = (BridgeXmlBlockParser)set;
+
+            isPlatformFile = parser.isPlatformFile();
+
+            Object key = parser.getViewCookie();
+            if (key != null) {
+                defaultPropMap = mDefaultPropMaps.get(key);
+                if (defaultPropMap == null) {
+                    defaultPropMap = new HashMap<String, String>();
+                    mDefaultPropMaps.put(key, defaultPropMap);
+                }
+            }
+
+        } else if (set instanceof BridgeLayoutParamsMapAttributes) {
+            // this is only for temp layout params generated dynamically, so this is never
+            // platform content.
+            isPlatformFile = false;
+        } else if (set != null) { // null parser is ok
+            // really this should not be happening since its instantiated in Bridge
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                    "Parser is not a BridgeXmlBlockParser!", null /*data*/);
+            return null;
+        }
+
+        boolean[] frameworkAttributes = new boolean[1];
+        TreeMap<Integer, String> styleNameMap = searchAttrs(attrs, frameworkAttributes);
+
+        BridgeTypedArray ta = ((BridgeResources) mSystemResources).newTypeArray(attrs.length,
+                isPlatformFile);
+
+        // resolve the defStyleAttr value into a IStyleResourceValue
+        StyleResourceValue defStyleValues = null;
+
+        // look for a custom style.
+        String customStyle = null;
+        if (set != null) {
+            customStyle = set.getAttributeValue(null /* namespace*/, "style");
+        }
+        if (customStyle != null) {
+            ResourceValue item = mRenderResources.findResValue(customStyle,
+                    false /*forceFrameworkOnly*/);
+
+            // resolve it in case it links to something else
+            item = mRenderResources.resolveResValue(item);
+
+            if (item instanceof StyleResourceValue) {
+                defStyleValues = (StyleResourceValue)item;
+            }
+        }
+
+        if (defStyleValues == null) {
+            if (defStyleAttr != 0) {
+                // get the name from the int.
+                String defStyleName = searchAttr(defStyleAttr);
+
+                if (defaultPropMap != null) {
+                    defaultPropMap.put("style", defStyleName);
+                }
+
+                // look for the style in the current theme, and its parent:
+                ResourceValue item = mRenderResources.findItemInTheme(defStyleName);
+
+                if (item != null) {
+                    // item is a reference to a style entry. Search for it.
+                    item = mRenderResources.findResValue(item.getValue(),
+                            false /*forceFrameworkOnly*/);
+
+                    if (item instanceof StyleResourceValue) {
+                        defStyleValues = (StyleResourceValue)item;
+                    }
+                } else {
+                    Bridge.getLog().error(null,
+                            String.format(
+                                    "Failed to find style '%s' in current theme", defStyleName),
+                            null /*data*/);
+                }
+            } else if (defStyleRes != 0) {
+                Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes);
+                if (value == null) {
+                    value = mProjectCallback.resolveResourceId(defStyleRes);
+                }
+
+                if (value != null) {
+                    if (value.getFirst() == ResourceType.STYLE) {
+                        // look for the style in the current theme, and its parent:
+                        ResourceValue item = mRenderResources.findItemInTheme(value.getSecond());
+                        if (item != null) {
+                            if (item instanceof StyleResourceValue) {
+                                if (defaultPropMap != null) {
+                                    defaultPropMap.put("style", item.getName());
+                                }
+
+                                defStyleValues = (StyleResourceValue)item;
+                            }
+                        } else {
+                            Bridge.getLog().error(null,
+                                    String.format(
+                                            "Style with id 0x%x (resolved to '%s') does not exist.",
+                                            defStyleRes, value.getSecond()),
+                                    null /*data*/);
+                        }
+                    } else {
+                        Bridge.getLog().error(null,
+                                String.format(
+                                        "Resouce id 0x%x is not of type STYLE (instead %s)",
+                                        defStyleRes, value.getFirst().toString()),
+                                null /*data*/);
+                    }
+                } else {
+                    Bridge.getLog().error(null,
+                            String.format(
+                                    "Failed to find style with id 0x%x in current theme",
+                                    defStyleRes),
+                            null /*data*/);
+                }
+            }
+        }
+
+        String namespace = BridgeConstants.NS_RESOURCES;
+        if (frameworkAttributes[0] == false) {
+            // need to use the application namespace
+            namespace = mProjectCallback.getNamespace();
+        }
+
+        if (styleNameMap != null) {
+            for (Entry<Integer, String> styleAttribute : styleNameMap.entrySet()) {
+                int index = styleAttribute.getKey().intValue();
+
+                String name = styleAttribute.getValue();
+                String value = null;
+                if (set != null) {
+                    value = set.getAttributeValue(namespace, name);
+                }
+
+                // if there's no direct value for this attribute in the XML, we look for default
+                // values in the widget defStyle, and then in the theme.
+                if (value == null) {
+                    ResourceValue resValue = null;
+
+                    // look for the value in the defStyle first (and its parent if needed)
+                    if (defStyleValues != null) {
+                        resValue = mRenderResources.findItemInStyle(defStyleValues, name);
+                    }
+
+                    // if the item is not present in the defStyle, we look in the main theme (and
+                    // its parent themes)
+                    if (resValue == null) {
+                        resValue = mRenderResources.findItemInTheme(name);
+                    }
+
+                    // if we found a value, we make sure this doesn't reference another value.
+                    // So we resolve it.
+                    if (resValue != null) {
+                        // put the first default value, before the resolution.
+                        if (defaultPropMap != null) {
+                            defaultPropMap.put(name, resValue.getValue());
+                        }
+
+                        resValue = mRenderResources.resolveResValue(resValue);
+                    }
+
+                    ta.bridgeSetValue(index, name, resValue);
+                } else {
+                    // there is a value in the XML, but we need to resolve it in case it's
+                    // referencing another resource or a theme value.
+                    ta.bridgeSetValue(index, name,
+                            mRenderResources.resolveValue(null, name, value, isPlatformFile));
+                }
+            }
+        }
+
+        ta.sealArray();
+
+        return ta;
+    }
+
+    @Override
+    public Looper getMainLooper() {
+        return Looper.myLooper();
+    }
+
+
+    // ------------- private new methods
+
+    /**
+     * Creates a {@link BridgeTypedArray} by filling the values defined by the int[] with the
+     * values found in the given style.
+     * @see #obtainStyledAttributes(int, int[])
+     */
+    private BridgeTypedArray createStyleBasedTypedArray(StyleResourceValue style, int[] attrs)
+            throws Resources.NotFoundException {
+        TreeMap<Integer, String> styleNameMap = searchAttrs(attrs, null);
+
+        BridgeTypedArray ta = ((BridgeResources) mSystemResources).newTypeArray(attrs.length,
+                false /* platformResourceFlag */);
+
+        // loop through all the values in the style map, and init the TypedArray with
+        // the style we got from the dynamic id
+        for (Entry<Integer, String> styleAttribute : styleNameMap.entrySet()) {
+            int index = styleAttribute.getKey().intValue();
+
+            String name = styleAttribute.getValue();
+
+            // get the value from the style, or its parent styles.
+            ResourceValue resValue = mRenderResources.findItemInStyle(style, name);
+
+            // resolve it to make sure there are no references left.
+            ta.bridgeSetValue(index, name, mRenderResources.resolveResValue(resValue));
+        }
+
+        ta.sealArray();
+
+        return ta;
+    }
+
+
+    /**
+     * The input int[] attrs is one of com.android.internal.R.styleable fields where the name
+     * of the field is the style being referenced and the array contains one index per attribute.
+     * <p/>
+     * searchAttrs() finds all the names of the attributes referenced so for example if
+     * attrs == com.android.internal.R.styleable.View, this returns the list of the "xyz" where
+     * there's a field com.android.internal.R.styleable.View_xyz and the field value is the index
+     * that is used to reference the attribute later in the TypedArray.
+     *
+     * @param attrs An attribute array reference given to obtainStyledAttributes.
+     * @return A sorted map Attribute-Value to Attribute-Name for all attributes declared by the
+     *         attribute array. Returns null if nothing is found.
+     */
+    private TreeMap<Integer,String> searchAttrs(int[] attrs, boolean[] outFrameworkFlag) {
+        // get the name of the array from the framework resources
+        String arrayName = Bridge.resolveResourceId(attrs);
+        if (arrayName != null) {
+            // if we found it, get the name of each of the int in the array.
+            TreeMap<Integer,String> attributes = new TreeMap<Integer, String>();
+            for (int i = 0 ; i < attrs.length ; i++) {
+                Pair<ResourceType, String> info = Bridge.resolveResourceId(attrs[i]);
+                if (info != null) {
+                    attributes.put(i, info.getSecond());
+                } else {
+                    // FIXME Not sure what we should be doing here...
+                    attributes.put(i, null);
+                }
+            }
+
+            if (outFrameworkFlag != null) {
+                outFrameworkFlag[0] = true;
+            }
+
+            return attributes;
+        }
+
+        // if the name was not found in the framework resources, look in the project
+        // resources
+        arrayName = mProjectCallback.resolveResourceId(attrs);
+        if (arrayName != null) {
+            TreeMap<Integer,String> attributes = new TreeMap<Integer, String>();
+            for (int i = 0 ; i < attrs.length ; i++) {
+                Pair<ResourceType, String> info = mProjectCallback.resolveResourceId(attrs[i]);
+                if (info != null) {
+                    attributes.put(i, info.getSecond());
+                } else {
+                    // FIXME Not sure what we should be doing here...
+                    attributes.put(i, null);
+                }
+            }
+
+            if (outFrameworkFlag != null) {
+                outFrameworkFlag[0] = false;
+            }
+
+            return attributes;
+        }
+
+        return null;
+    }
+
+    /**
+     * Searches for the attribute referenced by its internal id.
+     *
+     * @param attr An attribute reference given to obtainStyledAttributes such as defStyle.
+     * @return The unique name of the attribute, if found, e.g. "buttonStyle". Returns null
+     *         if nothing is found.
+     */
+    public String searchAttr(int attr) {
+        Pair<ResourceType, String> info = Bridge.resolveResourceId(attr);
+        if (info != null) {
+            return info.getSecond();
+        }
+
+        info = mProjectCallback.resolveResourceId(attr);
+        if (info != null) {
+            return info.getSecond();
+        }
+
+        return null;
+    }
+
+    int getDynamicIdByStyle(StyleResourceValue resValue) {
+        if (mDynamicIdToStyleMap == null) {
+            // create the maps.
+            mDynamicIdToStyleMap = new HashMap<Integer, StyleResourceValue>();
+            mStyleToDynamicIdMap = new HashMap<StyleResourceValue, Integer>();
+        }
+
+        // look for an existing id
+        Integer id = mStyleToDynamicIdMap.get(resValue);
+
+        if (id == null) {
+            // generate a new id
+            id = Integer.valueOf(++mDynamicIdGenerator);
+
+            // and add it to the maps.
+            mDynamicIdToStyleMap.put(id, resValue);
+            mStyleToDynamicIdMap.put(resValue, id);
+        }
+
+        return id;
+    }
+
+    private StyleResourceValue getStyleByDynamicId(int i) {
+        if (mDynamicIdToStyleMap != null) {
+            return mDynamicIdToStyleMap.get(i);
+        }
+
+        return null;
+    }
+
+    int getFrameworkResourceValue(ResourceType resType, String resName, int defValue) {
+        Integer value = Bridge.getResourceId(resType, resName);
+        if (value != null) {
+            return value.intValue();
+        }
+
+        return defValue;
+    }
+
+    int getProjectResourceValue(ResourceType resType, String resName, int defValue) {
+        if (mProjectCallback != null) {
+            Integer value = mProjectCallback.getResourceId(resType, resName);
+            if (value != null) {
+                return value.intValue();
+            }
+        }
+
+        return defValue;
+    }
+
+    //------------ NOT OVERRIDEN --------------------
+
+    @Override
+    public boolean bindService(Intent arg0, ServiceConnection arg1, int arg2) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public int checkCallingOrSelfPermission(String arg0) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public int checkCallingOrSelfUriPermission(Uri arg0, int arg1) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public int checkCallingPermission(String arg0) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public int checkCallingUriPermission(Uri arg0, int arg1) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public int checkPermission(String arg0, int arg1, int arg2) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public int checkUriPermission(Uri arg0, int arg1, int arg2, int arg3) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public int checkUriPermission(Uri arg0, String arg1, String arg2, int arg3,
+            int arg4, int arg5) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public void clearWallpaper() {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public Context createPackageContext(String arg0, int arg1) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public String[] databaseList() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public boolean deleteDatabase(String arg0) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public boolean deleteFile(String arg0) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public void enforceCallingOrSelfPermission(String arg0, String arg1) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void enforceCallingOrSelfUriPermission(Uri arg0, int arg1,
+            String arg2) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void enforceCallingPermission(String arg0, String arg1) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void enforceCallingUriPermission(Uri arg0, int arg1, String arg2) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void enforcePermission(String arg0, int arg1, int arg2, String arg3) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void enforceUriPermission(Uri arg0, int arg1, int arg2, int arg3,
+            String arg4) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void enforceUriPermission(Uri arg0, String arg1, String arg2,
+            int arg3, int arg4, int arg5, String arg6) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public String[] fileList() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public AssetManager getAssets() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public File getCacheDir() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public File getExternalCacheDir() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public ContentResolver getContentResolver() {
+        if (mContentResolver == null) {
+            mContentResolver = new BridgeContentResolver(this);
+        }
+        return mContentResolver;
+    }
+
+    @Override
+    public File getDatabasePath(String arg0) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public File getDir(String arg0, int arg1) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public File getFileStreamPath(String arg0) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public File getFilesDir() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public File getExternalFilesDir(String type) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public String getPackageCodePath() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public PackageManager getPackageManager() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public String getPackageName() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public ApplicationInfo getApplicationInfo() {
+        return mApplicationInfo;
+    }
+
+    @Override
+    public String getPackageResourcePath() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public File getSharedPrefsFile(String name) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public SharedPreferences getSharedPreferences(String arg0, int arg1) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Drawable getWallpaper() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public int getWallpaperDesiredMinimumWidth() {
+        return -1;
+    }
+
+    @Override
+    public int getWallpaperDesiredMinimumHeight() {
+        return -1;
+    }
+
+    @Override
+    public void grantUriPermission(String arg0, Uri arg1, int arg2) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public FileInputStream openFileInput(String arg0) throws FileNotFoundException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public FileOutputStream openFileOutput(String arg0, int arg1) throws FileNotFoundException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public SQLiteDatabase openOrCreateDatabase(String arg0, int arg1, CursorFactory arg2) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public SQLiteDatabase openOrCreateDatabase(String arg0, int arg1,
+            CursorFactory arg2, DatabaseErrorHandler arg3) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Drawable peekWallpaper() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1,
+            String arg2, Handler arg3) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public void removeStickyBroadcast(Intent arg0) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void revokeUriPermission(Uri arg0, int arg1) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void sendBroadcast(Intent arg0) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void sendBroadcast(Intent arg0, String arg1) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent arg0, String arg1) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent arg0, String arg1,
+            BroadcastReceiver arg2, Handler arg3, int arg4, String arg5,
+            Bundle arg6) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void sendStickyBroadcast(Intent arg0) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void sendStickyOrderedBroadcast(Intent intent,
+            BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData,
+           Bundle initialExtras) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void setTheme(int arg0) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void setWallpaper(Bitmap arg0) throws IOException {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void setWallpaper(InputStream arg0) throws IOException {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void startActivity(Intent arg0) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void startIntentSender(IntentSender intent,
+            Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
+            throws IntentSender.SendIntentException {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public boolean startInstrumentation(ComponentName arg0, String arg1,
+            Bundle arg2) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public ComponentName startService(Intent arg0) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public boolean stopService(Intent arg0) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public void unbindService(ServiceConnection arg0) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void unregisterReceiver(BroadcastReceiver arg0) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public Context getApplicationContext() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void startActivities(Intent[] arg0) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public boolean isRestricted() {
+        return false;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
similarity index 72%
rename from tools/layoutlib/bridge/src/android/view/BridgeInflater.java
rename to tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
index 0910d79..5740e8b 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
@@ -14,36 +14,42 @@
  * limitations under the License.
  */
 
-package android.view;
+package com.android.layoutlib.bridge.android;
 
-import com.android.layoutlib.api.IProjectCallback;
-import com.android.layoutlib.api.IResourceValue;
+import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.MergeCookie;
+import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.BridgeConstants;
-import com.android.layoutlib.bridge.BridgeContext;
-import com.android.layoutlib.bridge.BridgeXmlBlockParser;
+import com.android.resources.ResourceType;
+import com.android.util.Pair;
 
 import org.kxml2.io.KXmlParser;
 import org.xmlpull.v1.XmlPullParser;
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.InflateException;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
 
 import java.io.File;
 import java.io.FileReader;
 
 /**
- * Custom implementation of {@link LayoutInflater} to handle custom views. 
+ * Custom implementation of {@link LayoutInflater} to handle custom views.
  */
 public final class BridgeInflater extends LayoutInflater {
-    
+
     private final IProjectCallback mProjectCallback;
+    private boolean mIsInMerge = false;
 
     /**
      * List of class prefixes which are tried first by default.
      * <p/>
      * This should match the list in com.android.internal.policy.impl.PhoneLayoutInflater.
-     */ 
+     */
     private static final String[] sClassPrefixList = {
         "android.widget.",
         "android.webkit."
@@ -53,10 +59,10 @@
         super(original, newContext);
         mProjectCallback = null;
     }
-    
+
     /**
      * Instantiate a new BridgeInflater with an {@link IProjectCallback} object.
-     * 
+     *
      * @param context The Android application context.
      * @param projectCallback the {@link IProjectCallback} object.
      */
@@ -82,7 +88,7 @@
                     // Ignore. We'll try again using the base class below.
                 }
             }
-    
+
             // Next try using the parent loader. This will most likely only work for
             // fully-qualified class names.
             try {
@@ -92,7 +98,7 @@
             } catch (ClassNotFoundException e) {
                 // Ignore. We'll try again using the custom view loader below.
             }
-    
+
             // Finally try again using the custom view loader
             try {
                 if (view == null) {
@@ -109,17 +115,17 @@
             ClassNotFoundException exception = new ClassNotFoundException("onCreateView", e);
             throw exception;
         }
-        
+
         setupViewInContext(view, attrs);
-        
+
         return view;
     }
-    
+
     @Override
-    public View createViewFromTag(String name, AttributeSet attrs) {
+    public View createViewFromTag(View parent, String name, AttributeSet attrs) {
         View view = null;
         try {
-            view = super.createViewFromTag(name, attrs);
+            view = super.createViewFromTag(parent, name, attrs);
         } catch (InflateException e) {
             // try to load the class from using the custom view loader
             try {
@@ -128,7 +134,7 @@
                 // Wrap the real exception in an InflateException so that the calling
                 // method can deal with it.
                 InflateException exception = new InflateException();
-                if (e2.getClass().equals(ClassNotFoundException.class) == false) { 
+                if (e2.getClass().equals(ClassNotFoundException.class) == false) {
                     exception.initCause(e2);
                 } else {
                     exception.initCause(e);
@@ -136,30 +142,30 @@
                 throw exception;
             }
         }
-        
+
         setupViewInContext(view, attrs);
-        
+
         return view;
     }
-    
+
     @Override
     public View inflate(int resource, ViewGroup root) {
         Context context = getContext();
         if (context instanceof BridgeContext) {
             BridgeContext bridgeContext = (BridgeContext)context;
-            
-            IResourceValue value = null;
 
-            String[] layoutInfo = Bridge.resolveResourceValue(resource);
+            ResourceValue value = null;
+
+            Pair<ResourceType, String> layoutInfo = Bridge.resolveResourceId(resource);
             if (layoutInfo != null) {
-                value = bridgeContext.getFrameworkResource(BridgeConstants.RES_LAYOUT,
-                        layoutInfo[0]);
+                value = bridgeContext.getRenderResources().getFrameworkResource(
+                        ResourceType.LAYOUT, layoutInfo.getSecond());
             } else {
-                layoutInfo = mProjectCallback.resolveResourceValue(resource);
-                
+                layoutInfo = mProjectCallback.resolveResourceId(resource);
+
                 if (layoutInfo != null) {
-                    value = bridgeContext.getProjectResource(BridgeConstants.RES_LAYOUT,
-                            layoutInfo[0]);
+                    value = bridgeContext.getRenderResources().getProjectResource(
+                            ResourceType.LAYOUT, layoutInfo.getSecond());
                 }
             }
 
@@ -170,21 +176,23 @@
                         KXmlParser parser = new KXmlParser();
                         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
                         parser.setInput(new FileReader(f));
-                        
+
                         BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
                                 parser, bridgeContext, false);
-                        
+
                         return inflate(bridgeParser, root);
                     } catch (Exception e) {
-                        bridgeContext.getLogger().error(e);
-                        // return null below.
+                        Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
+                                "Failed to parse file " + f.getAbsolutePath(), e, null /*data*/);
+
+                        return null;
                     }
                 }
             }
         }
         return null;
     }
-    
+
     private View loadCustomView(String name, AttributeSet attrs) throws ClassNotFoundException,
             Exception{
         if (mProjectCallback != null) {
@@ -192,12 +200,12 @@
             if (name.equals("view")) {
                 name = attrs.getAttributeValue(null, "class");
             }
-            
+
             mConstructorArgs[1] = attrs;
 
             Object customView = mProjectCallback.loadView(name, mConstructorSignature,
                     mConstructorArgs);
-            
+
             if (customView instanceof View) {
                 return (View)customView;
             }
@@ -205,21 +213,43 @@
 
         return null;
     }
-    
-    
-    
+
     private void setupViewInContext(View view, AttributeSet attrs) {
         if (getContext() instanceof BridgeContext) {
             BridgeContext bc = (BridgeContext) getContext();
             if (attrs instanceof BridgeXmlBlockParser) {
-                Object viewKey = ((BridgeXmlBlockParser) attrs).getViewKey();
+                BridgeXmlBlockParser parser = (BridgeXmlBlockParser) attrs;
+
+                // get the view key
+                Object viewKey = parser.getViewCookie();
+
+                // if there's no view key and the depth is 1 (ie this is the first tag), or 2
+                // (this is first item in included merge layout)
+                // look for a previous parser in the context, and check if this one has a viewkey.
+                int testDepth = mIsInMerge ? 2 : 1;
+                if (viewKey == null && parser.getDepth() == testDepth) {
+                    BridgeXmlBlockParser previousParser = bc.getPreviousParser();
+                    if (previousParser != null) {
+                        viewKey = previousParser.getViewCookie();
+                    }
+                }
+
                 if (viewKey != null) {
+                    if (testDepth == 2) {
+                        // include-merge case
+                        viewKey = new MergeCookie(viewKey);
+                    }
+
                     bc.addViewKey(view, viewKey);
                 }
             }
         }
     }
 
+    public void setIsInMerge(boolean isInMerge) {
+        mIsInMerge = isInMerge;
+    }
+
     @Override
     public LayoutInflater cloneInContext(Context newContext) {
         return new BridgeInflater(this, newContext);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeLayoutParamsMapAttributes.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeLayoutParamsMapAttributes.java
new file mode 100644
index 0000000..d208408
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeLayoutParamsMapAttributes.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2010 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.android;
+
+import com.android.layoutlib.bridge.BridgeConstants;
+
+import android.util.AttributeSet;
+
+import java.util.Map;
+
+/**
+ * An implementation of the {@link AttributeSet} interface on top of a map of attribute in the form
+ * of (name, value).
+ *
+ * This is meant to be called only from {@link BridgeContext#obtainStyledAttributes(AttributeSet, int[], int, int)}
+ * in the case of LayoutParams and therefore isn't a full implementation.
+ */
+public class BridgeLayoutParamsMapAttributes implements AttributeSet {
+
+    private final Map<String, String> mAttributes;
+
+    public BridgeLayoutParamsMapAttributes(Map<String, String> attributes) {
+        mAttributes = attributes;
+    }
+
+    public String getAttributeValue(String namespace, String name) {
+        if (BridgeConstants.NS_RESOURCES.equals(namespace)) {
+            return mAttributes.get(name);
+        }
+
+        return null;
+    }
+
+    // ---- the following methods are not called from
+    // BridgeContext#obtainStyledAttributes(AttributeSet, int[], int, int)
+    // Should they ever be called, we'll just implement them on a need basis.
+
+    public int getAttributeCount() {
+        throw new UnsupportedOperationException();
+    }
+
+    public String getAttributeName(int index) {
+        throw new UnsupportedOperationException();
+    }
+
+    public String getAttributeValue(int index) {
+        throw new UnsupportedOperationException();
+    }
+
+    public String getPositionDescription() {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getAttributeNameResource(int index) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getAttributeListValue(String namespace, String attribute,
+            String[] options, int defaultValue) {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean getAttributeBooleanValue(String namespace, String attribute,
+            boolean defaultValue) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getAttributeResourceValue(String namespace, String attribute,
+            int defaultValue) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getAttributeIntValue(String namespace, String attribute,
+            int defaultValue) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getAttributeUnsignedIntValue(String namespace, String attribute,
+            int defaultValue) {
+        throw new UnsupportedOperationException();
+    }
+
+    public float getAttributeFloatValue(String namespace, String attribute,
+            float defaultValue) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getAttributeListValue(int index,
+            String[] options, int defaultValue) {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getAttributeResourceValue(int index, int defaultValue) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getAttributeIntValue(int index, int defaultValue) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getAttributeUnsignedIntValue(int index, int defaultValue) {
+        throw new UnsupportedOperationException();
+    }
+
+    public float getAttributeFloatValue(int index, float defaultValue) {
+        throw new UnsupportedOperationException();
+    }
+
+    public String getIdAttribute() {
+        throw new UnsupportedOperationException();
+    }
+
+    public String getClassAttribute() {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getIdAttributeResourceValue(int defaultValue) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getStyleAttribute() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
similarity index 67%
rename from tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java
rename to tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
index 6358abb..5e5aeb1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
@@ -14,10 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.layoutlib.bridge;
+package com.android.layoutlib.bridge.android;
 
-import com.android.layoutlib.api.IProjectCallback;
-import com.android.layoutlib.api.IResourceValue;
+import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.BridgeConstants;
+import com.android.layoutlib.bridge.impl.ResourceHelper;
+import com.android.ninepatch.NinePatch;
+import com.android.resources.ResourceType;
+import com.android.util.Pair;
 
 import org.kxml2.io.KXmlParser;
 import org.xmlpull.v1.XmlPullParser;
@@ -52,6 +59,35 @@
     private 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/>
@@ -64,21 +100,18 @@
             DisplayMetrics metrics,
             Configuration config,
             IProjectCallback projectCallback) {
-        if (!(Resources.mSystem instanceof BridgeResources)) {
-            Resources.mSystem = new BridgeResources(context,
-                    assets,
-                    metrics,
-                    config,
-                    projectCallback);
-        }
-        return Resources.mSystem;
+        return Resources.mSystem = new BridgeResources(context,
+                assets,
+                metrics,
+                config,
+                projectCallback);
     }
 
     /**
-     * Clears the static {@link Resources#mSystem} to make sure we don't leave objects
+     * Disposes the static {@link Resources#mSystem} to make sure we don't leave objects
      * around that would prevent us from unloading the library.
      */
-    /*package*/ static void clearSystem() {
+    /*package*/ static void disposeSystem() {
         if (Resources.mSystem instanceof BridgeResources) {
             ((BridgeResources)(Resources.mSystem)).mContext = null;
             ((BridgeResources)(Resources.mSystem)).mProjectCallback = null;
@@ -97,22 +130,24 @@
         return new BridgeTypedArray(this, mContext, numEntries, platformFile);
     }
 
-    private IResourceValue getResourceValue(int id, boolean[] platformResFlag_out) {
+    private ResourceValue getResourceValue(int id, boolean[] platformResFlag_out) {
         // first get the String related to this id in the framework
-        String[] resourceInfo = Bridge.resolveResourceValue(id);
+        Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
 
         if (resourceInfo != null) {
             platformResFlag_out[0] = true;
-            return mContext.getFrameworkResource(resourceInfo[1], resourceInfo[0]);
+            return mContext.getRenderResources().getFrameworkResource(
+                    resourceInfo.getFirst(), resourceInfo.getSecond());
         }
 
         // didn't find a match in the framework? look in the project.
         if (mProjectCallback != null) {
-            resourceInfo = mProjectCallback.resolveResourceValue(id);
+            resourceInfo = mProjectCallback.resolveResourceId(id);
 
             if (resourceInfo != null) {
                 platformResFlag_out[0] = false;
-                return mContext.getProjectResource(resourceInfo[1], resourceInfo[0]);
+                return mContext.getRenderResources().getProjectResource(
+                        resourceInfo.getFirst(), resourceInfo.getSecond());
             }
         }
 
@@ -121,10 +156,10 @@
 
     @Override
     public Drawable getDrawable(int id) throws NotFoundException {
-        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
 
         if (value != null) {
-            return ResourceHelper.getDrawable(value, mContext, value.isFramework());
+            return ResourceHelper.getDrawable(value, mContext);
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
@@ -136,12 +171,14 @@
 
     @Override
     public int getColor(int id) throws NotFoundException {
-        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
 
         if (value != null) {
             try {
                 return ResourceHelper.getColor(value.getValue());
             } catch (NumberFormatException e) {
+                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e,
+                        null /*data*/);
                 return 0;
             }
         }
@@ -155,14 +192,12 @@
 
     @Override
     public ColorStateList getColorStateList(int id) throws NotFoundException {
-        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        ResourceValue resValue = getResourceValue(id, mPlatformResourceFlag);
 
-        if (value != null) {
-            try {
-                int color = ResourceHelper.getColor(value.getValue());
-                return ColorStateList.valueOf(color);
-            } catch (NumberFormatException e) {
-                return null;
+        if (resValue != null) {
+            ColorStateList stateList = ResourceHelper.getColorStateList(resValue, mContext);
+            if (stateList != null) {
+                return stateList;
             }
         }
 
@@ -175,7 +210,7 @@
 
     @Override
     public CharSequence getText(int id) throws NotFoundException {
-        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
 
         if (value != null) {
             return value.getValue();
@@ -190,27 +225,76 @@
 
     @Override
     public XmlResourceParser getLayout(int id) throws NotFoundException {
-        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
 
         if (value != null) {
-            File xml = new File(value.getValue());
-            if (xml.isFile()) {
-                // we need to create a pull parser around the layout XML file, and then
-                // give that to our XmlBlockParser
-                try {
-                    KXmlParser parser = new KXmlParser();
+            XmlPullParser parser = null;
+
+            try {
+                // check if the current parser can provide us with a custom parser.
+                BridgeXmlBlockParser currentParser = mContext.getCurrentParser();
+                if (currentParser != null) {
+                    parser = currentParser.getParser(value.getName());
+                }
+
+                // create a new one manually if needed.
+                if (parser == null) {
+                    File xml = new File(value.getValue());
+                    if (xml.isFile()) {
+                        // we need to create a pull parser around the layout XML file, and then
+                        // give that to our XmlBlockParser
+                        parser = new KXmlParser();
+                        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+                        parser.setInput(new FileReader(xml));
+                    }
+                }
+
+                if (parser != null) {
+                    return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+                }
+            } catch (XmlPullParserException e) {
+                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                        "Failed to configure parser for " + value.getValue(), e, null /*data*/);
+                // we'll return null below.
+            } catch (FileNotFoundException e) {
+                // this shouldn't happen since we check above.
+            }
+
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @Override
+    public XmlResourceParser getAnimation(int id) throws NotFoundException {
+        ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+
+        if (value != null) {
+            XmlPullParser parser = null;
+
+            try {
+                File xml = new File(value.getValue());
+                if (xml.isFile()) {
+                    // we need to create a pull parser around the layout XML file, and then
+                    // give that to our XmlBlockParser
+                    parser = new KXmlParser();
                     parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
                     parser.setInput(new FileReader(xml));
 
                     return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
-                } catch (XmlPullParserException e) {
-                    mContext.getLogger().error(e);
-
-                    // we'll return null below.
-                } catch (FileNotFoundException e) {
-                    // this shouldn't happen since we check above.
                 }
+            } catch (XmlPullParserException e) {
+                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                        "Failed to configure parser for " + value.getValue(), e, null /*data*/);
+                // we'll return null below.
+            } catch (FileNotFoundException e) {
+                // this shouldn't happen since we check above.
             }
+
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
@@ -233,7 +317,7 @@
 
     @Override
     public float getDimension(int id) throws NotFoundException {
-        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
 
         if (value != null) {
             String v = value.getValue();
@@ -262,7 +346,7 @@
 
     @Override
     public int getDimensionPixelOffset(int id) throws NotFoundException {
-        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
 
         if (value != null) {
             String v = value.getValue();
@@ -284,7 +368,7 @@
 
     @Override
     public int getDimensionPixelSize(int id) throws NotFoundException {
-        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
 
         if (value != null) {
             String v = value.getValue();
@@ -306,7 +390,7 @@
 
     @Override
     public int getInteger(int id) throws NotFoundException {
-        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
 
         if (value != null && value.getValue() != null) {
             String v = value.getValue();
@@ -361,7 +445,7 @@
 
     @Override
     public String getString(int id) throws NotFoundException {
-        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
 
         if (value != null && value.getValue() != null) {
             return value.getValue();
@@ -377,7 +461,7 @@
     @Override
     public void getValue(int id, TypedValue outValue, boolean resolveRefs)
             throws NotFoundException {
-        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
 
         if (value != null) {
             String v = value.getValue();
@@ -406,7 +490,7 @@
 
     @Override
     public XmlResourceParser getXml(int id) throws NotFoundException {
-        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
 
         if (value != null) {
             String v = value.getValue();
@@ -470,16 +554,22 @@
 
     @Override
     public InputStream openRawResource(int id) throws NotFoundException {
-        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
 
         if (value != null) {
-            String v = value.getValue();
+            String path = value.getValue();
 
-            if (v != null) {
+            if (path != null) {
                 // check this is a file
-                File f = new File(value.getValue());
+                File f = new File(path);
                 if (f.isFile()) {
                     try {
+                        // if it's a nine-patch return a custom input stream so that
+                        // other methods (mainly bitmap factory) can detect it's a 9-patch
+                        // and actually load it as a 9-patch instead of a normal bitmap
+                        if (path.toLowerCase().endsWith(NinePatch.EXTENSION_9PATCH)) {
+                            return new NinePatchInputStream(f);
+                        }
                         return new FileInputStream(f);
                     } catch (FileNotFoundException e) {
                         NotFoundException newE = new NotFoundException();
@@ -501,9 +591,17 @@
     public InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
         getValue(id, value, true);
 
-        File f = new File(value.string.toString());
+        String path = value.string.toString();
+
+        File f = new File(path);
         if (f.isFile()) {
             try {
+                // if it's a nine-patch return a custom input stream so that
+                // other methods (mainly bitmap factory) can detect it's a 9-patch
+                // and actually load it as a 9-patch instead of a normal bitmap
+                if (path.toLowerCase().endsWith(NinePatch.EXTENSION_9PATCH)) {
+                    return new NinePatchInputStream(f);
+                }
                 return new FileInputStream(f);
             } catch (FileNotFoundException e) {
                 NotFoundException exception = new NotFoundException();
@@ -527,18 +625,18 @@
      */
     private void throwException(int id) throws NotFoundException {
         // first get the String related to this id in the framework
-        String[] resourceInfo = Bridge.resolveResourceValue(id);
+        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 && mProjectCallback != null) {
-            resourceInfo = mProjectCallback.resolveResourceValue(id);
+            resourceInfo = mProjectCallback.resolveResourceId(id);
         }
 
         String message = null;
         if (resourceInfo != null) {
             message = String.format(
                     "Could not find %1$s resource matching value 0x%2$X (resolved name: %3$s) in current configuration.",
-                    resourceInfo[1], id, resourceInfo[0]);
+                    resourceInfo.getFirst(), id, resourceInfo.getSecond());
         } else {
             message = String.format(
                     "Could not resolve resource value: 0x%1$X.", id);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
similarity index 71%
rename from tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java
rename to tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
index 70c5bd7..c1d7600 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
@@ -14,14 +14,21 @@
  * limitations under the License.
  */
 
-package com.android.layoutlib.bridge;
+package com.android.layoutlib.bridge.android;
 
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.StyleResourceValue;
 import com.android.internal.util.XmlUtils;
-import com.android.layoutlib.api.IResourceValue;
-import com.android.layoutlib.api.IStyleResourceValue;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.BridgeConstants;
+import com.android.layoutlib.bridge.impl.ResourceHelper;
+import com.android.resources.ResourceType;
 
 import org.kxml2.io.KXmlParser;
 import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -33,39 +40,38 @@
 
 import java.io.File;
 import java.io.FileReader;
+import java.util.Arrays;
 import java.util.Map;
 
 /**
- * TODO: describe.
+ * Custom implementation of TypedArray to handle non compiled resources.
  */
 public final class BridgeTypedArray extends TypedArray {
 
-    @SuppressWarnings("hiding")
-    private BridgeResources mResources;
+    private BridgeResources mBridgeResources;
     private BridgeContext mContext;
-    @SuppressWarnings("hiding")
-    private IResourceValue[] mData;
+    private ResourceValue[] mResourceData;
     private String[] mNames;
     private final boolean mPlatformFile;
 
     public BridgeTypedArray(BridgeResources resources, BridgeContext context, int len,
             boolean platformFile) {
         super(null, null, null, 0);
-        mResources = resources;
+        mBridgeResources = resources;
         mContext = context;
         mPlatformFile = platformFile;
-        mData = new IResourceValue[len];
+        mResourceData = new ResourceValue[len];
         mNames = new String[len];
     }
 
     /** A bridge-specific method that sets a value in the type array */
-    public void bridgeSetValue(int index, String name, IResourceValue value) {
-        mData[index] = value;
+    public void bridgeSetValue(int index, String name, ResourceValue value) {
+        mResourceData[index] = value;
         mNames[index] = name;
     }
 
     /**
-     * Seals the array after all calls to {@link #bridgeSetValue(int, String, IResourceValue)} have
+     * Seals the array after all calls to {@link #bridgeSetValue(int, String, ResourceValue)} have
      * been done.
      * <p/>This allows to compute the list of non default values, permitting
      * {@link #getIndexCount()} to return the proper value.
@@ -74,7 +80,7 @@
         // fills TypedArray.mIndices which is used to implement getIndexCount/getIndexAt
         // first count the array size
         int count = 0;
-        for (IResourceValue data : mData) {
+        for (ResourceValue data : mResourceData) {
             if (data != null) {
                 count++;
             }
@@ -86,8 +92,8 @@
 
         // fill the array with the indices.
         int index = 1;
-        for (int i = 0 ; i < mData.length ; i++) {
-            if (mData[i] != null) {
+        for (int i = 0 ; i < mResourceData.length ; i++) {
+            if (mResourceData[i] != null) {
                 mIndices[index++] = i;
             }
         }
@@ -98,7 +104,7 @@
      */
     @Override
     public int length() {
-        return mData.length;
+        return mResourceData.length;
     }
 
     /**
@@ -106,7 +112,7 @@
      */
     @Override
     public Resources getResources() {
-        return mResources;
+        return mBridgeResources;
     }
 
     /**
@@ -119,9 +125,9 @@
      */
     @Override
     public CharSequence getText(int index) {
-        if (mData[index] != null) {
+        if (mResourceData[index] != null) {
             // FIXME: handle styled strings!
-            return mData[index].getValue();
+            return mResourceData[index].getValue();
         }
 
         return null;
@@ -137,8 +143,8 @@
      */
     @Override
     public String getString(int index) {
-        if (mData[index] != null) {
-            return mData[index].getValue();
+        if (mResourceData[index] != null) {
+            return mResourceData[index].getValue();
         }
 
         return null;
@@ -154,11 +160,11 @@
      */
     @Override
     public boolean getBoolean(int index, boolean defValue) {
-        if (mData[index] == null) {
+        if (mResourceData[index] == null) {
             return defValue;
         }
 
-        String s = mData[index].getValue();
+        String s = mResourceData[index].getValue();
         if (s != null) {
             return XmlUtils.convertValueToBoolean(s, defValue);
         }
@@ -176,11 +182,15 @@
      */
     @Override
     public int getInt(int index, int defValue) {
-        if (mData[index] == null) {
+        if (mResourceData[index] == null) {
             return defValue;
         }
 
-        String s = mData[index].getValue();
+        String s = mResourceData[index].getValue();
+
+        if (RenderResources.REFERENCE_NULL.equals(s)) {
+            return defValue;
+        }
 
         try {
             return (s == null) ? defValue : XmlUtils.convertValueToInt(s, defValue);
@@ -204,9 +214,10 @@
                 if (i != null) {
                     result |= i.intValue();
                 } else {
-                    mContext.getLogger().warning(String.format(
-                            "Unknown constant \"%s\" in attribute \"%2$s\"",
-                            keyword, mNames[index]));
+                    Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+                            String.format(
+                                "\"%s\" in attribute \"%2$s\" is not a valid value",
+                                keyword, mNames[index]), null /*data*/);
                 }
             }
             return result;
@@ -224,19 +235,20 @@
      */
     @Override
     public float getFloat(int index, float defValue) {
-        if (mData[index] == null) {
+        if (mResourceData[index] == null) {
             return defValue;
         }
 
-        String s = mData[index].getValue();
+        String s = mResourceData[index].getValue();
 
         if (s != null) {
             try {
                 return Float.parseFloat(s);
             } catch (NumberFormatException e) {
-                mContext.getLogger().warning(String.format(
-                        "Unable to convert \"%s\" into a float in attribute \"%2$s\"",
-                        s, mNames[index]));
+                Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+                        String.format(
+                            "\"%s\" in attribute \"%2$s\" cannot be converted to float.",
+                            s, mNames[index]), null /*data*/);
 
                 // we'll return the default value below.
             }
@@ -258,19 +270,14 @@
      */
     @Override
     public int getColor(int index, int defValue) {
-        if (mData[index] == null) {
+        if (mResourceData[index] == null) {
             return defValue;
         }
 
-        String s = mData[index].getValue();
-        try {
-            return ResourceHelper.getColor(s);
-        } catch (NumberFormatException e) {
-            mContext.getLogger().warning(String.format(
-                    "Unable to convert \"%s\" into a color in attribute \"%2$s\"",
-                    s, mNames[index]));
-
-            // we'll return the default value below.
+        ColorStateList colorStateList = ResourceHelper.getColorStateList(
+                mResourceData[index], mContext);
+        if (colorStateList != null) {
+            return colorStateList.getDefaultColor();
         }
 
         return defValue;
@@ -287,50 +294,57 @@
      */
     @Override
     public ColorStateList getColorStateList(int index) {
-        if (mData[index] == null) {
+        if (mResourceData[index] == null) {
             return null;
         }
 
-        String value = mData[index].getValue();
+        ResourceValue resValue = mResourceData[index];
+        String value = resValue.getValue();
 
         if (value == null) {
             return null;
         }
 
+        if (RenderResources.REFERENCE_NULL.equals(value)) {
+            return null;
+        }
+
+        // let the framework inflate the ColorStateList from the XML file.
+        File f = new File(value);
+        if (f.isFile()) {
+            try {
+                KXmlParser parser = new KXmlParser();
+                parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+                parser.setInput(new FileReader(f));
+
+                BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
+                        parser, mContext, resValue.isFramework());
+                try {
+                    return ColorStateList.createFromXml(mContext.getResources(), blockParser);
+                } finally {
+                    blockParser.ensurePopped();
+                }
+            } catch (XmlPullParserException e) {
+                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                        "Failed to configure parser for " + value, e, null /*data*/);
+                return null;
+            } catch (Exception e) {
+                // this is an error and not warning since the file existence is checked before
+                // attempting to parse it.
+                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
+                        "Failed to parse file " + value, e, null /*data*/);
+
+                return null;
+            }
+        }
+
         try {
             int color = ResourceHelper.getColor(value);
             return ColorStateList.valueOf(color);
         } catch (NumberFormatException e) {
-            // if it's not a color value, we'll attempt to read the xml based color below.
+            Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e, null /*data*/);
         }
 
-        // let the framework inflate the ColorStateList from the XML file.
-        try {
-            File f = new File(value);
-            if (f.isFile()) {
-                KXmlParser parser = new KXmlParser();
-                parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                parser.setInput(new FileReader(f));
-
-                ColorStateList colorStateList = ColorStateList.createFromXml(
-                        mContext.getResources(),
-                        // FIXME: we need to know if this resource is platform or not
-                        new BridgeXmlBlockParser(parser, mContext, false));
-                return colorStateList;
-            }
-        } catch (Exception e) {
-            // this is an error and not warning since the file existence is checked before
-            // attempting to parse it.
-            mContext.getLogger().error(e);
-
-            // return null below.
-        }
-
-        // looks like were unable to resolve the color value.
-        mContext.getLogger().warning(String.format(
-                "Unable to resolve color value \"%1$s\" in attribute \"%2$s\"",
-                value, mNames[index]));
-
         return null;
     }
 
@@ -345,19 +359,20 @@
      */
     @Override
     public int getInteger(int index, int defValue) {
-        if (mData[index] == null) {
+        if (mResourceData[index] == null) {
             return defValue;
         }
 
-        String s = mData[index].getValue();
+        String s = mResourceData[index].getValue();
 
         if (s != null) {
             try {
                 return Integer.parseInt(s);
             } catch (NumberFormatException e) {
-                mContext.getLogger().warning(String.format(
-                        "Unable to convert \"%s\" into a integer in attribute \"%2$s\"",
-                        s, mNames[index]));
+                Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+                        String.format(
+                            "\"%s\" in attribute \"%2$s\" cannont be converted to an integer.",
+                            s, mNames[index]), null /*data*/);
 
                 // The default value is returned below.
             }
@@ -384,11 +399,11 @@
      */
     @Override
     public float getDimension(int index, float defValue) {
-        if (mData[index] == null) {
+        if (mResourceData[index] == null) {
             return defValue;
         }
 
-        String s = mData[index].getValue();
+        String s = mResourceData[index].getValue();
 
         if (s == null) {
             return defValue;
@@ -399,14 +414,19 @@
             return LayoutParams.WRAP_CONTENT;
         }
 
+        if (RenderResources.REFERENCE_NULL.equals(s)) {
+            return defValue;
+        }
+
         if (ResourceHelper.stringToFloat(s, mValue)) {
-            return mValue.getDimension(mResources.mMetrics);
+            return mValue.getDimension(mBridgeResources.mMetrics);
         }
 
         // looks like we were unable to resolve the dimension value
-        mContext.getLogger().warning(String.format(
-                "Unable to resolve dimension value \"%1$s\" in attribute \"%2$s\"",
-                s, mNames[index]));
+        Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+                String.format(
+                    "\"%1$s\" in attribute \"%2$s\" is not a valid format.",
+                    s, mNames[index]), null /*data*/);
 
         return defValue;
     }
@@ -453,11 +473,11 @@
      */
     @Override
     public int getDimensionPixelSize(int index, int defValue) {
-        if (mData[index] == null) {
+        if (mResourceData[index] == null) {
             return defValue;
         }
 
-        String s = mData[index].getValue();
+        String s = mResourceData[index].getValue();
 
         if (s == null) {
             return defValue;
@@ -468,6 +488,10 @@
             return LayoutParams.WRAP_CONTENT;
         }
 
+        if (RenderResources.REFERENCE_NULL.equals(s)) {
+            return defValue;
+        }
+
         // FIXME huh?
 
         float f = getDimension(index, defValue);
@@ -476,8 +500,11 @@
         if (f == 0) return 0;
         if (f > 0) return 1;
 
-        throw new UnsupportedOperationException("Can't convert to dimension: " +
-                Integer.toString(index));
+        Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
+                "Can't convert to dimension: " + Integer.toString(index),
+                null, null /*data*/);
+
+        return defValue;
     }
 
     /**
@@ -519,11 +546,11 @@
      */
     @Override
     public float getFraction(int index, int base, int pbase, float defValue) {
-        if (mData[index] == null) {
+        if (mResourceData[index] == null) {
             return defValue;
         }
 
-        String value = mData[index].getValue();
+        String value = mResourceData[index].getValue();
         if (value == null) {
             return defValue;
         }
@@ -533,9 +560,10 @@
         }
 
         // looks like we were unable to resolve the fraction value
-        mContext.getLogger().warning(String.format(
-                "Unable to resolve fraction value \"%1$s\" in attribute \"%2$s\"",
-                value, mNames[index]));
+        Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+                String.format(
+                    "\"%1$s\" in attribute \"%2$s\" cannont be converted to a fraction.",
+                    value, mNames[index]), null /*data*/);
 
         return defValue;
     }
@@ -556,8 +584,8 @@
      */
     @Override
     public int getResourceId(int index, int defValue) {
-        // get the IResource for this index
-        IResourceValue resValue = mData[index];
+        // get the Resource for this index
+        ResourceValue resValue = mResourceData[index];
 
         // no data, return the default value.
         if (resValue == null) {
@@ -565,24 +593,30 @@
         }
 
         // check if this is a style resource
-        if (resValue instanceof IStyleResourceValue) {
+        if (resValue instanceof StyleResourceValue) {
             // get the id that will represent this style.
-            return mContext.getDynamicIdByStyle((IStyleResourceValue)resValue);
+            return mContext.getDynamicIdByStyle((StyleResourceValue)resValue);
         }
 
-        // if the attribute was a reference to an id, and not a declaration of an id (@+id), then
-        // the xml attribute value was "resolved" which leads us to a IResourceValue with
-        // getType() returning "id" and getName() returning the id name
+        if (RenderResources.REFERENCE_NULL.equals(resValue.getValue())) {
+            return defValue;
+        }
+
+        // if the attribute was a reference to a resource, and not a declaration of an id (@+id),
+        // then the xml attribute value was "resolved" which leads us to a ResourceValue with a
+        // valid getType() and getName() returning a resource name.
         // (and getValue() returning null!). We need to handle this!
-        if (resValue.getType() != null && resValue.getType().equals(BridgeConstants.RES_ID)) {
+        if (resValue.getResourceType() != null) {
             // if this is a framework id
             if (mPlatformFile || resValue.isFramework()) {
                 // look for idName in the android R classes
-                return mContext.getFrameworkIdValue(resValue.getName(), defValue);
+                return mContext.getFrameworkResourceValue(
+                        resValue.getResourceType(), resValue.getName(), defValue);
             }
 
             // look for idName in the project R class.
-            return mContext.getProjectIdValue(resValue.getName(), defValue);
+            return mContext.getProjectResourceValue(
+                    resValue.getResourceType(), resValue.getName(), defValue);
         }
 
         // else, try to get the value, and resolve it somehow.
@@ -619,29 +653,33 @@
             // if this is a framework id
             if (mPlatformFile || value.startsWith("@android") || value.startsWith("@+android")) {
                 // look for idName in the android R classes
-                return mContext.getFrameworkIdValue(idName, defValue);
+                return mContext.getFrameworkResourceValue(ResourceType.ID, idName, defValue);
             }
 
             // look for idName in the project R class.
-            return mContext.getProjectIdValue(idName, defValue);
+            return mContext.getProjectResourceValue(ResourceType.ID, idName, defValue);
         }
 
         // not a direct id valid reference? resolve it
         Integer idValue = null;
 
         if (resValue.isFramework()) {
-            idValue = Bridge.getResourceValue(resValue.getType(), resValue.getName());
+            idValue = Bridge.getResourceId(resValue.getResourceType(),
+                    resValue.getName());
         } else {
-            idValue = mContext.getProjectCallback().getResourceValue(
-                    resValue.getType(), resValue.getName());
+            idValue = mContext.getProjectCallback().getResourceId(
+                    resValue.getResourceType(), resValue.getName());
         }
 
         if (idValue != null) {
             return idValue.intValue();
         }
 
-        mContext.getLogger().warning(String.format(
-                "Unable to resolve id \"%1$s\" for attribute \"%2$s\"", value, mNames[index]));
+        Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE,
+                String.format(
+                    "Unable to resolve id \"%1$s\" for attribute \"%2$s\"", value, mNames[index]),
+                    resValue);
+
         return defValue;
     }
 
@@ -657,28 +695,17 @@
      */
     @Override
     public Drawable getDrawable(int index) {
-        if (mData[index] == null) {
+        if (mResourceData[index] == null) {
             return null;
         }
 
-        IResourceValue value = mData[index];
+        ResourceValue value = mResourceData[index];
         String stringValue = value.getValue();
-        if (stringValue == null || BridgeConstants.REFERENCE_NULL.equals(stringValue)) {
+        if (stringValue == null || RenderResources.REFERENCE_NULL.equals(stringValue)) {
             return null;
         }
 
-        Drawable d = ResourceHelper.getDrawable(value, mContext, mData[index].isFramework());
-
-        if (d != null) {
-            return d;
-        }
-
-        // looks like we were unable to resolve the drawable
-        mContext.getLogger().warning(String.format(
-                "Unable to resolve drawable \"%1$s\" in attribute \"%2$s\"", stringValue,
-                mNames[index]));
-
-        return null;
+        return ResourceHelper.getDrawable(value, mContext);
     }
 
 
@@ -694,18 +721,23 @@
      */
     @Override
     public CharSequence[] getTextArray(int index) {
-        if (mData[index] == null) {
+        if (mResourceData[index] == null) {
             return null;
         }
 
-        String value = mData[index].getValue();
+        String value = mResourceData[index].getValue();
         if (value != null) {
+            if (RenderResources.REFERENCE_NULL.equals(value)) {
+                return null;
+            }
+
             return new CharSequence[] { value };
         }
 
-        mContext.getLogger().warning(String.format(
-                String.format("Unknown value for getTextArray(%d) => %s", //DEBUG
-                index, mData[index].getName())));
+        Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+                String.format(
+                    String.format("Unknown value for getTextArray(%d) => %s", //DEBUG
+                    index, mResourceData[index].getName())), null /*data*/);
 
         return null;
     }
@@ -721,11 +753,11 @@
      */
     @Override
     public boolean getValue(int index, TypedValue outValue) {
-        if (mData[index] == null) {
+        if (mResourceData[index] == null) {
             return false;
         }
 
-        String s = mData[index].getValue();
+        String s = mResourceData[index].getValue();
 
         return ResourceHelper.stringToFloat(s, outValue);
     }
@@ -739,7 +771,7 @@
      */
     @Override
     public boolean hasValue(int index) {
-        return mData[index] != null;
+        return mResourceData[index] != null;
     }
 
     /**
@@ -786,6 +818,6 @@
 
     @Override
     public String toString() {
-        return mData.toString();
+        return Arrays.toString(mResourceData);
     }
  }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
new file mode 100644
index 0000000..a8da377
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 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.android;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.view.DragEvent;
+import android.view.IWindow;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View.AttachInfo;
+
+/**
+ * Implementation of {@link IWindow} to pass to the {@link AttachInfo}.
+ */
+public final class BridgeWindow implements IWindow {
+
+    public void dispatchAppVisibility(boolean arg0) throws RemoteException {
+        // pass for now.
+    }
+
+    public void dispatchGetNewSurface() throws RemoteException {
+        // pass for now.
+    }
+
+    public void dispatchKey(KeyEvent arg0) throws RemoteException {
+        // pass for now.
+    }
+
+    public void dispatchPointer(MotionEvent arg0, long arg1, boolean arg2) throws RemoteException {
+        // pass for now.
+    }
+
+    public void dispatchTrackball(MotionEvent arg0, long arg1, boolean arg2)
+    throws RemoteException {
+        // pass for now.
+    }
+
+    public void executeCommand(String arg0, String arg1, ParcelFileDescriptor arg2)
+            throws RemoteException {
+        // pass for now.
+    }
+
+    public void resized(int arg0, int arg1, Rect arg2, Rect arg3, boolean arg4, Configuration arg5)
+            throws RemoteException {
+        // pass for now.
+    }
+
+    public void windowFocusChanged(boolean arg0, boolean arg1) throws RemoteException {
+        // pass for now.
+    }
+
+    public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
+            boolean sync) {
+        // pass for now.
+    }
+
+    public void dispatchWallpaperCommand(String action, int x, int y,
+            int z, Bundle extras, boolean sync) {
+        // pass for now.
+    }
+
+    public void closeSystemDialogs(String reason) {
+        // pass for now.
+    }
+
+    public void dispatchDragEvent(DragEvent event) {
+        // pass for now.
+    }
+
+    public void dispatchSystemUiVisibilityChanged(int visibility) {
+        // pass for now.
+    }
+
+    public IBinder asBinder() {
+        // pass for now.
+        return null;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
new file mode 100644
index 0000000..8422d48
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2010 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.android;
+
+import android.content.ClipData;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.IWindow;
+import android.view.IWindowSession;
+import android.view.InputChannel;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.SurfaceView;
+import android.view.WindowManager.LayoutParams;
+
+/**
+ * Implementation of {@link IWindowSession} so that mSession is not null in
+ * the {@link SurfaceView}.
+ */
+public final class BridgeWindowSession implements IWindowSession {
+
+    public int add(IWindow arg0, LayoutParams arg1, int arg2, Rect arg3,
+            InputChannel outInputchannel)
+            throws RemoteException {
+        // pass for now.
+        return 0;
+    }
+
+    public int addWithoutInputChannel(IWindow arg0, LayoutParams arg1, int arg2, Rect arg3)
+            throws RemoteException {
+        // pass for now.
+        return 0;
+    }
+
+    public void finishDrawing(IWindow arg0) throws RemoteException {
+        // pass for now.
+    }
+
+    public void finishKey(IWindow arg0) throws RemoteException {
+        // pass for now.
+    }
+
+    public boolean getInTouchMode() throws RemoteException {
+        // pass for now.
+        return false;
+    }
+
+    public boolean performHapticFeedback(IWindow window, int effectId, boolean always) {
+        // pass for now.
+        return false;
+    }
+
+    public MotionEvent getPendingPointerMove(IWindow arg0) throws RemoteException {
+        // pass for now.
+        return null;
+    }
+
+    public MotionEvent getPendingTrackballMove(IWindow arg0) throws RemoteException {
+        // pass for now.
+        return null;
+    }
+
+    public int relayout(IWindow arg0, LayoutParams arg1, int arg2, int arg3, int arg4,
+            boolean arg4_5, Rect arg5, Rect arg6, Rect arg7, Configuration arg7b, Surface arg8)
+            throws RemoteException {
+        // pass for now.
+        return 0;
+    }
+
+    public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
+        // pass for now.
+    }
+
+    public void remove(IWindow arg0) throws RemoteException {
+        // pass for now.
+    }
+
+    public void setInTouchMode(boolean arg0) throws RemoteException {
+        // pass for now.
+    }
+
+    public void setTransparentRegion(IWindow arg0, Region arg1) throws RemoteException {
+        // pass for now.
+    }
+
+    public void setInsets(IWindow window, int touchable, Rect contentInsets,
+            Rect visibleInsets, Region touchableRegion) {
+        // pass for now.
+    }
+
+    public IBinder prepareDrag(IWindow window, int flags,
+            int thumbnailWidth, int thumbnailHeight, Surface outSurface)
+            throws RemoteException {
+        // pass for now
+        return null;
+    }
+
+    public boolean performDrag(IWindow window, IBinder dragToken,
+            float touchX, float touchY, float thumbCenterX, float thumbCenterY,
+            ClipData data)
+            throws RemoteException {
+        // pass for now
+        return false;
+    }
+
+    public void reportDropResult(IWindow window, boolean consumed) throws RemoteException {
+        // pass for now
+    }
+
+    public void dragRecipientEntered(IWindow window) throws RemoteException {
+        // pass for now
+    }
+
+    public void dragRecipientExited(IWindow window) throws RemoteException {
+        // pass for now
+    }
+
+    public void setWallpaperPosition(IBinder window, float x, float y,
+        float xStep, float yStep) {
+        // pass for now.
+    }
+
+    public void wallpaperOffsetsComplete(IBinder window) {
+        // pass for now.
+    }
+
+    public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
+            int z, Bundle extras, boolean sync) {
+        // pass for now.
+        return null;
+    }
+
+    public void wallpaperCommandComplete(IBinder window, Bundle result) {
+        // pass for now.
+    }
+
+    public void closeSystemDialogs(String reason) {
+        // pass for now.
+    }
+
+    public IBinder asBinder() {
+        // pass for now.
+        return null;
+    }
+
+    public IBinder prepareDrag(IWindow arg0, boolean arg1, int arg2, int arg3, Surface arg4)
+            throws RemoteException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlBlockParser.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
similarity index 90%
rename from tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlBlockParser.java
rename to tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
index d842a66..2f54ae6 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlBlockParser.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.layoutlib.bridge;
+package com.android.layoutlib.bridge.android;
 
-import com.android.layoutlib.api.IXmlPullParser;
+
+import com.android.ide.common.rendering.api.ILayoutPullParser;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -36,14 +37,15 @@
  */
 public class BridgeXmlBlockParser implements XmlResourceParser {
 
-    private XmlPullParser mParser;
-    private XmlPullAttributes mAttrib;
+    private final XmlPullParser mParser;
+    private final XmlPullAttributes mAttrib;
+    private final BridgeContext mContext;
+    private final boolean mPlatformFile;
 
     private boolean mStarted = false;
-    private boolean mDecNextDepth = false;
-    private int mDepth = 0;
     private int mEventType = START_DOCUMENT;
-    private final boolean mPlatformFile;
+
+    private boolean mPopped = true; // default to true in case it's not pushed.
 
     /**
      * Builds a {@link BridgeXmlBlockParser}.
@@ -53,25 +55,45 @@
      */
     public BridgeXmlBlockParser(XmlPullParser parser, BridgeContext context, boolean platformFile) {
         mParser = parser;
+        mContext = context;
         mPlatformFile = platformFile;
         mAttrib = new BridgeXmlPullAttributes(parser, context, mPlatformFile);
+
+        if (mContext != null) {
+            mContext.pushParser(this);
+            mPopped = false;
+        }
     }
-    
+
     public boolean isPlatformFile() {
         return mPlatformFile;
     }
 
-    public Object getViewKey() {
-        if (mParser instanceof IXmlPullParser) {
-            return ((IXmlPullParser)mParser).getViewKey();
+    public ILayoutPullParser getParser(String layoutName) {
+        if (mParser instanceof ILayoutPullParser) {
+            return ((ILayoutPullParser)mParser).getParser(layoutName);
         }
 
         return null;
     }
-    
-    
+
+    public Object getViewCookie() {
+        if (mParser instanceof ILayoutPullParser) {
+            return ((ILayoutPullParser)mParser).getViewCookie();
+        }
+
+        return null;
+    }
+
+    public void ensurePopped() {
+        if (mContext != null && mPopped == false) {
+            mContext.popParser();
+            mPopped = true;
+        }
+    }
+
     // ------- XmlResourceParser implementation
-    
+
     public void setFeature(String name, boolean state)
             throws XmlPullParserException {
         if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) {
@@ -145,7 +167,7 @@
     }
 
     public int getDepth() {
-        return mDepth;
+        return mParser.getDepth();
     }
 
     public String getText() {
@@ -236,17 +258,10 @@
             return START_DOCUMENT;
         }
         int ev = mParser.next();
-        if (mDecNextDepth) {
-            mDepth--;
-            mDecNextDepth = false;
-        }
-        switch (ev) {
-        case START_TAG:
-            mDepth++;
-            break;
-        case END_TAG:
-            mDecNextDepth = true;
-            break;
+
+        if (ev == END_TAG && mParser.getDepth() == 1) {
+            // done with parser remove it from the context stack.
+            ensurePopped();
         }
         mEventType = ev;
         return ev;
@@ -301,7 +316,7 @@
 
     // AttributeSet implementation
 
-    
+
     public void close() {
         // pass
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlPullAttributes.java
similarity index 80%
rename from tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java
rename to tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlPullAttributes.java
index d145ff6..ba856e0 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlPullAttributes.java
@@ -14,9 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.layoutlib.bridge;
+package com.android.layoutlib.bridge.android;
 
-import com.android.layoutlib.api.IResourceValue;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.BridgeConstants;
+import com.android.resources.ResourceType;
 
 import org.xmlpull.v1.XmlPullParser;
 
@@ -55,7 +59,7 @@
         String ns = mParser.getAttributeNamespace(index);
 
         if (BridgeConstants.NS_RESOURCES.equals(ns)) {
-            Integer v = Bridge.getResourceValue(BridgeConstants.RES_ATTR, name);
+            Integer v = Bridge.getResourceId(ResourceType.ATTR, name);
             if (v != null) {
                 return v.intValue();
             }
@@ -66,8 +70,7 @@
         // this is not an attribute in the android namespace, we query the customviewloader, if
         // the namespaces match.
         if (mContext.getProjectCallback().getNamespace().equals(ns)) {
-            Integer v = mContext.getProjectCallback().getResourceValue(BridgeConstants.RES_ATTR,
-                    name);
+            Integer v = mContext.getProjectCallback().getResourceId(ResourceType.ATTR, name);
             if (v != null) {
                 return v.intValue();
             }
@@ -100,16 +103,17 @@
 
     private int resolveResourceValue(String value, int defaultValue) {
         // now look for this particular value
-        IResourceValue resource = mContext.resolveResValue(
-                mContext.findResValue(value, mPlatformFile));
+        RenderResources resources = mContext.getRenderResources();
+        ResourceValue resource = resources.resolveResValue(
+                resources.findResValue(value, mPlatformFile));
 
         if (resource != null) {
             Integer id = null;
             if (mPlatformFile || resource.isFramework()) {
-                id = Bridge.getResourceValue(resource.getType(), resource.getName());
+                id = Bridge.getResourceId(resource.getResourceType(), resource.getName());
             } else {
-                id = mContext.getProjectCallback().getResourceValue(
-                        resource.getType(), resource.getName());
+                id = mContext.getProjectCallback().getResourceId(
+                        resource.getResourceType(), resource.getName());
             }
 
             if (id != null) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
new file mode 100644
index 0000000..0c4b0d3
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2011 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.bars;
+
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.StyleResourceValue;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.layoutlib.bridge.impl.ResourceHelper;
+import com.android.resources.Density;
+import com.android.resources.ResourceType;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap_Delegate;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Base "bar" class for the window decor around the the edited layout.
+ * This is basically an horizontal layout that loads a given layout on creation (it is read
+ * through {@link Class#getResourceAsStream(String)}).
+ *
+ * The given layout should be a merge layout so that all the children belong to this class directly.
+ *
+ * It also provides a few utility methods to configure the content of the layout.
+ */
+abstract class CustomBar extends LinearLayout {
+
+    protected abstract TextView getStyleableTextView();
+
+    protected CustomBar(Context context, Density density, String layoutPath)
+            throws XmlPullParserException {
+        super(context);
+        setOrientation(LinearLayout.HORIZONTAL);
+        setGravity(Gravity.CENTER_VERTICAL);
+
+        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
+
+        KXmlParser parser = new KXmlParser();
+        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+        parser.setInput(
+                getClass().getResourceAsStream(layoutPath),
+                "UTF8");
+
+        BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
+                parser, (BridgeContext) context, false /*platformFile*/);
+
+        try {
+            inflater.inflate(bridgeParser, this, true);
+        } finally {
+            bridgeParser.ensurePopped();
+        }
+    }
+
+    private InputStream getIcon(String iconName, Density[] densityInOut, String[] pathOut,
+            boolean tryOtherDensities) {
+        // current density
+        Density density = densityInOut[0];
+
+        // bitmap url relative to this class
+        pathOut[0] = "/bars/" + density.getResourceValue() + "/" + iconName;
+
+        InputStream stream = getClass().getResourceAsStream(pathOut[0]);
+        if (stream == null && tryOtherDensities) {
+            for (Density d : Density.values()) {
+                if (d != density) {
+                    densityInOut[0] = d;
+                    stream = getIcon(iconName, densityInOut, pathOut, false /*tryOtherDensities*/);
+                    if (stream != null) {
+                        return stream;
+                    }
+                }
+            }
+        }
+
+        return stream;
+    }
+
+    protected void loadIcon(int index, String iconName, Density density) {
+        View child = getChildAt(index);
+        if (child instanceof ImageView) {
+            ImageView imageView = (ImageView) child;
+
+            String[] pathOut = new String[1];
+            Density[] densityInOut = new Density[] { density };
+            InputStream stream = getIcon(iconName, densityInOut, pathOut,
+                    true /*tryOtherDensities*/);
+            density = densityInOut[0];
+
+            if (stream != null) {
+                // look for a cached bitmap
+                Bitmap bitmap = Bridge.getCachedBitmap(pathOut[0], true /*isFramework*/);
+                if (bitmap == null) {
+                    try {
+                        bitmap = Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density);
+                        Bridge.setCachedBitmap(pathOut[0], bitmap, true /*isFramework*/);
+                    } catch (IOException e) {
+                        return;
+                    }
+                }
+
+                if (bitmap != null) {
+                    BitmapDrawable drawable = new BitmapDrawable(getContext().getResources(),
+                            bitmap);
+                    imageView.setBackgroundDrawable(drawable);
+                }
+            }
+        }
+    }
+
+    protected void loadIcon(int index, String iconReference) {
+        ResourceValue value = getResourceValue(iconReference);
+        if (value != null) {
+            loadIcon(index, value);
+        }
+    }
+
+    protected Drawable loadIcon(int index, ResourceType type, String name) {
+        BridgeContext bridgeContext = (BridgeContext) mContext;
+        RenderResources res = bridgeContext.getRenderResources();
+
+        // find the resource
+        ResourceValue value = res.getFrameworkResource(type, name);
+
+        // resolve it if needed
+        value = res.resolveResValue(value);
+        return loadIcon(index, value);
+    }
+
+    private Drawable loadIcon(int index, ResourceValue value) {
+        View child = getChildAt(index);
+        if (child instanceof ImageView) {
+            ImageView imageView = (ImageView) child;
+
+            Drawable drawable = ResourceHelper.getDrawable(
+                    value, (BridgeContext) mContext);
+            if (drawable != null) {
+                imageView.setBackgroundDrawable(drawable);
+            }
+
+            return drawable;
+        }
+
+        return null;
+    }
+
+    protected TextView setText(int index, String stringReference) {
+        View child = getChildAt(index);
+        if (child instanceof TextView) {
+            TextView textView = (TextView) child;
+            ResourceValue value = getResourceValue(stringReference);
+            if (value != null) {
+                textView.setText(value.getValue());
+            } else {
+                textView.setText(stringReference);
+            }
+            return textView;
+        }
+
+        return null;
+    }
+
+    protected void setStyle(String themeEntryName) {
+
+        BridgeContext bridgeContext = (BridgeContext) mContext;
+        RenderResources res = bridgeContext.getRenderResources();
+
+        ResourceValue value = res.findItemInTheme(themeEntryName);
+        value = res.resolveResValue(value);
+
+        if (value instanceof StyleResourceValue == false) {
+            return;
+        }
+
+        StyleResourceValue style = (StyleResourceValue) value;
+
+        // get the background
+        ResourceValue backgroundValue = res.findItemInStyle(style, "background");
+        backgroundValue = res.resolveResValue(backgroundValue);
+        if (backgroundValue != null) {
+            Drawable d = ResourceHelper.getDrawable(backgroundValue, bridgeContext);
+            if (d != null) {
+                setBackgroundDrawable(d);
+            }
+        }
+
+        TextView textView = getStyleableTextView();
+        if (textView != null) {
+            // get the text style
+            ResourceValue textStyleValue = res.findItemInStyle(style, "titleTextStyle");
+            textStyleValue = res.resolveResValue(textStyleValue);
+            if (textStyleValue instanceof StyleResourceValue) {
+                StyleResourceValue textStyle = (StyleResourceValue) textStyleValue;
+
+                ResourceValue textSize = res.findItemInStyle(textStyle, "textSize");
+                textSize = res.resolveResValue(textSize);
+
+                if (textSize != null) {
+                    TypedValue out = new TypedValue();
+                    if (ResourceHelper.stringToFloat(textSize.getValue(), out)) {
+                        textView.setTextSize(
+                                out.getDimension(bridgeContext.getResources().mMetrics));
+                    }
+                }
+
+
+                ResourceValue textColor = res.findItemInStyle(textStyle, "textColor");
+                textColor = res.resolveResValue(textColor);
+                if (textColor != null) {
+                    ColorStateList stateList = ResourceHelper.getColorStateList(
+                            textColor, bridgeContext);
+                    if (stateList != null) {
+                        textView.setTextColor(stateList);
+                    }
+                }
+            }
+        }
+    }
+
+    private ResourceValue getResourceValue(String reference) {
+        BridgeContext bridgeContext = (BridgeContext) mContext;
+        RenderResources res = bridgeContext.getRenderResources();
+
+        // find the resource
+        ResourceValue value = res.findResValue(reference, false /*isFramework*/);
+
+        // resolve it if needed
+        return res.resolveResValue(value);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java
new file mode 100644
index 0000000..3af4e3a
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 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.bars;
+
+import com.android.resources.Density;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.Context;
+import android.widget.TextView;
+
+public class FakeActionBar extends CustomBar {
+
+    private TextView mTextView;
+
+    public FakeActionBar(Context context, Density density, String label, String icon)
+            throws XmlPullParserException {
+        super(context, density, "/bars/action_bar.xml");
+
+        // Cannot access the inside items through id because no R.id values have been
+        // created for them.
+        // We do know the order though.
+        loadIcon(0, icon);
+        mTextView = setText(1, label);
+
+        setStyle("actionBarStyle");
+    }
+
+    @Override
+    protected TextView getStyleableTextView() {
+        return mTextView;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java
new file mode 100644
index 0000000..9fab51a
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 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.bars;
+
+import com.android.resources.Density;
+import com.android.resources.ResourceType;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LevelListDrawable;
+import android.view.Gravity;
+import android.widget.TextView;
+
+public class PhoneSystemBar extends CustomBar {
+
+    public PhoneSystemBar(Context context, Density density) throws XmlPullParserException {
+        super(context, density, "/bars/phone_system_bar.xml");
+
+        setGravity(mGravity | Gravity.RIGHT);
+        setBackgroundColor(0xFF000000);
+
+        // Cannot access the inside items through id because no R.id values have been
+        // created for them.
+        // We do know the order though.
+        // 0 is the spacer
+        loadIcon(1, "stat_sys_wifi_signal_4_fully.png", density);
+        Drawable drawable = loadIcon(2, ResourceType.DRAWABLE, "stat_sys_battery_charge");
+        if (drawable instanceof LevelListDrawable) {
+            ((LevelListDrawable) drawable).setLevel(100);
+        }
+    }
+
+    @Override
+    protected TextView getStyleableTextView() {
+        return null;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TabletSystemBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TabletSystemBar.java
new file mode 100644
index 0000000..5ca68fa
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TabletSystemBar.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011 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.bars;
+
+import com.android.resources.Density;
+import com.android.resources.ResourceType;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LevelListDrawable;
+import android.widget.TextView;
+
+public class TabletSystemBar extends CustomBar {
+
+    public TabletSystemBar(Context context, Density density) throws XmlPullParserException {
+        super(context, density, "/bars/tablet_system_bar.xml");
+
+        setBackgroundColor(0xFF000000);
+
+        // Cannot access the inside items through id because no R.id values have been
+        // created for them.
+        // We do know the order though.
+        loadIcon(0, "ic_sysbar_back_default.png", density);
+        loadIcon(1, "ic_sysbar_home_default.png", density);
+        loadIcon(2, "ic_sysbar_recent_default.png", density);
+        // 3 is the spacer
+        loadIcon(4, "stat_sys_wifi_signal_4_fully.png", density);
+        Drawable drawable = loadIcon(5, ResourceType.DRAWABLE, "stat_sys_battery_charge");
+        if (drawable instanceof LevelListDrawable) {
+            ((LevelListDrawable) drawable).setLevel(100);
+        }
+    }
+
+    @Override
+    protected TextView getStyleableTextView() {
+        return null;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
new file mode 100644
index 0000000..d7401d9
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2011 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.bars;
+
+import com.android.resources.Density;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.Context;
+import android.widget.TextView;
+
+public class TitleBar extends CustomBar {
+
+    private TextView mTextView;
+
+    public TitleBar(Context context, Density density, String label)
+            throws XmlPullParserException {
+        super(context, density, "/bars/title_bar.xml");
+
+        // Cannot access the inside items through id because no R.id values have been
+        // created for them.
+        // We do know the order though.
+        mTextView = setText(0, label);
+
+        setStyle("windowTitleBackgroundStyle");
+    }
+
+    @Override
+    protected TextView getStyleableTextView() {
+        return mTextView;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
new file mode 100644
index 0000000..4c18656
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2010 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.impl;
+
+import com.android.ide.common.rendering.api.IAnimationListener;
+import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.Result;
+import com.android.ide.common.rendering.api.Result.Status;
+import com.android.layoutlib.bridge.Bridge;
+
+import android.animation.ValueAnimator;
+import android.os.Handler;
+import android.os.Handler_Delegate;
+import android.os.Message;
+import android.os.Handler_Delegate.IHandlerCallback;
+
+import java.util.PriorityQueue;
+import java.util.Queue;
+
+/**
+ * Abstract animation thread.
+ * <p/>
+ * This does not actually start an animation, instead it fakes a looper that will play whatever
+ * animation is sending messages to its own {@link Handler}.
+ * <p/>
+ * Classes should implement {@link #preAnimation()} and {@link #postAnimation()}.
+ * <p/>
+ * If {@link #preAnimation()} does not start an animation somehow then the thread doesn't do
+ * anything.
+ *
+ */
+public abstract class AnimationThread extends Thread {
+
+    private static class MessageBundle implements Comparable<MessageBundle> {
+        final Handler mTarget;
+        final Message mMessage;
+        final long mUptimeMillis;
+
+        MessageBundle(Handler target, Message message, long uptimeMillis) {
+            mTarget = target;
+            mMessage = message;
+            mUptimeMillis = uptimeMillis;
+        }
+
+        public int compareTo(MessageBundle bundle) {
+            if (mUptimeMillis < bundle.mUptimeMillis) {
+                return -1;
+            }
+            return 1;
+        }
+    }
+
+    private final RenderSessionImpl mSession;
+
+    private Queue<MessageBundle> mQueue = new PriorityQueue<MessageBundle>();
+    private final IAnimationListener mListener;
+
+    public AnimationThread(RenderSessionImpl scene, String threadName,
+            IAnimationListener listener) {
+        super(threadName);
+        mSession = scene;
+        mListener = listener;
+    }
+
+    public abstract Result preAnimation();
+    public abstract void postAnimation();
+
+    @Override
+    public void run() {
+        Bridge.prepareThread();
+        try {
+            Handler_Delegate.setCallback(new IHandlerCallback() {
+                public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
+                    if (msg.what == ValueAnimator.ANIMATION_START ||
+                            msg.what == ValueAnimator.ANIMATION_FRAME) {
+                        mQueue.add(new MessageBundle(handler, msg, uptimeMillis));
+                    } else {
+                        // just ignore.
+                    }
+                }
+            });
+
+            // call out to the pre-animation work, which should start an animation or more.
+            Result result = preAnimation();
+            if (result.isSuccess() == false) {
+                mListener.done(result);
+            }
+
+            // loop the animation
+            RenderSession session = mSession.getSession();
+            do {
+                // check early.
+                if (mListener.isCanceled()) {
+                    break;
+                }
+
+                // get the next message.
+                MessageBundle bundle = mQueue.poll();
+                if (bundle == null) {
+                    break;
+                }
+
+                // sleep enough for this bundle to be on time
+                long currentTime = System.currentTimeMillis();
+                if (currentTime < bundle.mUptimeMillis) {
+                    try {
+                        sleep(bundle.mUptimeMillis - currentTime);
+                    } catch (InterruptedException e) {
+                        // FIXME log/do something/sleep again?
+                        e.printStackTrace();
+                    }
+                }
+
+                // check after sleeping.
+                if (mListener.isCanceled()) {
+                    break;
+                }
+
+                // ready to do the work, acquire the scene.
+                result = mSession.acquire(250);
+                if (result.isSuccess() == false) {
+                    mListener.done(result);
+                    return;
+                }
+
+                // process the bundle. If the animation is not finished, this will enqueue
+                // the next message, so mQueue will have another one.
+                try {
+                    // check after acquiring in case it took a while.
+                    if (mListener.isCanceled()) {
+                        break;
+                    }
+
+                    bundle.mTarget.handleMessage(bundle.mMessage);
+                    if (mSession.render(false /*freshRender*/).isSuccess()) {
+                        mListener.onNewFrame(session);
+                    }
+                } finally {
+                    mSession.release();
+                }
+            } while (mListener.isCanceled() == false && mQueue.size() > 0);
+
+            mListener.done(Status.SUCCESS.createResult());
+
+        } catch (Throwable throwable) {
+            // can't use Bridge.getLog() as the exception might be thrown outside
+            // of an acquire/release block.
+            mListener.done(Status.ERROR_UNKNOWN.createResult("Error playing animation", throwable));
+
+        } finally {
+            postAnimation();
+            Handler_Delegate.setCallback(null);
+            Bridge.cleanupThread();
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
new file mode 100644
index 0000000..ae1217d
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2010 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.impl;
+
+import com.android.layoutlib.bridge.util.Debug;
+import com.android.layoutlib.bridge.util.SparseWeakArray;
+
+import android.util.SparseArray;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages native delegates.
+ *
+ * This is used in conjunction with layoublib_create: certain Android java classes are mere
+ * wrappers around a heavily native based implementation, and we need a way to run these classes
+ * in our Eclipse rendering framework without bringing all the native code from the Android
+ * platform.
+ *
+ * Thus we instruct layoutlib_create to modify the bytecode of these classes to replace their
+ * native methods by "delegate calls".
+ *
+ * For example, a native method android.graphics.Matrix.init(...) will actually become
+ * a call to android.graphics.Matrix_Delegate.init(...).
+ *
+ * The Android java classes that use native code uses an int (Java side) to reference native
+ * objects. This int is generally directly the pointer to the C structure counterpart.
+ * Typically a creation method will return such an int, and then this int will be passed later
+ * to a Java method to identify the C object to manipulate.
+ *
+ * Since we cannot use the Java object reference as the int directly, DelegateManager manages the
+ * int -> Delegate class link.
+ *
+ * Native methods usually always have the int as parameters. The first thing the delegate method
+ * will do is call {@link #getDelegate(int)} to get the Java object matching the int.
+ *
+ * Typical native init methods are returning a new int back to the Java class, so
+ * {@link #addNewDelegate(Object)} does the same.
+ *
+ * The JNI references are counted, so we do the same through a {@link WeakReference}. Because
+ * the Java object needs to count as a reference (even though it only holds an int), we use the
+ * following mechanism:
+ *
+ * - {@link #addNewDelegate(Object)} and {@link #removeJavaReferenceFor(int)} adds and removes
+ *   the delegate to/from a list. This list hold the reference and prevents the GC from reclaiming
+ *   the delegate.
+ *
+ * - {@link #addNewDelegate(Object)} also adds the delegate to a {@link SparseArray} that holds a
+ *   {@link WeakReference} to the delegate. This allows the delegate to be deleted automatically
+ *   when nothing references it. This means that any class that holds a delegate (except for the
+ *   Java main class) must not use the int but the Delegate class instead. The integers must
+ *   only be used in the API between the main Java class and the Delegate.
+ *
+ * @param <T> the delegate class to manage
+ */
+public final class DelegateManager<T> {
+    private final Class<T> mClass;
+    private final SparseWeakArray<T> mDelegates = new SparseWeakArray<T>();
+    /** list used to store delegates when their main object holds a reference to them.
+     * This is to ensure that the WeakReference in the SparseWeakArray doesn't get GC'ed
+     * @see #addNewDelegate(Object)
+     * @see #removeJavaReferenceFor(int)
+     */
+    private final List<T> mJavaReferences = new ArrayList<T>();
+    private int mDelegateCounter = 0;
+
+    public DelegateManager(Class<T> theClass) {
+        mClass = theClass;
+    }
+
+    /**
+     * Returns the delegate from the given native int.
+     * <p>
+     * If the int is zero, then this will always return null.
+     * <p>
+     * If the int is non zero and the delegate is not found, this will throw an assert.
+     *
+     * @param native_object the native int.
+     * @return the delegate or null if not found.
+     */
+    public T getDelegate(int native_object) {
+        if (native_object > 0) {
+            T delegate =  mDelegates.get(native_object);
+
+            if (Debug.DEBUG) {
+                if (delegate == null) {
+                    System.out.println("Unknown " + mClass.getSimpleName() + " with int " +
+                            native_object);
+                }
+            }
+
+            assert delegate != null;
+            return delegate;
+        }
+        return null;
+    }
+
+    /**
+     * Adds a delegate to the manager and returns the native int used to identify it.
+     * @param newDelegate the delegate to add
+     * @return a unique native int to identify the delegate
+     */
+    public int addNewDelegate(T newDelegate) {
+        int native_object = ++mDelegateCounter;
+        mDelegates.put(native_object, newDelegate);
+        assert !mJavaReferences.contains(newDelegate);
+        mJavaReferences.add(newDelegate);
+
+        if (Debug.DEBUG) {
+            System.out.println("New " + mClass.getSimpleName() + " with int " + native_object);
+        }
+
+        return native_object;
+    }
+
+    /**
+     * Removes the main reference on the given delegate.
+     * @param native_object the native integer representing the delegate.
+     */
+    public void removeJavaReferenceFor(int native_object) {
+        T delegate = getDelegate(native_object);
+
+        if (Debug.DEBUG) {
+            System.out.println("Removing main Java ref on " + mClass.getSimpleName() +
+                    " with int " + native_object);
+        }
+
+        mJavaReferences.remove(delegate);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java
similarity index 95%
rename from tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java
rename to tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java
index 801503b..f62fad2 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.layoutlib.bridge;
+package com.android.layoutlib.bridge.impl;
 
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
@@ -134,6 +134,15 @@
         return mFallBackFonts;
     }
 
+    /**
+     * Returns a {@link Font} object given a family name and a style value (constant in
+     * {@link Typeface}).
+     * @param family the family name
+     * @param style a 1-item array containing the requested style. Based on the font being read
+     *              the actual style may be different. The array contains the actual style after
+     *              the method returns.
+     * @return the font object or null if no match could be found.
+     */
     public synchronized Font getFont(String family, int[] style) {
         if (family == null) {
             return null;
@@ -154,7 +163,7 @@
             mTtfToFontMap.put(ttf, styleMap);
         }
 
-        Font f = styleMap.get(style);
+        Font f = styleMap.get(style[0]);
 
         if (f != null) {
             return f;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
new file mode 100644
index 0000000..21d6b1a
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
@@ -0,0 +1,803 @@
+/*
+ * Copyright (C) 2010 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.impl;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+
+import android.graphics.Bitmap_Delegate;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint_Delegate;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.graphics.Region_Delegate;
+import android.graphics.Shader_Delegate;
+import android.graphics.Xfermode_Delegate;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+
+/**
+ * Class representing a graphics context snapshot, as well as a context stack as a linked list.
+ * <p>
+ * This is based on top of {@link Graphics2D} but can operate independently if none are available
+ * yet when setting transforms and clip information.
+ * <p>
+ * This allows for drawing through {@link #draw(Drawable, Paint_Delegate)} and
+ * {@link #draw(Drawable, Paint_Delegate)}
+ *
+ * Handling of layers (created with {@link Canvas#saveLayer(RectF, Paint, int)}) is handled through
+ * a list of Graphics2D for each layers. The class actually maintains a list of {@link Layer}
+ * for each layer. Doing a save() will duplicate this list so that each graphics2D object
+ * ({@link Layer#getGraphics()}) is configured only for the new snapshot.
+ */
+public class GcSnapshot {
+
+    private final GcSnapshot mPrevious;
+    private final int mFlags;
+
+    /** list of layers. The first item in the list is always the  */
+    private final ArrayList<Layer> mLayers = new ArrayList<Layer>();
+
+    /** temp transform in case transformation are set before a Graphics2D exists */
+    private AffineTransform mTransform = null;
+    /** temp clip in case clipping is set before a Graphics2D exists */
+    private Area mClip = null;
+
+    // local layer data
+    /** a local layer created with {@link Canvas#saveLayer(RectF, Paint, int)}.
+     * If this is null, this does not mean there's no layer, just that the snapshot is not the
+     * one that created the layer.
+     * @see #getLayerSnapshot()
+     */
+    private final Layer mLocalLayer;
+    private final Paint_Delegate mLocalLayerPaint;
+    private final Rect mLayerBounds;
+
+    public interface Drawable {
+        void draw(Graphics2D graphics, Paint_Delegate paint);
+    }
+
+    /**
+     * Class containing information about a layer.
+     *
+     * This contains graphics, bitmap and layer information.
+     */
+    private static class Layer {
+        private final Graphics2D mGraphics;
+        private final Bitmap_Delegate mBitmap;
+        private final BufferedImage mImage;
+        /** the flags that were used to configure the layer. This is never changed, and passed
+         * as is when {@link #makeCopy()} is called */
+        private final int mFlags;
+        /** the original content of the layer when the next object was created. This is not
+         * passed in {@link #makeCopy()} and instead is recreated when a new layer is added
+         * (depending on its flags) */
+        private BufferedImage mOriginalCopy;
+
+        /**
+         * Creates a layer with a graphics and a bitmap. This is only used to create
+         * the base layer.
+         *
+         * @param graphics the graphics
+         * @param bitmap the bitmap
+         */
+        Layer(Graphics2D graphics, Bitmap_Delegate bitmap) {
+            mGraphics = graphics;
+            mBitmap = bitmap;
+            mImage = mBitmap.getImage();
+            mFlags = 0;
+        }
+
+        /**
+         * Creates a layer with a graphics and an image. If the image belongs to a
+         * {@link Bitmap_Delegate} (case of the base layer), then
+         * {@link Layer#Layer(Graphics2D, Bitmap_Delegate)} should be used.
+         *
+         * @param graphics the graphics the new graphics for this layer
+         * @param image the image the image from which the graphics came
+         * @param flags the flags that were used to save this layer
+         */
+        Layer(Graphics2D graphics, BufferedImage image, int flags) {
+            mGraphics = graphics;
+            mBitmap = null;
+            mImage = image;
+            mFlags = flags;
+        }
+
+        /** The Graphics2D, guaranteed to be non null */
+        Graphics2D getGraphics() {
+            return mGraphics;
+        }
+
+        /** The BufferedImage, guaranteed to be non null */
+        BufferedImage getImage() {
+            return mImage;
+        }
+
+        /** Returns the layer save flags. This is only valid for additional layers.
+         * For the base layer this will always return 0;
+         * For a given layer, all further copies of this {@link Layer} object in new snapshots
+         * will always return the same value.
+         */
+        int getFlags() {
+            return mFlags;
+        }
+
+        Layer makeCopy() {
+            if (mBitmap != null) {
+                return new Layer((Graphics2D) mGraphics.create(), mBitmap);
+            }
+
+            return new Layer((Graphics2D) mGraphics.create(), mImage, mFlags);
+        }
+
+        /** sets an optional copy of the original content to be used during restore */
+        void setOriginalCopy(BufferedImage image) {
+            mOriginalCopy = image;
+        }
+
+        BufferedImage getOriginalCopy() {
+            return mOriginalCopy;
+        }
+
+        void change() {
+            if (mBitmap != null) {
+                mBitmap.change();
+            }
+        }
+
+        /**
+         * Sets the clip for the graphics2D object associated with the layer.
+         * This should be used over the normal Graphics2D setClip method.
+         *
+         * @param clipShape the shape to use a the clip shape.
+         */
+        void setClip(Shape clipShape) {
+            // because setClip is only guaranteed to work with rectangle shape,
+            // first reset the clip to max and then intersect the current (empty)
+            // clip with the shap.
+            mGraphics.setClip(null);
+            mGraphics.clip(clipShape);
+        }
+
+        /**
+         * Clips the layer with the given shape. This performs an intersect between the current
+         * clip shape and the given shape.
+         * @param shape the new clip shape.
+         */
+        public void clip(Shape shape) {
+            mGraphics.clip(shape);
+        }
+    }
+
+    /**
+     * Creates the root snapshot associating it with a given bitmap.
+     * <p>
+     * If <var>bitmap</var> is null, then {@link GcSnapshot#setBitmap(Bitmap_Delegate)} must be
+     * called before the snapshot can be used to draw. Transform and clip operations are permitted
+     * before.
+     *
+     * @param image the image to associate to the snapshot or null.
+     * @return the root snapshot
+     */
+    public static GcSnapshot createDefaultSnapshot(Bitmap_Delegate bitmap) {
+        GcSnapshot snapshot = new GcSnapshot();
+        if (bitmap != null) {
+            snapshot.setBitmap(bitmap);
+        }
+
+        return snapshot;
+    }
+
+    /**
+     * Saves the current state according to the given flags and returns the new current snapshot.
+     * <p/>
+     * This is the equivalent of {@link Canvas#save(int)}
+     *
+     * @param flags the save flags.
+     * @return the new snapshot
+     *
+     * @see Canvas#save(int)
+     */
+    public GcSnapshot save(int flags) {
+        return new GcSnapshot(this, null /*layerbounds*/, null /*paint*/, flags);
+    }
+
+    /**
+     * Saves the current state and creates a new layer, and returns the new current snapshot.
+     * <p/>
+     * This is the equivalent of {@link Canvas#saveLayer(RectF, Paint, int)}
+     *
+     * @param layerBounds the layer bounds
+     * @param paint the Paint information used to blit the layer back into the layers underneath
+     *          upon restore
+     * @param flags the save flags.
+     * @return the new snapshot
+     *
+     * @see Canvas#saveLayer(RectF, Paint, int)
+     */
+    public GcSnapshot saveLayer(RectF layerBounds, Paint_Delegate paint, int flags) {
+        return new GcSnapshot(this, layerBounds, paint, flags);
+    }
+
+    /**
+     * Creates the root snapshot.
+     * {@link #setGraphics2D(Graphics2D)} will have to be called on it when possible.
+     */
+    private GcSnapshot() {
+        mPrevious = null;
+        mFlags = 0;
+        mLocalLayer = null;
+        mLocalLayerPaint = null;
+        mLayerBounds = null;
+    }
+
+    /**
+     * Creates a new {@link GcSnapshot} on top of another one, with a layer data to be restored
+     * into the main graphics when {@link #restore()} is called.
+     *
+     * @param previous the previous snapshot head.
+     * @param layerBounds the region of the layer. Optional, if null, this is a normal save()
+     * @param paint the Paint information used to blit the layer back into the layers underneath
+     *          upon restore
+     * @param flags the flags regarding what should be saved.
+     */
+    private GcSnapshot(GcSnapshot previous, RectF layerBounds, Paint_Delegate paint, int flags) {
+        assert previous != null;
+        mPrevious = previous;
+        mFlags = flags;
+
+        // make a copy of the current layers before adding the new one.
+        // This keeps the same BufferedImage reference but creates new Graphics2D for this
+        // snapshot.
+        // It does not copy whatever original copy the layers have, as they will be done
+        // only if the new layer doesn't clip drawing to itself.
+        for (Layer layer : mPrevious.mLayers) {
+            mLayers.add(layer.makeCopy());
+        }
+
+        if (layerBounds != null) {
+            // get the current transform
+            AffineTransform matrix = mLayers.get(0).getGraphics().getTransform();
+
+            // transform the layerBounds with the current transform and stores it into a int rect
+            RectF rect2 = new RectF();
+            mapRect(matrix, rect2, layerBounds);
+            mLayerBounds = new Rect();
+            rect2.round(mLayerBounds);
+
+            // get the base layer (always at index 0)
+            Layer baseLayer = mLayers.get(0);
+
+            // create the image for the layer
+            BufferedImage layerImage = new BufferedImage(
+                    baseLayer.getImage().getWidth(),
+                    baseLayer.getImage().getHeight(),
+                    (mFlags & Canvas.HAS_ALPHA_LAYER_SAVE_FLAG) != 0 ?
+                            BufferedImage.TYPE_INT_ARGB :
+                                BufferedImage.TYPE_INT_RGB);
+
+            // create a graphics for it so that drawing can be done.
+            Graphics2D layerGraphics = layerImage.createGraphics();
+
+            // because this layer inherits the current context for transform and clip,
+            // set them to one from the base layer.
+            AffineTransform currentMtx = baseLayer.getGraphics().getTransform();
+            layerGraphics.setTransform(currentMtx);
+
+            // create a new layer for this new layer and add it to the list at the end.
+            mLayers.add(mLocalLayer = new Layer(layerGraphics, layerImage, flags));
+
+            // set the clip on it.
+            Shape currentClip = baseLayer.getGraphics().getClip();
+            mLocalLayer.setClip(currentClip);
+
+            // if the drawing is not clipped to the local layer only, we save the current content
+            // of all other layers. We are only interested in the part that will actually
+            // be drawn, so we create as small bitmaps as we can.
+            // This is so that we can erase the drawing that goes in the layers below that will
+            // be coming from the layer itself.
+            if ((mFlags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0) {
+                int w = mLayerBounds.width();
+                int h = mLayerBounds.height();
+                for (int i = 0 ; i < mLayers.size() - 1 ; i++) {
+                    Layer layer = mLayers.get(i);
+                    BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
+                    Graphics2D graphics = image.createGraphics();
+                    graphics.drawImage(layer.getImage(),
+                            0, 0, w, h,
+                            mLayerBounds.left, mLayerBounds.top,
+                                    mLayerBounds.right, mLayerBounds.bottom,
+                            null);
+                    graphics.dispose();
+                    layer.setOriginalCopy(image);
+                }
+            }
+        } else {
+            mLocalLayer = null;
+            mLayerBounds = null;
+        }
+
+        mLocalLayerPaint  = paint;
+    }
+
+    public void dispose() {
+        for (Layer layer : mLayers) {
+            layer.getGraphics().dispose();
+        }
+
+        if (mPrevious != null) {
+            mPrevious.dispose();
+        }
+    }
+
+    /**
+     * Restores the top {@link GcSnapshot}, and returns the next one.
+     */
+    public GcSnapshot restore() {
+        return doRestore();
+    }
+
+    /**
+     * Restores the {@link GcSnapshot} to <var>saveCount</var>.
+     * @param saveCount the saveCount or -1 to only restore 1.
+     *
+     * @return the new head of the Gc snapshot stack.
+     */
+    public GcSnapshot restoreTo(int saveCount) {
+        return doRestoreTo(size(), saveCount);
+    }
+
+    public int size() {
+        if (mPrevious != null) {
+            return mPrevious.size() + 1;
+        }
+
+        return 1;
+    }
+
+    /**
+     * Link the snapshot to a Bitmap_Delegate.
+     * <p/>
+     * This is only for the case where the snapshot was created with a null image when calling
+     * {@link #createDefaultSnapshot(Bitmap_Delegate)}, and is therefore not yet linked to
+     * a previous snapshot.
+     * <p/>
+     * If any transform or clip information was set before, they are put into the Graphics object.
+     * @param bitmap the bitmap to link to.
+     */
+    public void setBitmap(Bitmap_Delegate bitmap) {
+        // create a new Layer for the bitmap. This will be the base layer.
+        Graphics2D graphics2D = bitmap.getImage().createGraphics();
+        Layer baseLayer = new Layer(graphics2D, bitmap);
+
+        // Set the current transform and clip which can either come from mTransform/mClip if they
+        // were set when there was no bitmap/layers or from the current base layers if there is
+        // one already.
+
+        graphics2D.setTransform(getTransform());
+        // reset mTransform in case there was one.
+        mTransform = null;
+
+        baseLayer.setClip(getClip());
+        // reset mClip in case there was one.
+        mClip = null;
+
+        // replace whatever current layers we have with this.
+        mLayers.clear();
+        mLayers.add(baseLayer);
+
+    }
+
+    public void translate(float dx, float dy) {
+        if (mLayers.size() > 0) {
+            for (Layer layer : mLayers) {
+                layer.getGraphics().translate(dx, dy);
+            }
+        } else {
+            if (mTransform == null) {
+                mTransform = new AffineTransform();
+            }
+            mTransform.translate(dx, dy);
+        }
+    }
+
+    public void rotate(double radians) {
+        if (mLayers.size() > 0) {
+            for (Layer layer : mLayers) {
+                layer.getGraphics().rotate(radians);
+            }
+        } else {
+            if (mTransform == null) {
+                mTransform = new AffineTransform();
+            }
+            mTransform.rotate(radians);
+        }
+    }
+
+    public void scale(float sx, float sy) {
+        if (mLayers.size() > 0) {
+            for (Layer layer : mLayers) {
+                layer.getGraphics().scale(sx, sy);
+            }
+        } else {
+            if (mTransform == null) {
+                mTransform = new AffineTransform();
+            }
+            mTransform.scale(sx, sy);
+        }
+    }
+
+    public AffineTransform getTransform() {
+        if (mLayers.size() > 0) {
+            // all graphics2D in the list have the same transform
+            return mLayers.get(0).getGraphics().getTransform();
+        } else {
+            if (mTransform == null) {
+                mTransform = new AffineTransform();
+            }
+            return mTransform;
+        }
+    }
+
+    public void setTransform(AffineTransform transform) {
+        if (mLayers.size() > 0) {
+            for (Layer layer : mLayers) {
+                layer.getGraphics().setTransform(transform);
+            }
+        } else {
+            if (mTransform == null) {
+                mTransform = new AffineTransform();
+            }
+            mTransform.setTransform(transform);
+        }
+    }
+
+    public boolean clip(Shape shape, int regionOp) {
+        // Simple case of intersect with existing layers.
+        // Because Graphics2D#setClip works a bit peculiarly, we optimize
+        // the case of clipping by intersection, as it's supported natively.
+        if (regionOp == Region.Op.INTERSECT.nativeInt && mLayers.size() > 0) {
+            for (Layer layer : mLayers) {
+                layer.clip(shape);
+            }
+
+            Shape currentClip = getClip();
+            return currentClip != null && currentClip.getBounds().isEmpty() == false;
+        }
+
+        Area area = null;
+
+        if (regionOp == Region.Op.REPLACE.nativeInt) {
+            area = new Area(shape);
+        } else {
+            area = Region_Delegate.combineShapes(getClip(), shape, regionOp);
+        }
+
+        assert area != null;
+
+        if (mLayers.size() > 0) {
+            if (area != null) {
+                for (Layer layer : mLayers) {
+                    layer.setClip(area);
+                }
+            }
+
+            Shape currentClip = getClip();
+            return currentClip != null && currentClip.getBounds().isEmpty() == false;
+        } else {
+            if (area != null) {
+                mClip = area;
+            } else {
+                mClip = new Area();
+            }
+
+            return mClip.getBounds().isEmpty() == false;
+        }
+    }
+
+    public boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
+        return clip(new Rectangle2D.Float(left, top, right - left, bottom - top), regionOp);
+    }
+
+    /**
+     * Returns the current clip, or null if none have been setup.
+     */
+    public Shape getClip() {
+        if (mLayers.size() > 0) {
+            // they all have the same clip
+            return mLayers.get(0).getGraphics().getClip();
+        } else {
+            return mClip;
+        }
+    }
+
+    private GcSnapshot doRestoreTo(int size, int saveCount) {
+        if (size <= saveCount) {
+            return this;
+        }
+
+        // restore the current one first.
+        GcSnapshot previous = doRestore();
+
+        if (size == saveCount + 1) { // this was the only one that needed restore.
+            return previous;
+        } else {
+            return previous.doRestoreTo(size - 1, saveCount);
+        }
+    }
+
+    /**
+     * Executes the Drawable's draw method, with a null paint delegate.
+     * <p/>
+     * Note that the method can be called several times if there are more than one active layer.
+     * @param drawable
+     */
+    public void draw(Drawable drawable) {
+        draw(drawable, null, false /*compositeOnly*/, false /*forceSrcMode*/);
+    }
+
+    /**
+     * Executes the Drawable's draw method.
+     * <p/>
+     * Note that the method can be called several times if there are more than one active layer.
+     * @param drawable
+     * @param paint
+     * @param compositeOnly whether the paint is used for composite only. This is typically
+     *          the case for bitmaps.
+     * @param forceSrcMode if true, this overrides the composite to be SRC
+     */
+    public void draw(Drawable drawable, Paint_Delegate paint, boolean compositeOnly,
+            boolean forceSrcMode) {
+        // the current snapshot may not have a mLocalLayer (ie it was created on save() instead
+        // of saveLayer(), but that doesn't mean there's no layer.
+        // mLayers however saves all the information we need (flags).
+        if (mLayers.size() == 1) {
+            // no layer, only base layer. easy case.
+            drawInLayer(mLayers.get(0), drawable, paint, compositeOnly, forceSrcMode);
+        } else {
+            // draw in all the layers until the layer save flags tells us to stop (ie drawing
+            // in that layer is limited to the layer itself.
+            int flags;
+            int i = mLayers.size() - 1;
+
+            do {
+                Layer layer = mLayers.get(i);
+
+                drawInLayer(layer, drawable, paint, compositeOnly, forceSrcMode);
+
+                // then go to previous layer, only if there are any left, and its flags
+                // doesn't restrict drawing to the layer itself.
+                i--;
+                flags = layer.getFlags();
+            } while (i >= 0 && (flags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0);
+        }
+    }
+
+    private void drawInLayer(Layer layer, Drawable drawable, Paint_Delegate paint,
+            boolean compositeOnly, boolean forceSrcMode) {
+        Graphics2D originalGraphics = layer.getGraphics();
+        // get a Graphics2D object configured with the drawing parameters.
+        Graphics2D configuredGraphics2D =
+            paint != null ?
+                    createCustomGraphics(originalGraphics, paint, compositeOnly, forceSrcMode) :
+                        (Graphics2D) originalGraphics.create();
+
+        try {
+            drawable.draw(configuredGraphics2D, paint);
+            layer.change();
+        } finally {
+            // dispose Graphics2D object
+            configuredGraphics2D.dispose();
+        }
+    }
+
+    private GcSnapshot doRestore() {
+        if (mPrevious != null) {
+            if (mLocalLayer != null) {
+                // prepare to blit the layers in which we have draw, in the layer beneath
+                // them, starting with the top one (which is the current local layer).
+                int i = mLayers.size() - 1;
+                int flags;
+                do {
+                    Layer dstLayer = mLayers.get(i - 1);
+
+                    restoreLayer(dstLayer);
+
+                    flags = dstLayer.getFlags();
+                    i--;
+                } while (i > 0 && (flags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0);
+            }
+
+            // if this snapshot does not save everything, then set the previous snapshot
+            // to this snapshot content
+
+            // didn't save the matrix? set the current matrix on the previous snapshot
+            if ((mFlags & Canvas.MATRIX_SAVE_FLAG) == 0) {
+                AffineTransform mtx = getTransform();
+                for (Layer layer : mPrevious.mLayers) {
+                    layer.getGraphics().setTransform(mtx);
+                }
+            }
+
+            // didn't save the clip? set the current clip on the previous snapshot
+            if ((mFlags & Canvas.CLIP_SAVE_FLAG) == 0) {
+                Shape clip = getClip();
+                for (Layer layer : mPrevious.mLayers) {
+                    layer.setClip(clip);
+                }
+            }
+        }
+
+        for (Layer layer : mLayers) {
+            layer.getGraphics().dispose();
+        }
+
+        return mPrevious;
+    }
+
+    private void restoreLayer(Layer dstLayer) {
+
+        Graphics2D baseGfx = dstLayer.getImage().createGraphics();
+
+        // if the layer contains an original copy this means the flags
+        // didn't restrict drawing to the local layer and we need to make sure the
+        // layer bounds in the layer beneath didn't receive any drawing.
+        // so we use the originalCopy to erase the new drawings in there.
+        BufferedImage originalCopy = dstLayer.getOriginalCopy();
+        if (originalCopy != null) {
+            Graphics2D g = (Graphics2D) baseGfx.create();
+            g.setComposite(AlphaComposite.Src);
+
+            g.drawImage(originalCopy,
+                    mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
+                    0, 0, mLayerBounds.width(), mLayerBounds.height(),
+                    null);
+            g.dispose();
+        }
+
+        // now draw put the content of the local layer onto the layer,
+        // using the paint information
+        Graphics2D g = createCustomGraphics(baseGfx, mLocalLayerPaint,
+                true /*alphaOnly*/, false /*forceSrcMode*/);
+
+        g.drawImage(mLocalLayer.getImage(),
+                mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
+                mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
+                null);
+        g.dispose();
+
+        baseGfx.dispose();
+    }
+
+    /**
+     * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
+     * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
+     */
+    private Graphics2D createCustomGraphics(Graphics2D original, Paint_Delegate paint,
+            boolean compositeOnly, boolean forceSrcMode) {
+        // make new one graphics
+        Graphics2D g = (Graphics2D) original.create();
+
+        // configure it
+
+        if (paint.isAntiAliased()) {
+            g.setRenderingHint(
+                    RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+            g.setRenderingHint(
+                    RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+        }
+
+        boolean customShader = false;
+
+        // get the shader first, as it'll replace the color if it can be used it.
+        if (compositeOnly == false) {
+            Shader_Delegate shaderDelegate = paint.getShader();
+            if (shaderDelegate != null) {
+                if (shaderDelegate.isSupported()) {
+                    java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
+                    assert shaderPaint != null;
+                    if (shaderPaint != null) {
+                        g.setPaint(shaderPaint);
+                        customShader = true;
+                    }
+                } else {
+                    Bridge.getLog().fidelityWarning(LayoutLog.TAG_SHADER,
+                            shaderDelegate.getSupportMessage(),
+                            null /*throwable*/, null /*data*/);
+                }
+            }
+
+            // if no shader, use the paint color
+            if (customShader == false) {
+                g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
+            }
+
+            // set the stroke
+            g.setStroke(paint.getJavaStroke());
+        }
+
+        // the alpha for the composite. Always opaque if the normal paint color is used since
+        // it contains the alpha
+        int alpha = (compositeOnly || customShader) ? paint.getAlpha() : 0xFF;
+
+        if (forceSrcMode) {
+            g.setComposite(AlphaComposite.getInstance(
+                    AlphaComposite.SRC, (float) alpha / 255.f));
+        } else {
+            boolean customXfermode = false;
+            Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
+            if (xfermodeDelegate != null) {
+                if (xfermodeDelegate.isSupported()) {
+                    Composite composite = xfermodeDelegate.getComposite(alpha);
+                    assert composite != null;
+                    if (composite != null) {
+                        g.setComposite(composite);
+                        customXfermode = true;
+                    }
+                } else {
+                    Bridge.getLog().fidelityWarning(LayoutLog.TAG_XFERMODE,
+                            xfermodeDelegate.getSupportMessage(),
+                            null /*throwable*/, null /*data*/);
+                }
+            }
+
+            // if there was no custom xfermode, but we have alpha (due to a shader and a non
+            // opaque alpha channel in the paint color), then we create an AlphaComposite anyway
+            // that will handle the alpha.
+            if (customXfermode == false && alpha != 0xFF) {
+                g.setComposite(AlphaComposite.getInstance(
+                        AlphaComposite.SRC_OVER, (float) alpha / 255.f));
+            }
+        }
+
+        return g;
+    }
+
+    private void mapRect(AffineTransform matrix, RectF dst, RectF src) {
+        // array with 4 corners
+        float[] corners = new float[] {
+                src.left, src.top,
+                src.right, src.top,
+                src.right, src.bottom,
+                src.left, src.bottom,
+        };
+
+        // apply the transform to them.
+        matrix.transform(corners, 0, corners, 0, 4);
+
+        // now put the result in the rect. We take the min/max of Xs and min/max of Ys
+        dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
+        dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
+
+        dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
+        dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java
new file mode 100644
index 0000000..35e5987
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 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.impl;
+
+import com.android.ide.common.rendering.api.IAnimationListener;
+import com.android.ide.common.rendering.api.Result;
+import com.android.ide.common.rendering.api.Result.Status;
+
+import android.animation.Animator;
+
+public class PlayAnimationThread extends AnimationThread {
+
+    private final Animator mAnimator;
+
+    public PlayAnimationThread(Animator animator, RenderSessionImpl scene, String animName,
+            IAnimationListener listener) {
+        super(scene, animName, listener);
+        mAnimator = animator;
+    }
+
+    @Override
+    public Result preAnimation() {
+        // start the animation. This will send a message to the handler right away, so
+        // the queue is filled when this method returns.
+        mAnimator.start();
+
+        return Status.SUCCESS.createResult();
+    }
+
+    @Override
+    public void postAnimation() {
+        // nothing to be done.
+    }
+}
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
new file mode 100644
index 0000000..8e80c21
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2010 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.impl;
+
+import static com.android.ide.common.rendering.api.Result.Status.ERROR_LOCK_INTERRUPTED;
+import static com.android.ide.common.rendering.api.Result.Status.ERROR_TIMEOUT;
+import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.RenderParams;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.Result;
+import com.android.ide.common.rendering.api.RenderResources.FrameworkResourceIdProvider;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.resources.ResourceType;
+
+import android.util.DisplayMetrics;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Base class for rendering action.
+ *
+ * It provides life-cycle methods to init and stop the rendering.
+ * The most important methods are:
+ * {@link #init(long)} and {@link #acquire(long)} to start a rendering and {@link #release()}
+ * after the rendering.
+ *
+ *
+ * @param <T> the {@link RenderParams} implementation
+ *
+ */
+public abstract class RenderAction<T extends RenderParams> extends FrameworkResourceIdProvider {
+
+    /**
+     * The current context being rendered. This is set through {@link #acquire(long)} and
+     * {@link #init(long)}, and unset in {@link #release()}.
+     */
+    private static BridgeContext sCurrentContext = null;
+
+    private final T mParams;
+
+    private BridgeContext mContext;
+
+    /**
+     * Creates a renderAction.
+     * <p>
+     * This <b>must</b> be followed by a call to {@link RenderAction#init()}, which act as a
+     * call to {@link RenderAction#acquire(long)}
+     *
+     * @param params the RenderParams. This must be a copy that the action can keep
+     *
+     */
+    protected RenderAction(T params) {
+        mParams = params;
+    }
+
+    /**
+     * Initializes and acquires the scene, creating various Android objects such as context,
+     * inflater, and parser.
+     *
+     * @param timeout the time to wait if another rendering is happening.
+     *
+     * @return whether the scene was prepared
+     *
+     * @see #acquire(long)
+     * @see #release()
+     */
+    public Result init(long timeout) {
+        // acquire the lock. if the result is null, lock was just acquired, otherwise, return
+        // the result.
+        Result result = acquireLock(timeout);
+        if (result != null) {
+            return result;
+        }
+
+        // setup the display Metrics.
+        DisplayMetrics metrics = new DisplayMetrics();
+        metrics.densityDpi = mParams.getDensity().getDpiValue();
+        metrics.density = metrics.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT;
+        metrics.scaledDensity = metrics.density;
+        metrics.widthPixels = mParams.getScreenWidth();
+        metrics.heightPixels = mParams.getScreenHeight();
+        metrics.xdpi = mParams.getXdpi();
+        metrics.ydpi = mParams.getYdpi();
+
+        RenderResources resources = mParams.getResources();
+
+        // build the context
+        mContext = new BridgeContext(mParams.getProjectKey(), metrics, resources,
+                mParams.getProjectCallback(), mParams.getTargetSdkVersion());
+
+        setUp();
+
+        return SUCCESS.createResult();
+    }
+
+    /**
+     * Prepares the scene for action.
+     * <p>
+     * This call is blocking if another rendering/inflating is currently happening, and will return
+     * whether the preparation worked.
+     *
+     * 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}.
+     *
+     * After scene actions have taken place, only one call to {@link #release()} must be
+     * done.
+     *
+     * @param timeout the time to wait if another rendering is happening.
+     *
+     * @return whether the scene was prepared
+     *
+     * @see #release()
+     *
+     * @throws IllegalStateException if {@link #init(long)} was never called.
+     */
+    public Result acquire(long timeout) {
+        if (mContext == null) {
+            throw new IllegalStateException("After scene creation, #init() must be called");
+        }
+
+        // acquire the lock. if the result is null, lock was just acquired, otherwise, return
+        // the result.
+        Result result = acquireLock(timeout);
+        if (result != null) {
+            return result;
+        }
+
+        setUp();
+
+        return SUCCESS.createResult();
+    }
+
+    /**
+     * 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
+     * instance (see {@link Result#getStatus()}) if an error occurred.
+     *
+     * @param timeout the time to wait if another rendering is happening.
+     * @return null if the lock was just acquire or another result depending on the state.
+     *
+     * @throws IllegalStateException if the current context is different than the one owned by
+     *      the scene.
+     */
+    private Result acquireLock(long timeout) {
+        ReentrantLock lock = Bridge.getLock();
+        if (lock.isHeldByCurrentThread() == false) {
+            try {
+                boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
+
+                if (acquired == false) {
+                    return ERROR_TIMEOUT.createResult();
+                }
+            } catch (InterruptedException e) {
+                return ERROR_LOCK_INTERRUPTED.createResult();
+            }
+        } else {
+            // This thread holds the lock already. Checks that this wasn't for a different context.
+            // If this is called by init, mContext will be null and so should sCurrentContext
+            // anyway
+            if (mContext != sCurrentContext) {
+                throw new IllegalStateException("Acquiring different scenes from same thread without releases");
+            }
+            return SUCCESS.createResult();
+        }
+
+        return null;
+    }
+
+    /**
+     * Cleans up the scene after an action.
+     */
+    public void release() {
+        ReentrantLock lock = Bridge.getLock();
+
+        // with the use of finally blocks, it is possible to find ourself calling this
+        // without a successful call to prepareScene. This test makes sure that unlock() will
+        // not throw IllegalMonitorStateException.
+        if (lock.isHeldByCurrentThread()) {
+            tearDown();
+            lock.unlock();
+        }
+    }
+
+    /**
+     * Sets up the session for rendering.
+     * <p/>
+     * The counterpart is {@link #tearDown()}.
+     */
+    private void setUp() {
+        // make sure the Resources object references the context (and other objects) for this
+        // scene
+        mContext.initResources();
+        sCurrentContext = mContext;
+
+        LayoutLog currentLog = mParams.getLog();
+        Bridge.setLog(currentLog);
+        mContext.getRenderResources().setFrameworkResourceIdProvider(this);
+        mContext.getRenderResources().setLogger(currentLog);
+    }
+
+    /**
+     * Tear down the session after rendering.
+     * <p/>
+     * The counterpart is {@link #setUp()}.
+     */
+    private void tearDown() {
+        // Make sure to remove static references, otherwise we could not unload the lib
+        mContext.disposeResources();
+        sCurrentContext = null;
+
+        Bridge.setLog(null);
+        mContext.getRenderResources().setFrameworkResourceIdProvider(null);
+        mContext.getRenderResources().setLogger(null);
+    }
+
+    public static BridgeContext getCurrentContext() {
+        return sCurrentContext;
+    }
+
+    protected T getParams() {
+        return mParams;
+    }
+
+    protected BridgeContext getContext() {
+        return mContext;
+    }
+
+    /**
+     * Returns the log associated with the session.
+     * @return the log or null if there are none.
+     */
+    public LayoutLog getLog() {
+        if (mParams != null) {
+            return mParams.getLog();
+        }
+
+        return null;
+    }
+
+    /**
+     * Checks that the lock is owned by the current thread and that the current context is the one
+     * from this scene.
+     *
+     * @throws IllegalStateException if the current context is different than the one owned by
+     *      the scene, or if {@link #acquire(long)} was not called.
+     */
+    protected void checkLock() {
+        ReentrantLock lock = Bridge.getLock();
+        if (lock.isHeldByCurrentThread() == false) {
+            throw new IllegalStateException("scene must be acquired first. see #acquire(long)");
+        }
+        if (sCurrentContext != mContext) {
+            throw new IllegalStateException("Thread acquired a scene but is rendering a different one");
+        }
+    }
+
+    // --- FrameworkResourceIdProvider methods
+
+    @Override
+    public Integer getId(ResourceType resType, String resName) {
+        return Bridge.getResourceId(resType, resName);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
new file mode 100644
index 0000000..953d8cf
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2011 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.impl;
+
+import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN;
+
+import com.android.ide.common.rendering.api.DrawableParams;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.Result;
+import com.android.ide.common.rendering.api.Result.Status;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.BridgeWindow;
+import com.android.layoutlib.bridge.android.BridgeWindowSession;
+import com.android.resources.ResourceType;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap_Delegate;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.view.View;
+import android.view.View.AttachInfo;
+import android.view.View.MeasureSpec;
+import android.widget.FrameLayout;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+
+/**
+ * Action to render a given Drawable provided through {@link DrawableParams#getDrawable()}.
+ *
+ * The class only provides a simple {@link #render()} method, but the full life-cycle of the
+ * action must be respected.
+ *
+ * @see RenderAction
+ *
+ */
+public class RenderDrawable extends RenderAction<DrawableParams> {
+
+    public RenderDrawable(DrawableParams params) {
+        super(new DrawableParams(params));
+    }
+
+    public Result render() {
+        checkLock();
+        try {
+            // get the drawable resource value
+            DrawableParams params = getParams();
+            ResourceValue drawableResource = params.getDrawable();
+
+            // resolve it
+            BridgeContext context = getContext();
+            drawableResource = context.getRenderResources().resolveResValue(drawableResource);
+
+            if (drawableResource == null ||
+                    drawableResource.getResourceType() != ResourceType.DRAWABLE) {
+                return Status.ERROR_NOT_A_DRAWABLE.createResult();
+            }
+
+            // create a simple FrameLayout
+            FrameLayout content = new FrameLayout(context);
+
+            // get the actual Drawable object to draw
+            Drawable d = ResourceHelper.getDrawable(drawableResource, context);
+            content.setBackgroundDrawable(d);
+
+            // set the AttachInfo on the root view.
+            AttachInfo info = new AttachInfo(new BridgeWindowSession(), new BridgeWindow(),
+                    new Handler(), null);
+            info.mHasWindowFocus = true;
+            info.mWindowVisibility = View.VISIBLE;
+            info.mInTouchMode = false; // this is so that we can display selections.
+            info.mHardwareAccelerated = false;
+            content.dispatchAttachedToWindow(info, 0);
+
+
+            // measure
+            int w = params.getScreenWidth();
+            int h = params.getScreenHeight();
+            int w_spec = MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY);
+            int h_spec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY);
+            content.measure(w_spec, h_spec);
+
+            // now do the layout.
+            content.layout(0, 0, w, h);
+
+            // preDraw setup
+            content.mAttachInfo.mTreeObserver.dispatchOnPreDraw();
+
+            // draw into a new image
+            BufferedImage image = getImage(w, h);
+
+            // create an Android bitmap around the BufferedImage
+            Bitmap bitmap = Bitmap_Delegate.createBitmap(image,
+                    true /*isMutable*/, params.getDensity());
+
+            // create a Canvas around the Android bitmap
+            Canvas canvas = new Canvas(bitmap);
+            canvas.setDensity(params.getDensity().getDpiValue());
+
+            // and draw
+            content.draw(canvas);
+
+            return Status.SUCCESS.createResult(image);
+        } catch (IOException e) {
+            return ERROR_UNKNOWN.createResult(e.getMessage(), e);
+        }
+    }
+
+    protected BufferedImage getImage(int w, int h) {
+        BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
+        Graphics2D gc = image.createGraphics();
+        gc.setComposite(AlphaComposite.Src);
+
+        gc.setColor(new Color(0x00000000, true));
+        gc.fillRect(0, 0, w, h);
+
+        // done
+        gc.dispose();
+
+        return image;
+    }
+
+}
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
new file mode 100644
index 0000000..2fd58e4
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -0,0 +1,1223 @@
+/*
+ * Copyright (C) 2010 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.impl;
+
+import static com.android.ide.common.rendering.api.Result.Status.ERROR_ANIM_NOT_FOUND;
+import static com.android.ide.common.rendering.api.Result.Status.ERROR_INFLATION;
+import static com.android.ide.common.rendering.api.Result.Status.ERROR_NOT_INFLATED;
+import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN;
+import static com.android.ide.common.rendering.api.Result.Status.ERROR_VIEWGROUP_NO_CHILDREN;
+import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
+
+import com.android.ide.common.rendering.api.IAnimationListener;
+import com.android.ide.common.rendering.api.ILayoutPullParser;
+import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.RenderParams;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.Result;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.ide.common.rendering.api.ViewInfo;
+import com.android.ide.common.rendering.api.Result.Status;
+import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
+import com.android.internal.util.XmlUtils;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.BridgeInflater;
+import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
+import com.android.layoutlib.bridge.android.BridgeWindow;
+import com.android.layoutlib.bridge.android.BridgeWindowSession;
+import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.layoutlib.bridge.bars.FakeActionBar;
+import com.android.layoutlib.bridge.bars.PhoneSystemBar;
+import com.android.layoutlib.bridge.bars.TabletSystemBar;
+import com.android.layoutlib.bridge.bars.TitleBar;
+import com.android.resources.ResourceType;
+import com.android.resources.ScreenSize;
+import com.android.util.Pair;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.LayoutTransition;
+import android.animation.LayoutTransition.TransitionListener;
+import android.app.Fragment_Delegate;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap_Delegate;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.AttachInfo;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.QuickContactBadge;
+import android.widget.TabHost;
+import android.widget.TabWidget;
+import android.widget.TabHost.TabSpec;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Class implementing the render session.
+ *
+ * A session is a stateful representation of a layout file. It is initialized with data coming
+ * through the {@link Bridge} API to inflate the layout. Further actions and rendering can then
+ * be done on the layout.
+ *
+ */
+public class RenderSessionImpl extends RenderAction<SessionParams> {
+
+    private static final int DEFAULT_TITLE_BAR_HEIGHT = 25;
+    private static final int DEFAULT_STATUS_BAR_HEIGHT = 25;
+
+    // scene state
+    private RenderSession mScene;
+    private BridgeXmlBlockParser mBlockParser;
+    private BridgeInflater mInflater;
+    private ResourceValue mWindowBackground;
+    private ViewGroup mViewRoot;
+    private FrameLayout mContentRoot;
+    private Canvas mCanvas;
+    private int mMeasuredScreenWidth = -1;
+    private int mMeasuredScreenHeight = -1;
+    private boolean mIsAlphaChannelImage;
+    private boolean mWindowIsFloating;
+
+    private int mStatusBarSize;
+    private int mSystemBarSize;
+    private int mTitleBarSize;
+    private int mActionBarSize;
+
+
+    // information being returned through the API
+    private BufferedImage mImage;
+    private List<ViewInfo> mViewInfoList;
+
+    private static final class PostInflateException extends Exception {
+        private static final long serialVersionUID = 1L;
+
+        public PostInflateException(String message) {
+            super(message);
+        }
+    }
+
+    /**
+     * Creates a layout scene with all the information coming from the layout bridge API.
+     * <p>
+     * This <b>must</b> be followed by a call to {@link RenderSessionImpl#init()}, which act as a
+     * call to {@link RenderSessionImpl#acquire(long)}
+     *
+     * @see LayoutBridge#createScene(com.android.layoutlib.api.SceneParams)
+     */
+    public RenderSessionImpl(SessionParams params) {
+        super(new SessionParams(params));
+    }
+
+    /**
+     * Initializes and acquires the scene, creating various Android objects such as context,
+     * inflater, and parser.
+     *
+     * @param timeout the time to wait if another rendering is happening.
+     *
+     * @return whether the scene was prepared
+     *
+     * @see #acquire(long)
+     * @see #release()
+     */
+    @Override
+    public Result init(long timeout) {
+        Result result = super.init(timeout);
+        if (result.isSuccess() == false) {
+            return result;
+        }
+
+        SessionParams params = getParams();
+        BridgeContext context = getContext();
+
+        RenderResources resources = getParams().getResources();
+        DisplayMetrics metrics = getContext().getMetrics();
+
+        // use default of true in case it's not found to use alpha by default
+        mIsAlphaChannelImage  = getBooleanThemeValue(resources,
+                "windowIsFloating", true /*defaultValue*/);
+
+        mWindowIsFloating = getBooleanThemeValue(resources, "windowIsFloating",
+                true /*defaultValue*/);
+
+        findBackground(resources);
+        findStatusBar(resources, metrics);
+        findActionBar(resources, metrics);
+        findSystemBar(resources, metrics);
+
+        // build the inflater and parser.
+        mInflater = new BridgeInflater(context, params.getProjectCallback());
+        context.setBridgeInflater(mInflater);
+        mInflater.setFactory2(context);
+
+        mBlockParser = new BridgeXmlBlockParser(
+                params.getLayoutDescription(), context, false /* platformResourceFlag */);
+
+        return SUCCESS.createResult();
+    }
+
+    /**
+     * Inflates the layout.
+     * <p>
+     * {@link #acquire(long)} must have been called before this.
+     *
+     * @throws IllegalStateException if the current context is different than the one owned by
+     *      the scene, or if {@link #init(long)} was not called.
+     */
+    public Result inflate() {
+        checkLock();
+
+        try {
+
+            SessionParams params = getParams();
+            BridgeContext context = getContext();
+
+            // the view group that receives the window background.
+            ViewGroup backgroundView = null;
+
+            if (mWindowIsFloating || params.isForceNoDecor()) {
+                backgroundView = mViewRoot = mContentRoot = new FrameLayout(context);
+            } else {
+                /*
+                 * we're creating the following layout
+                 *
+                   +-------------------------------------------------+
+                   | System bar (only in phone UI)                   |
+                   +-------------------------------------------------+
+                   | (Layout with background drawable)               |
+                   | +---------------------------------------------+ |
+                   | | Title/Action bar (optional)                 | |
+                   | +---------------------------------------------+ |
+                   | | Content, vertical extending                 | |
+                   | |                                             | |
+                   | +---------------------------------------------+ |
+                   +-------------------------------------------------+
+                   | System bar (only in tablet UI)                  |
+                   +-------------------------------------------------+
+
+                 */
+
+                LinearLayout topLayout = new LinearLayout(context);
+                mViewRoot = topLayout;
+                topLayout.setOrientation(LinearLayout.VERTICAL);
+
+                if (mStatusBarSize > 0) {
+                    // system bar
+                    try {
+                        PhoneSystemBar systemBar = new PhoneSystemBar(context,
+                                params.getDensity());
+                        systemBar.setLayoutParams(
+                                new LinearLayout.LayoutParams(
+                                        LayoutParams.MATCH_PARENT, mStatusBarSize));
+                        topLayout.addView(systemBar);
+                    } catch (XmlPullParserException e) {
+
+                    }
+                }
+
+                LinearLayout backgroundLayout = new LinearLayout(context);
+                backgroundView = backgroundLayout;
+                backgroundLayout.setOrientation(LinearLayout.VERTICAL);
+                LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+                        LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+                layoutParams.weight = 1;
+                backgroundLayout.setLayoutParams(layoutParams);
+                topLayout.addView(backgroundLayout);
+
+
+                // if the theme says no title/action bar, then the size will be 0
+                if (mActionBarSize > 0) {
+                    try {
+                        FakeActionBar actionBar = new FakeActionBar(context,
+                                params.getDensity(),
+                                params.getAppLabel(), params.getAppIcon());
+                        actionBar.setLayoutParams(
+                                new LinearLayout.LayoutParams(
+                                        LayoutParams.MATCH_PARENT, mActionBarSize));
+                        backgroundLayout.addView(actionBar);
+                    } catch (XmlPullParserException e) {
+
+                    }
+                } else if (mTitleBarSize > 0) {
+                    try {
+                        TitleBar titleBar = new TitleBar(context,
+                                params.getDensity(), params.getAppLabel());
+                        titleBar.setLayoutParams(
+                                new LinearLayout.LayoutParams(
+                                        LayoutParams.MATCH_PARENT, mTitleBarSize));
+                        backgroundLayout.addView(titleBar);
+                    } catch (XmlPullParserException e) {
+
+                    }
+                }
+
+
+                // content frame
+                mContentRoot = new FrameLayout(context);
+                layoutParams = new LinearLayout.LayoutParams(
+                        LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+                layoutParams.weight = 1;
+                mContentRoot.setLayoutParams(layoutParams);
+                backgroundLayout.addView(mContentRoot);
+
+                if (mSystemBarSize > 0) {
+                    // system bar
+                    try {
+                        TabletSystemBar systemBar = new TabletSystemBar(context,
+                                params.getDensity());
+                        systemBar.setLayoutParams(
+                                new LinearLayout.LayoutParams(
+                                        LayoutParams.MATCH_PARENT, mSystemBarSize));
+                        topLayout.addView(systemBar);
+                    } catch (XmlPullParserException e) {
+
+                    }
+                }
+            }
+
+
+            // Sets the project callback (custom view loader) to the fragment delegate so that
+            // it can instantiate the custom Fragment.
+            Fragment_Delegate.setProjectCallback(params.getProjectCallback());
+
+            View view = mInflater.inflate(mBlockParser, mContentRoot);
+
+            Fragment_Delegate.setProjectCallback(null);
+
+            // set the AttachInfo on the root view.
+            AttachInfo info = new AttachInfo(new BridgeWindowSession(), new BridgeWindow(),
+                    new Handler(), null);
+            info.mHasWindowFocus = true;
+            info.mWindowVisibility = View.VISIBLE;
+            info.mInTouchMode = false; // this is so that we can display selections.
+            info.mHardwareAccelerated = false;
+            mViewRoot.dispatchAttachedToWindow(info, 0);
+
+            // post-inflate process. For now this supports TabHost/TabWidget
+            postInflateProcess(view, params.getProjectCallback());
+
+            // get the background drawable
+            if (mWindowBackground != null && backgroundView != null) {
+                Drawable d = ResourceHelper.getDrawable(mWindowBackground, context);
+                backgroundView.setBackgroundDrawable(d);
+            }
+
+            return SUCCESS.createResult();
+        } catch (PostInflateException e) {
+            return ERROR_INFLATION.createResult(e.getMessage(), e);
+        } catch (Throwable e) {
+            // get the real cause of the exception.
+            Throwable t = e;
+            while (t.getCause() != null) {
+                t = t.getCause();
+            }
+
+            return ERROR_INFLATION.createResult(t.getMessage(), t);
+        }
+    }
+
+    /**
+     * Renders the scene.
+     * <p>
+     * {@link #acquire(long)} must have been called before this.
+     *
+     * @param freshRender whether the render is a new one and should erase the existing bitmap (in
+     *      the case where bitmaps are reused). This is typically needed when not playing
+     *      animations.)
+     *
+     * @throws IllegalStateException if the current context is different than the one owned by
+     *      the scene, or if {@link #acquire(long)} was not called.
+     *
+     * @see RenderParams#getRenderingMode()
+     * @see RenderSession#render(long)
+     */
+    public Result render(boolean freshRender) {
+        checkLock();
+
+        SessionParams params = getParams();
+
+        try {
+            if (mViewRoot == null) {
+                return ERROR_NOT_INFLATED.createResult();
+            }
+            // measure the views
+            int w_spec, h_spec;
+
+            RenderingMode renderingMode = params.getRenderingMode();
+
+            // only do the screen measure when needed.
+            boolean newRenderSize = false;
+            if (mMeasuredScreenWidth == -1) {
+                newRenderSize = true;
+                mMeasuredScreenWidth = params.getScreenWidth();
+                mMeasuredScreenHeight = params.getScreenHeight();
+
+                if (renderingMode != RenderingMode.NORMAL) {
+                    // measure the full size needed by the layout.
+                    w_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenWidth,
+                            renderingMode.isHorizExpand() ?
+                                    MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
+                                    : MeasureSpec.EXACTLY);
+                    h_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenHeight,
+                            renderingMode.isVertExpand() ?
+                                    MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
+                                    : MeasureSpec.EXACTLY);
+                    mViewRoot.measure(w_spec, h_spec);
+
+                    if (renderingMode.isHorizExpand()) {
+                        int neededWidth = mViewRoot.getChildAt(0).getMeasuredWidth();
+                        if (neededWidth > mMeasuredScreenWidth) {
+                            mMeasuredScreenWidth = neededWidth;
+                        }
+                    }
+
+                    if (renderingMode.isVertExpand()) {
+                        int neededHeight = mViewRoot.getChildAt(0).getMeasuredHeight();
+                        if (neededHeight > mMeasuredScreenHeight) {
+                            mMeasuredScreenHeight = neededHeight;
+                        }
+                    }
+                }
+            }
+
+            // remeasure with the size we need
+            // This must always be done before the call to layout
+            w_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenWidth, MeasureSpec.EXACTLY);
+            h_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenHeight, MeasureSpec.EXACTLY);
+            mViewRoot.measure(w_spec, h_spec);
+
+            // now do the layout.
+            mViewRoot.layout(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
+
+            if (params.isLayoutOnly()) {
+                // delete the canvas and image to reset them on the next full rendering
+                mImage = null;
+                mCanvas = null;
+            } else {
+                mViewRoot.mAttachInfo.mTreeObserver.dispatchOnPreDraw();
+
+                // draw the views
+                // create the BufferedImage into which the layout will be rendered.
+                boolean newImage = false;
+                if (newRenderSize || mCanvas == null) {
+                    if (params.getImageFactory() != null) {
+                        mImage = params.getImageFactory().getImage(
+                                mMeasuredScreenWidth,
+                                mMeasuredScreenHeight);
+                    } else {
+                        mImage = new BufferedImage(
+                                mMeasuredScreenWidth,
+                                mMeasuredScreenHeight,
+                                BufferedImage.TYPE_INT_ARGB);
+                        newImage = true;
+                    }
+
+                    if (params.isBgColorOverridden()) {
+                        // since we override the content, it's the same as if it was a new image.
+                        newImage = true;
+                        Graphics2D gc = mImage.createGraphics();
+                        gc.setColor(new Color(params.getOverrideBgColor(), true));
+                        gc.setComposite(AlphaComposite.Src);
+                        gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
+                        gc.dispose();
+                    }
+
+                    // create an Android bitmap around the BufferedImage
+                    Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
+                            true /*isMutable*/, params.getDensity());
+
+                    // create a Canvas around the Android bitmap
+                    mCanvas = new Canvas(bitmap);
+                    mCanvas.setDensity(params.getDensity().getDpiValue());
+                }
+
+                if (freshRender && newImage == false) {
+                    Graphics2D gc = mImage.createGraphics();
+                    gc.setComposite(AlphaComposite.Src);
+
+                    gc.setColor(new Color(0x00000000, true));
+                    gc.fillRect(0, 0,
+                            mMeasuredScreenWidth, mMeasuredScreenHeight);
+
+                    // done
+                    gc.dispose();
+                }
+
+                mViewRoot.draw(mCanvas);
+            }
+
+            mViewInfoList = startVisitingViews(mViewRoot, 0);
+
+            // success!
+            return SUCCESS.createResult();
+        } catch (Throwable e) {
+            // get the real cause of the exception.
+            Throwable t = e;
+            while (t.getCause() != null) {
+                t = t.getCause();
+            }
+
+            return ERROR_UNKNOWN.createResult(t.getMessage(), t);
+        }
+    }
+
+    /**
+     * Animate an object
+     * <p>
+     * {@link #acquire(long)} must have been called before this.
+     *
+     * @throws IllegalStateException if the current context is different than the one owned by
+     *      the scene, or if {@link #acquire(long)} was not called.
+     *
+     * @see RenderSession#animate(Object, String, boolean, IAnimationListener)
+     */
+    public Result animate(Object targetObject, String animationName,
+            boolean isFrameworkAnimation, IAnimationListener listener) {
+        checkLock();
+
+        BridgeContext context = getContext();
+
+        // find the animation file.
+        ResourceValue animationResource = null;
+        int animationId = 0;
+        if (isFrameworkAnimation) {
+            animationResource = context.getRenderResources().getFrameworkResource(
+                    ResourceType.ANIMATOR, animationName);
+            if (animationResource != null) {
+                animationId = Bridge.getResourceId(ResourceType.ANIMATOR, animationName);
+            }
+        } else {
+            animationResource = context.getRenderResources().getProjectResource(
+                    ResourceType.ANIMATOR, animationName);
+            if (animationResource != null) {
+                animationId = context.getProjectCallback().getResourceId(
+                        ResourceType.ANIMATOR, animationName);
+            }
+        }
+
+        if (animationResource != null) {
+            try {
+                Animator anim = AnimatorInflater.loadAnimator(context, animationId);
+                if (anim != null) {
+                    anim.setTarget(targetObject);
+
+                    new PlayAnimationThread(anim, this, animationName, listener).start();
+
+                    return SUCCESS.createResult();
+                }
+            } catch (Exception e) {
+                // get the real cause of the exception.
+                Throwable t = e;
+                while (t.getCause() != null) {
+                    t = t.getCause();
+                }
+
+                return ERROR_UNKNOWN.createResult(t.getMessage(), t);
+            }
+        }
+
+        return ERROR_ANIM_NOT_FOUND.createResult();
+    }
+
+    /**
+     * Insert a new child into an existing parent.
+     * <p>
+     * {@link #acquire(long)} must have been called before this.
+     *
+     * @throws IllegalStateException if the current context is different than the one owned by
+     *      the scene, or if {@link #acquire(long)} was not called.
+     *
+     * @see RenderSession#insertChild(Object, ILayoutPullParser, int, IAnimationListener)
+     */
+    public Result insertChild(final ViewGroup parentView, ILayoutPullParser childXml,
+            final int index, IAnimationListener listener) {
+        checkLock();
+
+        BridgeContext context = getContext();
+
+        // create a block parser for the XML
+        BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
+                childXml, context, false /* platformResourceFlag */);
+
+        // inflate the child without adding it to the root since we want to control where it'll
+        // get added. We do pass the parentView however to ensure that the layoutParams will
+        // be created correctly.
+        final View child = mInflater.inflate(blockParser, parentView, false /*attachToRoot*/);
+        blockParser.ensurePopped();
+
+        invalidateRenderingSize();
+
+        if (listener != null) {
+            new AnimationThread(this, "insertChild", listener) {
+
+                @Override
+                public Result preAnimation() {
+                    parentView.setLayoutTransition(new LayoutTransition());
+                    return addView(parentView, child, index);
+                }
+
+                @Override
+                public void postAnimation() {
+                    parentView.setLayoutTransition(null);
+                }
+            }.start();
+
+            // always return success since the real status will come through the listener.
+            return SUCCESS.createResult(child);
+        }
+
+        // add it to the parentView in the correct location
+        Result result = addView(parentView, child, index);
+        if (result.isSuccess() == false) {
+            return result;
+        }
+
+        result = render(false /*freshRender*/);
+        if (result.isSuccess()) {
+            result = result.getCopyWithData(child);
+        }
+
+        return result;
+    }
+
+    /**
+     * Adds a given view to a given parent at a given index.
+     *
+     * @param parent the parent to receive the view
+     * @param view the view to add to the parent
+     * @param index the index where to do the add.
+     *
+     * @return a Result with {@link Status#SUCCESS} or
+     *     {@link Status#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support
+     *     adding views.
+     */
+    private Result addView(ViewGroup parent, View view, int index) {
+        try {
+            parent.addView(view, index);
+            return SUCCESS.createResult();
+        } catch (UnsupportedOperationException e) {
+            // looks like this is a view class that doesn't support children manipulation!
+            return ERROR_VIEWGROUP_NO_CHILDREN.createResult();
+        }
+    }
+
+    /**
+     * Moves a view to a new parent at a given location
+     * <p>
+     * {@link #acquire(long)} must have been called before this.
+     *
+     * @throws IllegalStateException if the current context is different than the one owned by
+     *      the scene, or if {@link #acquire(long)} was not called.
+     *
+     * @see RenderSession#moveChild(Object, Object, int, Map, IAnimationListener)
+     */
+    public Result moveChild(final ViewGroup newParentView, final View childView, final int index,
+            Map<String, String> layoutParamsMap, final IAnimationListener listener) {
+        checkLock();
+
+        invalidateRenderingSize();
+
+        LayoutParams layoutParams = null;
+        if (layoutParamsMap != null) {
+            // need to create a new LayoutParams object for the new parent.
+            layoutParams = newParentView.generateLayoutParams(
+                    new BridgeLayoutParamsMapAttributes(layoutParamsMap));
+        }
+
+        // get the current parent of the view that needs to be moved.
+        final ViewGroup previousParent = (ViewGroup) childView.getParent();
+
+        if (listener != null) {
+            final LayoutParams params = layoutParams;
+
+            // there is no support for animating views across layouts, so in case the new and old
+            // parent views are different we fake the animation through a no animation thread.
+            if (previousParent != newParentView) {
+                new Thread("not animated moveChild") {
+                    @Override
+                    public void run() {
+                        Result result = moveView(previousParent, newParentView, childView, index,
+                                params);
+                        if (result.isSuccess() == false) {
+                            listener.done(result);
+                        }
+
+                        // ready to do the work, acquire the scene.
+                        result = acquire(250);
+                        if (result.isSuccess() == false) {
+                            listener.done(result);
+                            return;
+                        }
+
+                        try {
+                            result = render(false /*freshRender*/);
+                            if (result.isSuccess()) {
+                                listener.onNewFrame(RenderSessionImpl.this.getSession());
+                            }
+                        } finally {
+                            release();
+                        }
+
+                        listener.done(result);
+                    }
+                }.start();
+            } else {
+                new AnimationThread(this, "moveChild", listener) {
+
+                    @Override
+                    public Result preAnimation() {
+                        // set up the transition for the parent.
+                        LayoutTransition transition = new LayoutTransition();
+                        previousParent.setLayoutTransition(transition);
+
+                        // tweak the animation durations and start delays (to match the duration of
+                        // animation playing just before).
+                        // Note: Cannot user Animation.setDuration() directly. Have to set it
+                        // on the LayoutTransition.
+                        transition.setDuration(LayoutTransition.DISAPPEARING, 100);
+                        // CHANGE_DISAPPEARING plays after DISAPPEARING
+                        transition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 100);
+
+                        transition.setDuration(LayoutTransition.CHANGE_DISAPPEARING, 100);
+
+                        transition.setDuration(LayoutTransition.CHANGE_APPEARING, 100);
+                        // CHANGE_APPEARING plays after CHANGE_APPEARING
+                        transition.setStartDelay(LayoutTransition.APPEARING, 100);
+
+                        transition.setDuration(LayoutTransition.APPEARING, 100);
+
+                        return moveView(previousParent, newParentView, childView, index, params);
+                    }
+
+                    @Override
+                    public void postAnimation() {
+                        previousParent.setLayoutTransition(null);
+                        newParentView.setLayoutTransition(null);
+                    }
+                }.start();
+            }
+
+            // always return success since the real status will come through the listener.
+            return SUCCESS.createResult(layoutParams);
+        }
+
+        Result result = moveView(previousParent, newParentView, childView, index, layoutParams);
+        if (result.isSuccess() == false) {
+            return result;
+        }
+
+        result = render(false /*freshRender*/);
+        if (layoutParams != null && result.isSuccess()) {
+            result = result.getCopyWithData(layoutParams);
+        }
+
+        return result;
+    }
+
+    /**
+     * Moves a View from its current parent to a new given parent at a new given location, with
+     * an optional new {@link LayoutParams} instance
+     *
+     * @param previousParent the previous parent, still owning the child at the time of the call.
+     * @param newParent the new parent
+     * @param movedView the view to move
+     * @param index the new location in the new parent
+     * @param params an option (can be null) {@link LayoutParams} instance.
+     *
+     * @return a Result with {@link Status#SUCCESS} or
+     *     {@link Status#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support
+     *     adding views.
+     */
+    private Result moveView(ViewGroup previousParent, final ViewGroup newParent,
+            final View movedView, final int index, final LayoutParams params) {
+        try {
+            // check if there is a transition on the previousParent.
+            LayoutTransition previousTransition = previousParent.getLayoutTransition();
+            if (previousTransition != null) {
+                // in this case there is an animation. This means we have to wait for the child's
+                // parent reference to be null'ed out so that we can add it to the new parent.
+                // It is technically removed right before the DISAPPEARING animation is done (if
+                // the animation of this type is not null, otherwise it's after which is impossible
+                // to handle).
+                // Because there is no move animation, if the new parent is the same as the old
+                // parent, we need to wait until the CHANGE_DISAPPEARING animation is done before
+                // adding the child or the child will appear in its new location before the
+                // other children have made room for it.
+
+                // add a listener to the transition to be notified of the actual removal.
+                previousTransition.addTransitionListener(new TransitionListener() {
+                    private int mChangeDisappearingCount = 0;
+
+                    public void startTransition(LayoutTransition transition, ViewGroup container,
+                            View view, int transitionType) {
+                        if (transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
+                            mChangeDisappearingCount++;
+                        }
+                    }
+
+                    public void endTransition(LayoutTransition transition, ViewGroup container,
+                            View view, int transitionType) {
+                        if (transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
+                            mChangeDisappearingCount--;
+                        }
+
+                        if (transitionType == LayoutTransition.CHANGE_DISAPPEARING &&
+                                mChangeDisappearingCount == 0) {
+                            // add it to the parentView in the correct location
+                            if (params != null) {
+                                newParent.addView(movedView, index, params);
+                            } else {
+                                newParent.addView(movedView, index);
+                            }
+                        }
+                    }
+                });
+
+                // remove the view from the current parent.
+                previousParent.removeView(movedView);
+
+                // and return since adding the view to the new parent is done in the listener.
+                return SUCCESS.createResult();
+            } else {
+                // standard code with no animation. pretty simple.
+                previousParent.removeView(movedView);
+
+                // add it to the parentView in the correct location
+                if (params != null) {
+                    newParent.addView(movedView, index, params);
+                } else {
+                    newParent.addView(movedView, index);
+                }
+
+                return SUCCESS.createResult();
+            }
+        } catch (UnsupportedOperationException e) {
+            // looks like this is a view class that doesn't support children manipulation!
+            return ERROR_VIEWGROUP_NO_CHILDREN.createResult();
+        }
+    }
+
+    /**
+     * Removes a child from its current parent.
+     * <p>
+     * {@link #acquire(long)} must have been called before this.
+     *
+     * @throws IllegalStateException if the current context is different than the one owned by
+     *      the scene, or if {@link #acquire(long)} was not called.
+     *
+     * @see RenderSession#removeChild(Object, IAnimationListener)
+     */
+    public Result removeChild(final View childView, IAnimationListener listener) {
+        checkLock();
+
+        invalidateRenderingSize();
+
+        final ViewGroup parent = (ViewGroup) childView.getParent();
+
+        if (listener != null) {
+            new AnimationThread(this, "moveChild", listener) {
+
+                @Override
+                public Result preAnimation() {
+                    parent.setLayoutTransition(new LayoutTransition());
+                    return removeView(parent, childView);
+                }
+
+                @Override
+                public void postAnimation() {
+                    parent.setLayoutTransition(null);
+                }
+            }.start();
+
+            // always return success since the real status will come through the listener.
+            return SUCCESS.createResult();
+        }
+
+        Result result = removeView(parent, childView);
+        if (result.isSuccess() == false) {
+            return result;
+        }
+
+        return render(false /*freshRender*/);
+    }
+
+    /**
+     * Removes a given view from its current parent.
+     *
+     * @param view the view to remove from its parent
+     *
+     * @return a Result with {@link Status#SUCCESS} or
+     *     {@link Status#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support
+     *     adding views.
+     */
+    private Result removeView(ViewGroup parent, View view) {
+        try {
+            parent.removeView(view);
+            return SUCCESS.createResult();
+        } catch (UnsupportedOperationException e) {
+            // looks like this is a view class that doesn't support children manipulation!
+            return ERROR_VIEWGROUP_NO_CHILDREN.createResult();
+        }
+    }
+
+
+    private void findBackground(RenderResources resources) {
+        if (getParams().isBgColorOverridden() == false) {
+            mWindowBackground = resources.findItemInTheme("windowBackground");
+            if (mWindowBackground != null) {
+                mWindowBackground = resources.resolveResValue(mWindowBackground);
+            }
+        }
+    }
+
+    private boolean isTabletUi() {
+        return getParams().getConfigScreenSize() == ScreenSize.XLARGE;
+    }
+
+    private void findStatusBar(RenderResources resources, DisplayMetrics metrics) {
+        if (isTabletUi() == false) {
+            boolean windowFullscreen = getBooleanThemeValue(resources,
+                    "windowFullscreen", false /*defaultValue*/);
+
+            if (windowFullscreen == false && mWindowIsFloating == false) {
+                // default value
+                mStatusBarSize = DEFAULT_STATUS_BAR_HEIGHT;
+
+                // get the real value
+                ResourceValue value = resources.getFrameworkResource(ResourceType.DIMEN,
+                        "status_bar_height");
+
+                if (value != null) {
+                    TypedValue typedValue = ResourceHelper.getValue(value.getValue());
+                    if (typedValue != null) {
+                        // compute the pixel value based on the display metrics
+                        mStatusBarSize = (int)typedValue.getDimension(metrics);
+                    }
+                }
+            }
+        }
+    }
+
+    private void findActionBar(RenderResources resources, DisplayMetrics metrics) {
+        if (mWindowIsFloating) {
+            return;
+        }
+
+        boolean windowActionBar = getBooleanThemeValue(resources,
+                "windowActionBar", true /*defaultValue*/);
+
+        // if there's a value and it's false (default is true)
+        if (windowActionBar) {
+
+            // default size of the window title bar
+            mActionBarSize = DEFAULT_TITLE_BAR_HEIGHT;
+
+            // get value from the theme.
+            ResourceValue value = resources.findItemInTheme("actionBarSize");
+
+            // resolve it
+            value = resources.resolveResValue(value);
+
+            if (value != null) {
+                // get the numerical value, if available
+                TypedValue typedValue = ResourceHelper.getValue(value.getValue());
+                if (typedValue != null) {
+                    // compute the pixel value based on the display metrics
+                    mActionBarSize = (int)typedValue.getDimension(metrics);
+                }
+            }
+        } else {
+            // action bar overrides title bar so only look for this one if action bar is hidden
+            boolean windowNoTitle = getBooleanThemeValue(resources,
+                    "windowNoTitle", false /*defaultValue*/);
+
+            if (windowNoTitle == false) {
+
+                // default size of the window title bar
+                mTitleBarSize = DEFAULT_TITLE_BAR_HEIGHT;
+
+                // get value from the theme.
+                ResourceValue value = resources.findItemInTheme("windowTitleSize");
+
+                // resolve it
+                value = resources.resolveResValue(value);
+
+                if (value != null) {
+                    // get the numerical value, if available
+                    TypedValue typedValue = ResourceHelper.getValue(value.getValue());
+                    if (typedValue != null) {
+                        // compute the pixel value based on the display metrics
+                        mTitleBarSize = (int)typedValue.getDimension(metrics);
+                    }
+                }
+            }
+
+        }
+    }
+
+    private void findSystemBar(RenderResources resources, DisplayMetrics metrics) {
+        if (isTabletUi() && mWindowIsFloating == false) {
+
+            // default value
+            mSystemBarSize = 48; // ??
+
+            // get the real value
+            ResourceValue value = resources.getFrameworkResource(ResourceType.DIMEN,
+                    "status_bar_height");
+
+            if (value != null) {
+                TypedValue typedValue = ResourceHelper.getValue(value.getValue());
+                if (typedValue != null) {
+                    // compute the pixel value based on the display metrics
+                    mSystemBarSize = (int)typedValue.getDimension(metrics);
+                }
+            }
+        }
+    }
+
+    private boolean getBooleanThemeValue(RenderResources resources,
+            String name, boolean defaultValue) {
+
+        // get the title bar flag from the current theme.
+        ResourceValue value = resources.findItemInTheme(name);
+
+        // because it may reference something else, we resolve it.
+        value = resources.resolveResValue(value);
+
+        // if there's no value, return the default.
+        if (value == null || value.getValue() == null) {
+            return defaultValue;
+        }
+
+        return XmlUtils.convertValueToBoolean(value.getValue(), defaultValue);
+    }
+
+    /**
+     * Post process on a view hierachy that was just inflated.
+     * <p/>At the moment this only support TabHost: If {@link TabHost} is detected, look for the
+     * {@link TabWidget}, and the corresponding {@link FrameLayout} and make new tabs automatically
+     * based on the content of the {@link FrameLayout}.
+     * @param view the root view to process.
+     * @param projectCallback callback to the project.
+     */
+    private void postInflateProcess(View view, IProjectCallback projectCallback)
+            throws PostInflateException {
+        if (view instanceof TabHost) {
+            setupTabHost((TabHost)view, projectCallback);
+        } else if (view instanceof QuickContactBadge) {
+            QuickContactBadge badge = (QuickContactBadge) view;
+            badge.setImageToDefault();
+        } else if (view instanceof ViewGroup) {
+            ViewGroup group = (ViewGroup)view;
+            final int count = group.getChildCount();
+            for (int c = 0 ; c < count ; c++) {
+                View child = group.getChildAt(c);
+                postInflateProcess(child, projectCallback);
+            }
+        }
+    }
+
+    /**
+     * Sets up a {@link TabHost} object.
+     * @param tabHost the TabHost to setup.
+     * @param projectCallback The project callback object to access the project R class.
+     * @throws PostInflateException
+     */
+    private void setupTabHost(TabHost tabHost, IProjectCallback projectCallback)
+            throws PostInflateException {
+        // look for the TabWidget, and the FrameLayout. They have their own specific names
+        View v = tabHost.findViewById(android.R.id.tabs);
+
+        if (v == null) {
+            throw new PostInflateException(
+                    "TabHost requires a TabWidget with id \"android:id/tabs\".\n");
+        }
+
+        if ((v instanceof TabWidget) == false) {
+            throw new PostInflateException(String.format(
+                    "TabHost requires a TabWidget with id \"android:id/tabs\".\n" +
+                    "View found with id 'tabs' is '%s'", v.getClass().getCanonicalName()));
+        }
+
+        v = tabHost.findViewById(android.R.id.tabcontent);
+
+        if (v == null) {
+            // TODO: see if we can fake tabs even without the FrameLayout (same below when the framelayout is empty)
+            throw new PostInflateException(
+                    "TabHost requires a FrameLayout with id \"android:id/tabcontent\".");
+        }
+
+        if ((v instanceof FrameLayout) == false) {
+            throw new PostInflateException(String.format(
+                    "TabHost requires a FrameLayout with id \"android:id/tabcontent\".\n" +
+                    "View found with id 'tabcontent' is '%s'", v.getClass().getCanonicalName()));
+        }
+
+        FrameLayout content = (FrameLayout)v;
+
+        // now process the content of the framelayout and dynamically create tabs for it.
+        final int count = content.getChildCount();
+
+        // this must be called before addTab() so that the TabHost searches its TabWidget
+        // and FrameLayout.
+        tabHost.setup();
+
+        if (count == 0) {
+            // Create a dummy child to get a single tab
+            TabSpec spec = tabHost.newTabSpec("tag").setIndicator("Tab Label",
+                    tabHost.getResources().getDrawable(android.R.drawable.ic_menu_info_details))
+                    .setContent(new TabHost.TabContentFactory() {
+                        public View createTabContent(String tag) {
+                            return new LinearLayout(getContext());
+                        }
+                    });
+            tabHost.addTab(spec);
+            return;
+        } else {
+            // for each child of the framelayout, add a new TabSpec
+            for (int i = 0 ; i < count ; i++) {
+                View child = content.getChildAt(i);
+                String tabSpec = String.format("tab_spec%d", i+1);
+                int id = child.getId();
+                Pair<ResourceType, String> resource = projectCallback.resolveResourceId(id);
+                String name;
+                if (resource != null) {
+                    name = resource.getSecond();
+                } else {
+                    name = String.format("Tab %d", i+1); // default name if id is unresolved.
+                }
+                tabHost.addTab(tabHost.newTabSpec(tabSpec).setIndicator(name).setContent(id));
+            }
+        }
+    }
+
+    private List<ViewInfo> startVisitingViews(View view, int offset) {
+        if (view == null) {
+            return null;
+        }
+
+        // adjust the offset to this view.
+        offset += view.getTop();
+
+        if (view == mContentRoot) {
+            return visitAllChildren(mContentRoot, offset);
+        }
+
+        // otherwise, look for mContentRoot in the children
+        if (view instanceof ViewGroup) {
+            ViewGroup group = ((ViewGroup) view);
+
+            for (int i = 0; i < group.getChildCount(); i++) {
+                List<ViewInfo> list = startVisitingViews(group.getChildAt(i), offset);
+                if (list != null) {
+                    return list;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Visits a View and its children and generate a {@link ViewInfo} containing the
+     * bounds of all the views.
+     * @param view the root View
+     * @param offset an offset for the view bounds.
+     */
+    private ViewInfo visit(View view, int offset) {
+        if (view == null) {
+            return null;
+        }
+
+        ViewInfo result = new ViewInfo(view.getClass().getName(),
+                getContext().getViewKey(view),
+                view.getLeft(), view.getTop() + offset, view.getRight(), view.getBottom() + offset,
+                view, view.getLayoutParams());
+
+        if (view instanceof ViewGroup) {
+            ViewGroup group = ((ViewGroup) view);
+            result.setChildren(visitAllChildren(group, 0 /*offset*/));
+        }
+
+        return result;
+    }
+
+    /**
+     * Visits all the children of a given ViewGroup generate a list of {@link ViewInfo}
+     * containing the bounds of all the views.
+     * @param view the root View
+     * @param offset an offset for the view bounds.
+     */
+    private List<ViewInfo> visitAllChildren(ViewGroup viewGroup, int offset) {
+        if (viewGroup == null) {
+            return null;
+        }
+
+        List<ViewInfo> children = new ArrayList<ViewInfo>();
+        for (int i = 0; i < viewGroup.getChildCount(); i++) {
+            children.add(visit(viewGroup.getChildAt(i), offset));
+        }
+        return children;
+    }
+
+
+    private void invalidateRenderingSize() {
+        mMeasuredScreenWidth = mMeasuredScreenHeight = -1;
+    }
+
+    public BufferedImage getImage() {
+        return mImage;
+    }
+
+    public boolean isAlphaChannelImage() {
+        return mIsAlphaChannelImage;
+    }
+
+    public List<ViewInfo> getViewInfos() {
+        return mViewInfoList;
+    }
+
+    public Map<String, String> getDefaultProperties(Object viewObject) {
+        return getContext().getDefaultPropMap(viewObject);
+    }
+
+    public void setScene(RenderSession session) {
+        mScene = session;
+    }
+
+    public RenderSession getSession() {
+        return mScene;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
new file mode 100644
index 0000000..69f46e6
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2008 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.impl;
+
+import com.android.ide.common.rendering.api.DensityBasedResourceValue;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.ninepatch.NinePatch;
+import com.android.ninepatch.NinePatchChunk;
+import com.android.resources.Density;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap_Delegate;
+import android.graphics.NinePatch_Delegate;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.NinePatchDrawable;
+import android.util.TypedValue;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Helper class to provide various conversion method used in handling android resources.
+ */
+public final class ResourceHelper {
+
+    private final static Pattern sFloatPattern = Pattern.compile("(-?[0-9]+(?:\\.[0-9]+)?)(.*)");
+    private final static float[] sFloatOut = new float[1];
+
+    private final static TypedValue mValue = new TypedValue();
+
+    /**
+     * Returns the color value represented by the given string value
+     * @param value the color value
+     * @return the color as an int
+     * @throw NumberFormatException if the conversion failed.
+     */
+    public static int getColor(String value) {
+        if (value != null) {
+            if (value.startsWith("#") == false) {
+                throw new NumberFormatException(
+                        String.format("Color value '%s' must start with #", value));
+            }
+
+            value = value.substring(1);
+
+            // make sure it's not longer than 32bit
+            if (value.length() > 8) {
+                throw new NumberFormatException(String.format(
+                        "Color value '%s' is too long. Format is either" +
+                        "#AARRGGBB, #RRGGBB, #RGB, or #ARGB",
+                        value));
+            }
+
+            if (value.length() == 3) { // RGB format
+                char[] color = new char[8];
+                color[0] = color[1] = 'F';
+                color[2] = color[3] = value.charAt(0);
+                color[4] = color[5] = value.charAt(1);
+                color[6] = color[7] = value.charAt(2);
+                value = new String(color);
+            } else if (value.length() == 4) { // ARGB format
+                char[] color = new char[8];
+                color[0] = color[1] = value.charAt(0);
+                color[2] = color[3] = value.charAt(1);
+                color[4] = color[5] = value.charAt(2);
+                color[6] = color[7] = value.charAt(3);
+                value = new String(color);
+            } else if (value.length() == 6) {
+                value = "FF" + value;
+            }
+
+            // this is a RRGGBB or AARRGGBB value
+
+            // Integer.parseInt will fail to parse strings like "ff191919", so we use
+            // a Long, but cast the result back into an int, since we know that we're only
+            // dealing with 32 bit values.
+            return (int)Long.parseLong(value, 16);
+        }
+
+        throw new NumberFormatException();
+    }
+
+    public static ColorStateList getColorStateList(ResourceValue resValue, BridgeContext context) {
+        String value = resValue.getValue();
+        if (value != null) {
+            // first check if the value is a file (xml most likely)
+            File f = new File(value);
+            if (f.isFile()) {
+                try {
+                    // let the framework inflate the ColorStateList from the XML file, by
+                    // providing an XmlPullParser
+                    KXmlParser parser = new KXmlParser();
+                    parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+                    parser.setInput(new FileReader(f));
+
+                    BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
+                            parser, context, resValue.isFramework());
+                    try {
+                        return ColorStateList.createFromXml(context.getResources(), blockParser);
+                    } finally {
+                        blockParser.ensurePopped();
+                    }
+                } catch (XmlPullParserException e) {
+                    Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                            "Failed to configure parser for " + value, e, null /*data*/);
+                    // we'll return null below.
+                } catch (Exception e) {
+                    // this is an error and not warning since the file existence is
+                    // checked before attempting to parse it.
+                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
+                            "Failed to parse file " + value, e, null /*data*/);
+
+                    return null;
+                }
+            } else {
+                // try to load the color state list from an int
+                try {
+                    int color = ResourceHelper.getColor(value);
+                    return ColorStateList.valueOf(color);
+                } catch (NumberFormatException e) {
+                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
+                            "Failed to convert " + value + " into a ColorStateList", e,
+                            null /*data*/);
+                    return null;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns a drawable from the given value.
+     * @param value The value that contains a path to a 9 patch, a bitmap or a xml based drawable,
+     * or an hexadecimal color
+     * @param context the current context
+     */
+    public static Drawable getDrawable(ResourceValue value, BridgeContext context) {
+        String stringValue = value.getValue();
+        if (RenderResources.REFERENCE_NULL.equals(stringValue)) {
+            return null;
+        }
+
+        String lowerCaseValue = stringValue.toLowerCase();
+
+        Density density = Density.MEDIUM;
+        if (value instanceof DensityBasedResourceValue) {
+            density =
+                ((DensityBasedResourceValue)value).getResourceDensity();
+        }
+
+
+        if (lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) {
+            File file = new File(stringValue);
+            if (file.isFile()) {
+                try {
+                    return getNinePatchDrawable(
+                            new FileInputStream(file), density, value.isFramework(),
+                            stringValue, context);
+                } catch (IOException e) {
+                    // failed to read the file, we'll return null below.
+                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
+                            "Failed lot load " + file.getAbsolutePath(), e, null /*data*/);
+                }
+            }
+
+            return null;
+        } else if (lowerCaseValue.endsWith(".xml")) {
+            // create a block parser for the file
+            File f = new File(stringValue);
+            if (f.isFile()) {
+                try {
+                    // let the framework inflate the Drawable from the XML file.
+                    KXmlParser parser = new KXmlParser();
+                    parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+                    parser.setInput(new FileReader(f));
+
+                    BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
+                            parser, context, value.isFramework());
+                    try {
+                        return Drawable.createFromXml(context.getResources(), blockParser);
+                    } finally {
+                        blockParser.ensurePopped();
+                    }
+                } catch (Exception e) {
+                    // this is an error and not warning since the file existence is checked before
+                    // attempting to parse it.
+                    Bridge.getLog().error(null, "Failed to parse file " + stringValue,
+                            e, null /*data*/);
+                }
+            } else {
+                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                        String.format("File %s does not exist (or is not a file)", stringValue),
+                        null /*data*/);
+            }
+
+            return null;
+        } else {
+            File bmpFile = new File(stringValue);
+            if (bmpFile.isFile()) {
+                try {
+                    Bitmap bitmap = Bridge.getCachedBitmap(stringValue,
+                            value.isFramework() ? null : context.getProjectKey());
+
+                    if (bitmap == null) {
+                        bitmap = Bitmap_Delegate.createBitmap(bmpFile, false /*isMutable*/,
+                                density);
+                        Bridge.setCachedBitmap(stringValue, bitmap,
+                                value.isFramework() ? null : context.getProjectKey());
+                    }
+
+                    return new BitmapDrawable(context.getResources(), bitmap);
+                } catch (IOException e) {
+                    // we'll return null below
+                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
+                            "Failed lot load " + bmpFile.getAbsolutePath(), e, null /*data*/);
+                }
+            } else {
+                // attempt to get a color from the value
+                try {
+                    int color = getColor(stringValue);
+                    return new ColorDrawable(color);
+                } catch (NumberFormatException e) {
+                    // we'll return null below.
+                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
+                            "Failed to convert " + stringValue + " into a drawable", e,
+                            null /*data*/);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private static Drawable getNinePatchDrawable(InputStream inputStream, Density density,
+            boolean isFramework, String cacheKey, BridgeContext context) throws IOException {
+        // see if we still have both the chunk and the bitmap in the caches
+        NinePatchChunk chunk = Bridge.getCached9Patch(cacheKey,
+                isFramework ? null : context.getProjectKey());
+        Bitmap bitmap = Bridge.getCachedBitmap(cacheKey,
+                isFramework ? null : context.getProjectKey());
+
+        // if either chunk or bitmap is null, then we reload the 9-patch file.
+        if (chunk == null || bitmap == null) {
+            try {
+                NinePatch ninePatch = NinePatch.load(inputStream, true /*is9Patch*/,
+                        false /* convert */);
+                if (ninePatch != null) {
+                    if (chunk == null) {
+                        chunk = ninePatch.getChunk();
+
+                        Bridge.setCached9Patch(cacheKey, chunk,
+                                isFramework ? null : context.getProjectKey());
+                    }
+
+                    if (bitmap == null) {
+                        bitmap = Bitmap_Delegate.createBitmap(ninePatch.getImage(),
+                                false /*isMutable*/,
+                                density);
+
+                        Bridge.setCachedBitmap(cacheKey, bitmap,
+                                isFramework ? null : context.getProjectKey());
+                    }
+                }
+            } catch (MalformedURLException e) {
+                // URL is wrong, we'll return null below
+            }
+        }
+
+        if (chunk != null && bitmap != null) {
+            int[] padding = chunk.getPadding();
+            Rect paddingRect = new Rect(padding[0], padding[1], padding[2], padding[3]);
+
+            return new NinePatchDrawable(context.getResources(), bitmap,
+                    NinePatch_Delegate.serialize(chunk),
+                    paddingRect, null);
+        }
+
+        return null;
+    }
+
+    // ------- TypedValue stuff
+    // This is taken from //device/libs/utils/ResourceTypes.cpp
+
+    private static final class UnitEntry {
+        String name;
+        int type;
+        int unit;
+        float scale;
+
+        UnitEntry(String name, int type, int unit, float scale) {
+            this.name = name;
+            this.type = type;
+            this.unit = unit;
+            this.scale = scale;
+        }
+    }
+
+    private final static UnitEntry[] sUnitNames = new UnitEntry[] {
+        new UnitEntry("px", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_PX, 1.0f),
+        new UnitEntry("dip", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_DIP, 1.0f),
+        new UnitEntry("dp", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_DIP, 1.0f),
+        new UnitEntry("sp", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_SP, 1.0f),
+        new UnitEntry("pt", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_PT, 1.0f),
+        new UnitEntry("in", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_IN, 1.0f),
+        new UnitEntry("mm", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_MM, 1.0f),
+        new UnitEntry("%", TypedValue.TYPE_FRACTION, TypedValue.COMPLEX_UNIT_FRACTION, 1.0f/100),
+        new UnitEntry("%p", TypedValue.TYPE_FRACTION, TypedValue.COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100),
+    };
+
+    /**
+     * Returns the raw value from the given string.
+     * This object is only valid until the next call on to {@link ResourceHelper}.
+     */
+    public static TypedValue getValue(String s) {
+        if (stringToFloat(s, mValue)) {
+            return mValue;
+        }
+
+        return null;
+    }
+
+    /**
+     * Convert the string into a {@link TypedValue}.
+     * @param s
+     * @param outValue
+     * @return true if success.
+     */
+    public static boolean stringToFloat(String s, TypedValue outValue) {
+        // remove the space before and after
+        s = s.trim();
+        int len = s.length();
+
+        if (len <= 0) {
+            return false;
+        }
+
+        // check that there's no non ascii characters.
+        char[] buf = s.toCharArray();
+        for (int i = 0 ; i < len ; i++) {
+            if (buf[i] > 255) {
+                return false;
+            }
+        }
+
+        // check the first character
+        if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.') {
+            return false;
+        }
+
+        // now look for the string that is after the float...
+        Matcher m = sFloatPattern.matcher(s);
+        if (m.matches()) {
+            String f_str = m.group(1);
+            String end = m.group(2);
+
+            float f;
+            try {
+                f = Float.parseFloat(f_str);
+            } catch (NumberFormatException e) {
+                // this shouldn't happen with the regexp above.
+                return false;
+            }
+
+            if (end.length() > 0 && end.charAt(0) != ' ') {
+                // Might be a unit...
+                if (parseUnit(end, outValue, sFloatOut)) {
+
+                    f *= sFloatOut[0];
+                    boolean neg = f < 0;
+                    if (neg) {
+                        f = -f;
+                    }
+                    long bits = (long)(f*(1<<23)+.5f);
+                    int radix;
+                    int shift;
+                    if ((bits&0x7fffff) == 0) {
+                        // Always use 23p0 if there is no fraction, just to make
+                        // things easier to read.
+                        radix = TypedValue.COMPLEX_RADIX_23p0;
+                        shift = 23;
+                    } else if ((bits&0xffffffffff800000L) == 0) {
+                        // Magnitude is zero -- can fit in 0 bits of precision.
+                        radix = TypedValue.COMPLEX_RADIX_0p23;
+                        shift = 0;
+                    } else if ((bits&0xffffffff80000000L) == 0) {
+                        // Magnitude can fit in 8 bits of precision.
+                        radix = TypedValue.COMPLEX_RADIX_8p15;
+                        shift = 8;
+                    } else if ((bits&0xffffff8000000000L) == 0) {
+                        // Magnitude can fit in 16 bits of precision.
+                        radix = TypedValue.COMPLEX_RADIX_16p7;
+                        shift = 16;
+                    } else {
+                        // Magnitude needs entire range, so no fractional part.
+                        radix = TypedValue.COMPLEX_RADIX_23p0;
+                        shift = 23;
+                    }
+                    int mantissa = (int)(
+                        (bits>>shift) & TypedValue.COMPLEX_MANTISSA_MASK);
+                    if (neg) {
+                        mantissa = (-mantissa) & TypedValue.COMPLEX_MANTISSA_MASK;
+                    }
+                    outValue.data |=
+                        (radix<<TypedValue.COMPLEX_RADIX_SHIFT)
+                        | (mantissa<<TypedValue.COMPLEX_MANTISSA_SHIFT);
+                    return true;
+                }
+                return false;
+            }
+
+            // make sure it's only spaces at the end.
+            end = end.trim();
+
+            if (end.length() == 0) {
+                if (outValue != null) {
+                    outValue.type = TypedValue.TYPE_FLOAT;
+                    outValue.data = Float.floatToIntBits(f);
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private static boolean parseUnit(String str, TypedValue outValue, float[] outScale) {
+        str = str.trim();
+
+        for (UnitEntry unit : sUnitNames) {
+            if (unit.name.equals(str)) {
+                outValue.type = unit.type;
+                outValue.data = unit.unit << TypedValue.COMPLEX_UNIT_SHIFT;
+                outScale[0] = unit.scale;
+
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
+
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Stack.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Stack.java
new file mode 100644
index 0000000..9bd0015
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Stack.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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.impl;
+
+import java.util.ArrayList;
+
+/**
+ * Custom Stack implementation on top of an {@link ArrayList} instead of
+ * using {@link java.util.Stack} which is on top of a vector.
+ *
+ * @param <T>
+ */
+public class Stack<T> extends ArrayList<T> {
+
+    private static final long serialVersionUID = 1L;
+
+    public Stack() {
+        super();
+    }
+
+    public Stack(int size) {
+        super(size);
+    }
+
+    /**
+     * Pushes the given object to the stack
+     * @param object the object to push
+     */
+    public void push(T object) {
+        add(object);
+    }
+
+    /**
+     * Remove the object at the top of the stack and returns it.
+     * @return the removed object or null if the stack was empty.
+     */
+    public T pop() {
+        if (size() > 0) {
+            return remove(size() - 1);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the object at the top of the stack.
+     * @return the object at the top or null if the stack is empty.
+     */
+    public T peek() {
+        if (size() > 0) {
+            return get(size() - 1);
+        }
+
+        return null;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/Debug.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/Debug.java
new file mode 100644
index 0000000..82eab85
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/Debug.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2011 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;
+
+public class Debug {
+
+    public final static boolean DEBUG = false;
+
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java
new file mode 100644
index 0000000..4d0c9ce
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2011 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 com.android.internal.util.ArrayUtils;
+
+import android.util.SparseArray;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * This is a custom {@link SparseArray} that uses {@link WeakReference} around the objects added
+ * to it. When the array is compacted, not only deleted indices but also empty references
+ * are removed, making the array efficient at removing references that were reclaimed.
+ *
+ * The code is taken from {@link SparseArray} directly and adapted to use weak references.
+ *
+ * Because our usage means that we never actually call {@link #remove(int)} or {@link #delete(int)},
+ * we must manually check if there are reclaimed references to trigger an internal compact step
+ * (which is normally only triggered when an item is manually removed).
+ *
+ * SparseArrays map integers to Objects.  Unlike a normal array of Objects,
+ * there can be gaps in the indices.  It is intended to be more efficient
+ * than using a HashMap to map Integers to Objects.
+ */
+@SuppressWarnings("unchecked")
+public class SparseWeakArray<E> {
+
+    private static final Object DELETED_REF = new Object();
+    private static final WeakReference<?> DELETED = new WeakReference(DELETED_REF);
+    private boolean mGarbage = false;
+
+    /**
+     * Creates a new SparseArray containing no mappings.
+     */
+    public SparseWeakArray() {
+        this(10);
+    }
+
+    /**
+     * Creates a new SparseArray containing no mappings that will not
+     * require any additional memory allocation to store the specified
+     * number of mappings.
+     */
+    public SparseWeakArray(int initialCapacity) {
+        initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
+
+        mKeys = new int[initialCapacity];
+        mValues = new WeakReference[initialCapacity];
+        mSize = 0;
+    }
+
+    /**
+     * Gets the Object mapped from the specified key, or <code>null</code>
+     * if no such mapping has been made.
+     */
+    public E get(int key) {
+        return get(key, null);
+    }
+
+    /**
+     * Gets the Object mapped from the specified key, or the specified Object
+     * if no such mapping has been made.
+     */
+    public E get(int key, E valueIfKeyNotFound) {
+        int i = binarySearch(mKeys, 0, mSize, key);
+
+        if (i < 0 || mValues[i] == DELETED || mValues[i].get() == null) {
+            return valueIfKeyNotFound;
+        } else {
+            return (E) mValues[i].get();
+        }
+    }
+
+    /**
+     * Removes the mapping from the specified key, if there was any.
+     */
+    public void delete(int key) {
+        int i = binarySearch(mKeys, 0, mSize, key);
+
+        if (i >= 0) {
+            if (mValues[i] != DELETED) {
+                mValues[i] = DELETED;
+                mGarbage = true;
+            }
+        }
+    }
+
+    /**
+     * Alias for {@link #delete(int)}.
+     */
+    public void remove(int key) {
+        delete(key);
+    }
+
+    /**
+     * Removes the mapping at the specified index.
+     */
+    public void removeAt(int index) {
+        if (mValues[index] != DELETED) {
+            mValues[index] = DELETED;
+            mGarbage = true;
+        }
+    }
+
+    private void gc() {
+        int n = mSize;
+        int o = 0;
+        int[] keys = mKeys;
+        WeakReference<?>[] values = mValues;
+
+        for (int i = 0; i < n; i++) {
+            WeakReference<?> val = values[i];
+
+            // Don't keep any non DELETED values, but only the one that still have a valid
+            // reference.
+            if (val != DELETED && val.get() != null) {
+                if (i != o) {
+                    keys[o] = keys[i];
+                    values[o] = val;
+                }
+
+                o++;
+            }
+        }
+
+        mGarbage = false;
+        mSize = o;
+
+        int newSize = ArrayUtils.idealIntArraySize(mSize);
+        if (newSize < mKeys.length) {
+            int[] nkeys = new int[newSize];
+            WeakReference<?>[] nvalues = new WeakReference[newSize];
+
+            System.arraycopy(mKeys, 0, nkeys, 0, newSize);
+            System.arraycopy(mValues, 0, nvalues, 0, newSize);
+
+            mKeys = nkeys;
+            mValues = nvalues;
+        }
+    }
+
+    /**
+     * Adds a mapping from the specified key to the specified value,
+     * replacing the previous mapping from the specified key if there
+     * was one.
+     */
+    public void put(int key, E value) {
+        int i = binarySearch(mKeys, 0, mSize, key);
+
+        if (i >= 0) {
+            mValues[i] = new WeakReference(value);
+        } else {
+            i = ~i;
+
+            if (i < mSize && (mValues[i] == DELETED || mValues[i].get() == null)) {
+                mKeys[i] = key;
+                mValues[i] = new WeakReference(value);
+                return;
+            }
+
+            if (mSize >= mKeys.length && (mGarbage || hasReclaimedRefs())) {
+                gc();
+
+                // Search again because indices may have changed.
+                i = ~binarySearch(mKeys, 0, mSize, key);
+            }
+
+            if (mSize >= mKeys.length) {
+                int n = ArrayUtils.idealIntArraySize(mSize + 1);
+
+                int[] nkeys = new int[n];
+                WeakReference<?>[] nvalues = new WeakReference[n];
+
+                // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
+                System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+                System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+                mKeys = nkeys;
+                mValues = nvalues;
+            }
+
+            if (mSize - i != 0) {
+                // Log.e("SparseArray", "move " + (mSize - i));
+                System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
+                System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
+            }
+
+            mKeys[i] = key;
+            mValues[i] = new WeakReference(value);
+            mSize++;
+        }
+    }
+
+    /**
+     * Returns the number of key-value mappings that this SparseArray
+     * currently stores.
+     */
+    public int size() {
+        if (mGarbage) {
+            gc();
+        }
+
+        return mSize;
+    }
+
+    /**
+     * Given an index in the range <code>0...size()-1</code>, returns
+     * the key from the <code>index</code>th key-value mapping that this
+     * SparseArray stores.
+     */
+    public int keyAt(int index) {
+        if (mGarbage) {
+            gc();
+        }
+
+        return mKeys[index];
+    }
+
+    /**
+     * Given an index in the range <code>0...size()-1</code>, returns
+     * the value from the <code>index</code>th key-value mapping that this
+     * SparseArray stores.
+     */
+    public E valueAt(int index) {
+        if (mGarbage) {
+            gc();
+        }
+
+        return (E) mValues[index].get();
+    }
+
+    /**
+     * Given an index in the range <code>0...size()-1</code>, sets a new
+     * value for the <code>index</code>th key-value mapping that this
+     * SparseArray stores.
+     */
+    public void setValueAt(int index, E value) {
+        if (mGarbage) {
+            gc();
+        }
+
+        mValues[index] = new WeakReference(value);
+    }
+
+    /**
+     * Returns the index for which {@link #keyAt} would return the
+     * specified key, or a negative number if the specified
+     * key is not mapped.
+     */
+    public int indexOfKey(int key) {
+        if (mGarbage) {
+            gc();
+        }
+
+        return binarySearch(mKeys, 0, mSize, key);
+    }
+
+    /**
+     * Returns an index for which {@link #valueAt} would return the
+     * specified key, or a negative number if no keys map to the
+     * specified value.
+     * Beware that this is a linear search, unlike lookups by key,
+     * and that multiple keys can map to the same value and this will
+     * find only one of them.
+     */
+    public int indexOfValue(E value) {
+        if (mGarbage) {
+            gc();
+        }
+
+        for (int i = 0; i < mSize; i++)
+            if (mValues[i].get() == value)
+                return i;
+
+        return -1;
+    }
+
+    /**
+     * Removes all key-value mappings from this SparseArray.
+     */
+    public void clear() {
+        int n = mSize;
+        WeakReference<?>[] values = mValues;
+
+        for (int i = 0; i < n; i++) {
+            values[i] = null;
+        }
+
+        mSize = 0;
+        mGarbage = false;
+    }
+
+    /**
+     * Puts a key/value pair into the array, optimizing for the case where
+     * the key is greater than all existing keys in the array.
+     */
+    public void append(int key, E value) {
+        if (mSize != 0 && key <= mKeys[mSize - 1]) {
+            put(key, value);
+            return;
+        }
+
+        if (mSize >= mKeys.length && (mGarbage || hasReclaimedRefs())) {
+            gc();
+        }
+
+        int pos = mSize;
+        if (pos >= mKeys.length) {
+            int n = ArrayUtils.idealIntArraySize(pos + 1);
+
+            int[] nkeys = new int[n];
+            WeakReference<?>[] nvalues = new WeakReference[n];
+
+            // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
+            System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+            System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+            mKeys = nkeys;
+            mValues = nvalues;
+        }
+
+        mKeys[pos] = key;
+        mValues[pos] = new WeakReference(value);
+        mSize = pos + 1;
+    }
+
+    private boolean hasReclaimedRefs() {
+        for (int i = 0 ; i < mSize ; i++) {
+            if (mValues[i].get() == null) { // DELETED.get() never returns null.
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private static int binarySearch(int[] a, int start, int len, int key) {
+        int high = start + len, low = start - 1, guess;
+
+        while (high - low > 1) {
+            guess = (high + low) / 2;
+
+            if (a[guess] < key)
+                low = guess;
+            else
+                high = guess;
+        }
+
+        if (high == start + len)
+            return ~(start + len);
+        else if (a[high] == key)
+            return high;
+        else
+            return ~high;
+    }
+
+    private int[] mKeys;
+    private WeakReference<?>[] mValues;
+    private int mSize;
+}
diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
new file mode 100644
index 0000000..e6dc646
--- /dev/null
+++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2011 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 libcore.icu;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.util.Locale;
+
+/**
+ * Delegate implementing the native methods of libcore.icu.ICU
+ *
+ * Through the layoutlib_create tool, the original native methods of ICU have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ */
+public class ICU_Delegate {
+
+    // --- Java delegates
+
+    @LayoutlibDelegate
+    /*package*/ static String toLowerCase(String s, String localeName) {
+        return s.toLowerCase();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static String toUpperCase(String s, String localeName) {
+        return s.toUpperCase();
+    }
+
+    // --- Native methods accessing ICU's database.
+
+    @LayoutlibDelegate
+    /*package*/ static String[] getAvailableBreakIteratorLocalesNative() {
+        return new String[0];
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static String[] getAvailableCalendarLocalesNative() {
+        return new String[0];
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static String[] getAvailableCollatorLocalesNative() {
+        return new String[0];
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static String[] getAvailableDateFormatLocalesNative() {
+        return new String[0];
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static String[] getAvailableLocalesNative() {
+        return new String[0];
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static String[] getAvailableNumberFormatLocalesNative() {
+        return new String[0];
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static String getCurrencyCodeNative(String locale) {
+        return "";
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int getCurrencyFractionDigitsNative(String currencyCode) {
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static String getCurrencySymbolNative(String locale, String currencyCode) {
+        return "";
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static String getDisplayCountryNative(String countryCode, String locale) {
+        return "";
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static String getDisplayLanguageNative(String languageCode, String locale) {
+        return "";
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static String getDisplayVariantNative(String variantCode, String locale) {
+        return "";
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static String getISO3CountryNative(String locale) {
+        return "";
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static String getISO3LanguageNative(String locale) {
+        return "";
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static String[] getISOLanguagesNative() {
+        return Locale.getISOLanguages();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static String[] getISOCountriesNative() {
+        return Locale.getISOCountries();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean initLocaleDataImpl(String locale, LocaleData result) {
+
+        // Used by Calendar.
+        result.firstDayOfWeek = Integer.valueOf(1);
+        result.minimalDaysInFirstWeek = Integer.valueOf(1);
+
+        // Used by DateFormatSymbols.
+        result.amPm = new String[] { "AM", "PM" };
+        result.eras = new String[] { "BC", "AD" };
+
+        result.longMonthNames = new String[] { "January", "February", "March", "April", "May",
+                "June", "July", "August", "September", "October", "November", "December" };
+        result.shortMonthNames = new String[] { "Jan", "Feb", "Mar", "Apr", "May",
+                "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+        result.longStandAloneMonthNames = result.longMonthNames;
+        result.shortStandAloneMonthNames = result.shortMonthNames;
+
+        result.longWeekdayNames = new String[] {
+                "Monday" ,"Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
+        result.shortWeekdayNames = new String[] {
+                "Mon" ,"Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
+        result.longStandAloneWeekdayNames = result.longWeekdayNames;
+        result.shortStandAloneWeekdayNames = result.shortWeekdayNames;
+
+        result.fullTimeFormat = "";
+        result.longTimeFormat = "";
+        result.mediumTimeFormat = "";
+        result.shortTimeFormat = "";
+
+        result.fullDateFormat = "";
+        result.longDateFormat = "";
+        result.mediumDateFormat = "";
+        result.shortDateFormat = "";
+
+        // Used by DecimalFormatSymbols.
+        result.zeroDigit = '0';
+        result.digit = '0';
+        result.decimalSeparator = '.';
+        result.groupingSeparator = ',';
+        result.patternSeparator = ' ';
+        result.percent = '%';
+        result.perMill = '\u2030';
+        result.monetarySeparator = ' ';
+        result.minusSign = '-';
+        result.exponentSeparator = "e";
+        result.infinity = "\u221E";
+        result.NaN = "NaN";
+        // Also used by Currency.
+        result.currencySymbol = "$";
+        result.internationalCurrencySymbol = "USD";
+
+        // Used by DecimalFormat and NumberFormat.
+        result.numberPattern = "%f";
+        result.integerPattern = "%d";
+        result.currencyPattern = "%s";
+        result.percentPattern = "%f";
+
+        return true;
+    }
+}
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java
index 6e14e82..ba3c51a 100644
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java
@@ -17,8 +17,6 @@
 package com.android.layoutlib.bridge;
 
 import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics._Original_Paint;
 import android.text.TextPaint;
 
 import junit.framework.TestCase;
@@ -58,14 +56,6 @@
         }
     }
 
-    public void testPaint() {
-        _Original_Paint o = new _Original_Paint();
-        assertNotNull(o);
-
-        Paint p = new Paint();
-        assertNotNull(p);
-    }
-
     public void textTextPaint() {
         TextPaint p = new TextPaint();
         assertNotNull(p);
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java
index 23351ab..a3219e7 100644
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java
@@ -48,5 +48,4 @@
         assertEquals(36, mPatch.getWidth());
         assertEquals(25, mPatch.getHeight());
     }
-
 }
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java
deleted file mode 100644
index e0dc55f..0000000
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2009 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;
-
-import java.lang.reflect.Method;
-
-import junit.framework.TestCase;
-
-public class TestClassReplacement extends TestCase {
-
-    public void testClassReplacements() {
-        // TODO: we want to test all the classes. For now only Paint passes the tests.
-//        final String[] classes = CreateInfo.RENAMED_CLASSES;
-        final String[] classes = new String[] {
-                "android.graphics.Paint",               "android.graphics._Original_Paint"
-        };
-        final int count = classes.length;
-        for (int i = 0 ; i < count ; i += 2) {
-            loadAndCompareClasses(classes[i], classes[i+1]);
-        }
-    }
-
-    private void loadAndCompareClasses(String newClassName, String oldClassName) {
-        // load the classes
-        try {
-            Class<?> newClass = TestClassReplacement.class.getClassLoader().loadClass(newClassName);
-            Class<?> oldClass = TestClassReplacement.class.getClassLoader().loadClass(oldClassName);
-
-            compare(newClass, oldClass);
-        } catch (ClassNotFoundException e) {
-            fail("Failed to load class: " + e.getMessage());
-        }
-    }
-
-    private void compare(Class<?> newClass, Class<?> oldClass) {
-        // first compare the methods.
-        Method[] newClassMethods = newClass.getDeclaredMethods();
-        Method[] oldClassMethods = oldClass.getDeclaredMethods();
-
-        for (Method oldMethod : oldClassMethods) {
-            // we ignore anything that starts with native
-            if (oldMethod.getName().startsWith("native")) {
-                continue;
-            }
-            boolean found = false;
-            for (Method newMethod : newClassMethods) {
-                if (compareMethods(newClass, newMethod, oldClass, oldMethod)) {
-                    found = true;
-                    break;
-                }
-            }
-
-            if (found == false) {
-                fail(String.format("Unable to find %1$s", oldMethod.toGenericString()));
-            }
-        }
-
-        // TODO: check (somehow?) that the methods that were removed from the original class
-        // have been put back in the new class!
-        // For this we need the original unmodified class (ie renamed, but w/o the methods removed)
-    }
-
-    private boolean compareMethods(Class<?> newClass, Method newMethod,
-            Class<?> oldClass, Method oldMethod) {
-        // first check the name of the method
-        if (newMethod.getName().equals(oldMethod.getName()) == false) {
-            return false;
-        }
-
-        // check the return value
-        Class<?> oldReturnType = oldMethod.getReturnType();
-        // if it's the old class, or if it's a inner class of the oldclass, we need to change this.
-        oldReturnType = adapt(oldReturnType, newClass, oldClass);
-
-        // compare the return types
-        Class<?> newReturnType = newMethod.getReturnType();
-        if (newReturnType.equals(oldReturnType) == false) {
-            return false;
-        }
-
-        // now check the parameters type.
-        Class<?>[] oldParameters = oldMethod.getParameterTypes();
-        Class<?>[] newParemeters = newMethod.getParameterTypes();
-        if (oldParameters.length != newParemeters.length) {
-            return false;
-        }
-
-        for (int i = 0 ; i < oldParameters.length ; i++) {
-            if (newParemeters[i].equals(adapt(oldParameters[i], newClass, oldClass)) == false) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Adapts a class to deal with renamed classes.
-     * <p/>For instance if old class is <code>android.graphics._Original_Paint</code> and the
-     * new class is <code>android.graphics.Paint</code> and the class to adapt is
-     * <code>android.graphics._Original_Paint$Cap</code>, then the method will return a
-     * {@link Class} object representing <code>android.graphics.Paint$Cap</code>.
-     * <p/>
-     * This method will also ensure that all renamed classes contains all the proper inner classes
-     * that they should be declaring.
-     * @param theClass the class to adapt
-     * @param newClass the new class object
-     * @param oldClass the old class object
-     * @return the adapted class.
-     * @throws ClassNotFoundException
-     */
-    private Class<?> adapt(Class<?> theClass, Class<?> newClass, Class<?> oldClass) {
-        // only look for a new class if it's not primitive as Class.forName() would fail otherwise.
-        if (theClass.isPrimitive() == false) {
-            String n = theClass.getName().replace(oldClass.getName(), newClass.getName());
-            try {
-                return Class.forName(n);
-            } catch (ClassNotFoundException e) {
-                fail("Missing class: " + n);
-            }
-        }
-
-        return theClass;
-    }
-}
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestDelegates.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestDelegates.java
new file mode 100644
index 0000000..d3218db
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestDelegates.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2010 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;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import com.android.tools.layoutlib.create.CreateInfo;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests that native delegate classes implement all the required methods.
+ *
+ * This looks at {@link CreateInfo#DELEGATE_CLASS_NATIVES} to get the list of classes that
+ * have their native methods reimplemented through a delegate.
+ *
+ * Since the reimplemented methods are not native anymore, we look for the annotation
+ * {@link LayoutlibDelegate}, and look for a matching method in the delegate (named the same
+ * as the modified class with _Delegate added as a suffix).
+ * If the original native method is not static, then we make sure the delegate method also
+ * include the original class as first parameter (to access "this").
+ *
+ */
+public class TestDelegates extends TestCase {
+
+    public void testNativeDelegates() {
+
+        final String[] classes = CreateInfo.DELEGATE_CLASS_NATIVES;
+        final int count = classes.length;
+        for (int i = 0 ; i < count ; i++) {
+            loadAndCompareClasses(classes[i], classes[i] + "_Delegate");
+        }
+    }
+
+    public void testMethodDelegates() {
+        final String[] methods = CreateInfo.DELEGATE_METHODS;
+        final int count = methods.length;
+        for (int i = 0 ; i < count ; i++) {
+            String methodName = methods[i];
+
+            // extract the class name
+            String className = methodName.substring(0, methodName.indexOf('#'));
+            String targetClassName = className.replace('$', '_') + "_Delegate";
+
+            loadAndCompareClasses(className, targetClassName);
+        }
+    }
+
+    private void loadAndCompareClasses(String originalClassName, String delegateClassName) {
+        // load the classes
+        try {
+            ClassLoader classLoader = TestDelegates.class.getClassLoader();
+            Class<?> originalClass = classLoader.loadClass(originalClassName);
+            Class<?> delegateClass = classLoader.loadClass(delegateClassName);
+
+            compare(originalClass, delegateClass);
+        } catch (ClassNotFoundException e) {
+           fail("Failed to load class: " + e.getMessage());
+        } catch (SecurityException e) {
+            fail("Failed to load class: " + e.getMessage());
+        }
+    }
+
+    private void compare(Class<?> originalClass, Class<?> delegateClass) throws SecurityException {
+        List<Method> checkedDelegateMethods = new ArrayList<Method>();
+
+        // loop on the methods of the original class, and for the ones that are annotated
+        // with @LayoutlibDelegate, look for a matching method in the delegate class.
+        // The annotation is automatically added by layoutlib_create when it replace a method
+        // by a call to a delegate
+        Method[] originalMethods = originalClass.getDeclaredMethods();
+        for (Method originalMethod : originalMethods) {
+            // look for methods that are delegated: they have the LayoutlibDelegate annotation
+            if (originalMethod.getAnnotation(LayoutlibDelegate.class) == null) {
+                continue;
+            }
+
+            // get the signature.
+            Class<?>[] parameters = originalMethod.getParameterTypes();
+
+            // if the method is not static, then the class is added as the first parameter
+            // (for "this")
+            if ((originalMethod.getModifiers() & Modifier.STATIC) == 0) {
+
+                Class<?>[] newParameters = new Class<?>[parameters.length + 1];
+                newParameters[0] = originalClass;
+                System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
+                parameters = newParameters;
+            }
+
+            // if the original class is an inner class that's not static, then
+            // we add this on the enclosing class at the beginning
+            if (originalClass.getEnclosingClass() != null &&
+                    (originalClass.getModifiers() & Modifier.STATIC) == 0) {
+                Class<?>[] newParameters = new Class<?>[parameters.length + 1];
+                newParameters[0] = originalClass.getEnclosingClass();
+                System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
+                parameters = newParameters;
+            }
+
+            try {
+                // try to load the method with the given parameter types.
+                Method delegateMethod = delegateClass.getDeclaredMethod(originalMethod.getName(),
+                        parameters);
+
+                // check that the method has the annotation
+                assertNotNull(
+                        String.format(
+                                "Delegate method %1$s for class %2$s does not have the @LayoutlibDelegate annotation",
+                                delegateMethod.getName(),
+                                originalClass.getName()),
+                        delegateMethod.getAnnotation(LayoutlibDelegate.class));
+
+                // check that the method is static
+                assertTrue(
+                        String.format(
+                                "Delegate method %1$s for class %2$s is not static",
+                                delegateMethod.getName(),
+                                originalClass.getName()),
+                        (delegateMethod.getModifiers() & Modifier.STATIC) == Modifier.STATIC);
+
+                // add the method as checked.
+                checkedDelegateMethods.add(delegateMethod);
+            } catch (NoSuchMethodException e) {
+                String name = getMethodName(originalMethod, parameters);
+                fail(String.format("Missing %1$s.%2$s", delegateClass.getName(), name));
+            }
+        }
+
+        // look for dead (delegate) code.
+        // This looks for all methods in the delegate class, and if they have the
+        // @LayoutlibDelegate annotation, make sure they have been previously found as a
+        // match for a method in the original class.
+        // If not, this means the method is a delegate for a method that either doesn't exist
+        // anymore or is not delegated anymore.
+        Method[] delegateMethods = delegateClass.getDeclaredMethods();
+        for (Method delegateMethod : delegateMethods) {
+            // look for methods that are delegates: they have the LayoutlibDelegate annotation
+            if (delegateMethod.getAnnotation(LayoutlibDelegate.class) == null) {
+                continue;
+            }
+
+            assertTrue(
+                    String.format(
+                            "Delegate method %1$s.%2$s is not used anymore and must be removed",
+                            delegateClass.getName(),
+                            getMethodName(delegateMethod)),
+                    checkedDelegateMethods.contains(delegateMethod));
+        }
+
+    }
+
+    private String getMethodName(Method method) {
+        return getMethodName(method, method.getParameterTypes());
+    }
+
+    private String getMethodName(Method method, Class<?>[] parameters) {
+        // compute a full class name that's long but not too long.
+        StringBuilder sb = new StringBuilder(method.getName() + "(");
+        for (int j = 0; j < parameters.length; j++) {
+            Class<?> theClass = parameters[j];
+            sb.append(theClass.getName());
+            int dimensions = 0;
+            while (theClass.isArray()) {
+                dimensions++;
+                theClass = theClass.getComponentType();
+            }
+            for (int i = 0; i < dimensions; i++) {
+                sb.append("[]");
+            }
+            if (j < (parameters.length - 1)) {
+                sb.append(",");
+            }
+        }
+        sb.append(")");
+
+        return sb.toString();
+    }
+}
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
similarity index 96%
rename from tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java
rename to tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
index db1262f..3252fb49 100644
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.layoutlib.bridge;
+package com.android.layoutlib.bridge.android;
+
+import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
 
 import org.kxml2.io.KXmlParser;
 import org.w3c.dom.Node;
diff --git a/tools/layoutlib/create/README.txt b/tools/layoutlib/create/README.txt
index 09b392b..65a64cd 100644
--- a/tools/layoutlib/create/README.txt
+++ b/tools/layoutlib/create/README.txt
@@ -4,68 +4,213 @@
 - Description -
 ---------------
 
-makeLayoutLib generates a library used by the Eclipse graphical layout editor
+Layoutlib_create generates a JAR library used by the Eclipse graphical layout editor
 to perform layout.
 
 
-
 - Usage -
 ---------
 
- ./makeLayoutLib path/to/android.jar destination.jar
+ ./layoutlib_create path/to/android.jar destination.jar
+
+
+- Design Overview -
+-------------------
+
+Layoutlib_create uses the "android.jar" containing all the Java code used by Android
+as generated by the Android build, right before the classes are converted to a DEX format.
+
+The Android JAR can't be used directly in Eclipse:
+- it contains references to native code (which we want to avoid in Eclipse),
+- some classes need to be overridden, for example all the drawing code that is
+  replaced by Java 2D calls in Eclipse.
+- some of the classes that need to be changed are final and/or we need access
+  to their private internal state.
+
+Consequently this tool:
+- parses the input JAR,
+- modifies some of the classes directly using some bytecode manipulation,
+- filters some packages and removes some that we don't want to end in the output JAR,
+- injects some new classes,
+- and generates a modified JAR file that is suitable for the Android plugin
+  for Eclipse to perform rendering.
+
+The ASM library is used to do the bytecode modification using its visitor pattern API.
+
+The layoutlib_create is *NOT* generic. There is no configuration file. Instead all the
+configuration is done in the main() method and the CreateInfo structure is expected to
+change with the Android platform as new classes are added, changed or removed.
+
+The resulting JAR is used by layoutlib_bridge (a.k.a. "the bridge"), also part of the
+platform, that provides all the necessary missing implementation for rendering graphics
+in Eclipse.
 
 
 
 - Implementation Notes -
 ------------------------
 
-The goal of makeLayoutLib is to list all the classes from the input jar and create a new
-jar that only keeps certain classes and create stubs for all their dependencies.
-
-First the input jar is parsed to find all the classes defined.
-
-In the Main(), the following list of classes are hardcoded (TODO config file later):
-- keep all classes that derive from android.view.View.
-- keep all classes in the android.view and android.widget packages (sub-packages excluded).
-- keep specific classes such as android.policy.PhoneLayoutInflater.
-
-For each class to keep, their dependencies are examined using BCEL.
-A dependency is defined as a class needed to instantiate the given class that should be kept,
-directly or indirectly. So a dependency is a class that is used by the input class, that is
-defined in the input jar and that is not part of the current JRE.
-
-Dependencies are computed recursively.
-
-Once all dependencies are found, the final jar can be created.
-There are three kind of classes to write:
-- classes that are to be kept as-is. They are just dumped in the new jar unchanged.
-- classes that are to be kept yet contain native methods or fields.
-- classes that are just dependencies. We don't want to expose their implementation in the final
-  jar.
-
-The implementation of native methods and all methods of mock classes is replaced by a stub
-that throws UnsupportedOperationException.
-
-Incidentally, the access level of native and mock classes needs to be changed in order for
-native methods to be later overridden. Methods that are "final private native" must become
-non-final, non-native and at most protected. Package-default access is changed to public.
-Classes that are final are made non-final. Abstract methods are left untouched.
+The tool works in two phases:
+- first analyze the input jar (AsmAnalyzer class)
+- then generate the output jar (AsmGenerator class),
 
 
+- Analyzer
+----------
 
-----
-20080617 Replace Class
+The goal of the analyzer is to create a graph of all the classes from the input JAR
+with their dependencies and then only keep the ones we want.
 
-Some classes are basically wrappers over native objects.
-Subclassing doesn't work as most methods are either static or we don't
-control object creation. In this scenario the idea is to be able to
-replace classes in the final jar.
+To do that, the analyzer is created with a list of base classes to keep -- everything
+that derives from these is kept. Currently the one such class is android.view.View:
+since we want to render layouts, anything that is sort of the view needs to be kept.
 
-Example: android.graphics.Paint would get renamed to OriginalPaint
-in the generated jar. Then in the bridge we'll introduce a replacement
-Paint class that derives from OriginalPaint.
+The analyzer is also given a list of class names to keep in the output.
+This is done using shell-like glob patterns that filter on the fully-qualified
+class names, for example "android.*.R**" ("*" does not matches dots whilst "**" does,
+and "." and "$" are interpreted as-is).
+In practice we almost but not quite request the inclusion of full packages.
+
+With this information, the analyzer parses the input zip to find all the classes.
+All classes deriving from the requested bases classes are kept.
+All classes which name matched the glob pattern are kept.
+The analysis then finds all the dependencies of the classes that are to be kept
+using an ASM visitor on the class, the field types, the method types and annotations types.
+Classes that belong to the current JRE are excluded.
+
+The output of the analyzer is a set of ASM ClassReader instances which are then
+fed to the generator.
+
+
+- Generator
+-----------
+
+The generator is constructed from a CreateInfo struct that acts as a config file
+and lists:
+- the classes to inject in the output JAR -- these classes are directly implemented
+  in layoutlib_create and will be used to interface with the renderer in Eclipse.
+- specific methods to override (see method stubs details below).
+- specific methods to remove based on their return type.
+- specific classes to rename.
+
+Each of these are specific strategies we use to be able to modify the Android code
+to fit within the Eclipse renderer. These strategies are explained beow.
+
+The core method of the generator is transform(): it takes an input ASM ClassReader
+and modifies it to produce a byte array suitable for the final JAR file.
+
+The first step of the transformation is changing the name of the class in case
+we requested the class to be renamed. This uses the RenameClassAdapter to also rename
+all inner classes and references in methods and types. Note that other classes are
+not transformed and keep referencing the original name.
+
+The TransformClassAdapter is then used to process the potentially renamed class.
+All protected or private classes are market as public.
+All classes are made non-final.
+Interfaces are left as-is.
+
+If a method has a return type that must be erased, the whole method is skipped.
+Methods are also changed from protected/private to public.
+The code of the methods is then kept as-is, except for native methods which are
+replaced by a stub. Methods that are to be overridden are also replaced by a stub.
+
+Finally fields are also visited and changed from protected/private to public.
+
+
+- Method stubs
+--------------
+
+As indicated above, all native and overridden methods are replaced by a stub.
+We don't have the code to replace with in layoutlib_create.
+Instead the StubMethodAdapter replaces the code of the method by a call to
+OverrideMethod.invokeX(). When using the final JAR, the bridge can register
+listeners from these overridden method calls based on the method signatures.
+
+The listeners are currently pretty basic: we only pass the signature of the
+method being called, its caller object and a flag indicating whether the
+method was native. We do not currently provide the parameters. The listener
+can however specify the return value of the overridden method.
+
+An extension being worked on is to actually replace these listeners by
+direct calls to a delegate class, complete with parameters.
+
+
+- Strategies
+------------
+
+We currently have 4 strategies to deal with overriding the rendering code
+and make it run in Eclipse. Most of these strategies are implemented hand-in-hand
+by the bridge (which runs in Eclipse) and the generator.
+
+
+1- Class Injection
+
+This is the easiest: we currently inject 4 classes, namely:
+- OverrideMethod and its associated MethodListener and MethodAdapter are used
+  to intercept calls to some specific methods that are stubbed out and change
+  their return value.
+- CreateInfo class, which configured the generator. Not used yet, but could
+  in theory help us track what the generator changed.
+
+
+2- Overriding methods
+
+As explained earlier, the creator doesn't have any replacement code for
+methods to override. Instead it removes the original code and replaces it
+by a call to a specific OveriddeMethod.invokeX(). The bridge then registers
+a listener on the method signature and can provide an implementation.
+
+
+3- Renaming classes
+
+This simply changes the name of a class in its definition, as well as all its
+references in internal inner classes and methods.
+Calls from other classes are not modified -- they keep referencing the original
+class name. This allows the bridge to literally replace an implementation.
+
+An example will make this easier: android.graphics.Paint is the main drawing
+class that we need to replace. To do so, the generator renames Paint to _original_Paint.
+Later the bridge provides its own replacement version of Paint which will be used
+by the rest of the Android stack. The replacement version of Paint can still use
+(either by inheritance or delegation) all the original non-native code of _original_Paint
+if it so desires.
+
+Some of the Android classes are basically wrappers over native objects and since
+we don't have the native code in Eclipse, we need to provide a full alternate
+implementation. Sub-classing doesn't work as some native methods are static and
+we don't control object creation.
 
 This won't rename/replace the inner static methods of a given class.
 
 
+4- Method erasure based on return type
 
+This is mostly an implementation detail of the bridge: in the Paint class
+mentioned above, some inner static classes are used to pass around
+attributes (e.g. FontMetrics, or the Style enum) and all the original implementation
+is native.
+
+In this case we have a strategy that tells the generator that anything returning, for
+example, the inner class Paint$Style in the Paint class should be discarded and the
+bridge will provide its own implementation.
+
+
+- References -
+--------------
+
+
+The JVM Specification 2nd edition:
+  http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html
+
+Understanding bytecode:
+  http://www.ibm.com/developerworks/ibm/library/it-haggar_bytecode/
+
+Bytecode opcode list:
+  http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings
+
+ASM user guide:
+  http://download.forge.objectweb.org/asm/asm-guide.pdf
+
+
+--
+end
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/LayoutlibDelegate.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/LayoutlibDelegate.java
new file mode 100644
index 0000000..9a48ea6
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/LayoutlibDelegate.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 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.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Denotes a method that has been converted to a delegate by layoutlib_create.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface LayoutlibDelegate {
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/Nullable.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/Nullable.java
new file mode 100755
index 0000000..0689c92
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/Nullable.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 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.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Denotes a parameter or field can be null.
+ * <p/>
+ * When decorating a method call parameter, this denotes the parameter can
+ * legitimately be null and the method will gracefully deal with it. Typically used
+ * on optional parameters.
+ * <p/>
+ * When decorating a method, this denotes the method might legitimately return null.
+ * <p/>
+ * This is a marker annotation and it has no specific attributes.
+ */
+@Retention(RetentionPolicy.SOURCE)
+public @interface Nullable {
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/VisibleForTesting.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/VisibleForTesting.java
new file mode 100755
index 0000000..e4e016b
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/VisibleForTesting.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 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.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Denotes that the class, method or field has its visibility relaxed so
+ * that unit tests can access it.
+ * <p/>
+ * The <code>visibility</code> argument can be used to specific what the original
+ * visibility should have been if it had not been made public or package-private for testing.
+ * The default is to consider the element private.
+ */
+@Retention(RetentionPolicy.SOURCE)
+public @interface VisibleForTesting {
+    /**
+     * Intended visibility if the element had not been made public or package-private for
+     * testing.
+     */
+    enum Visibility {
+        /** The element should be considered protected. */
+        PROTECTED,
+        /** The element should be considered package-private. */
+        PACKAGE,
+        /** The element should be considered private. */
+        PRIVATE
+    }
+
+    /**
+     * Intended visibility if the element had not been made public or package-private for testing.
+     * If not specified, one should assume the element originally intended to be private.
+     */
+    Visibility visibility() default Visibility.PRIVATE;
+}
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 7b55ed3e..a9ede26 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
@@ -28,9 +28,9 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeMap;
-import java.util.Map.Entry;
 import java.util.jar.JarEntry;
 import java.util.jar.JarOutputStream;
 
@@ -60,38 +60,56 @@
      *  old-FQCN to rename and they get erased as they get renamed. At the end, classes still
      *  left here are not in the code base anymore and thus were not renamed. */
     private HashSet<String> mClassesNotRenamed;
-    /** A map { FQCN => map { list of return types to delete from the FQCN } }. */
+    /** A map { FQCN => set { list of return types to delete from the FQCN } }. */
     private HashMap<String, Set<String>> mDeleteReturns;
+    /** A map { FQCN => set { method names } } of methods to rewrite as delegates.
+     *  The special name {@link DelegateClassAdapter#ALL_NATIVES} can be used as in internal set. */
+    private final HashMap<String, Set<String>> mDelegateMethods;
 
     /**
      * Creates a new generator that can generate the output JAR with the stubbed classes.
-     * 
+     *
      * @param log Output logger.
      * @param osDestJar The path of the destination JAR to create.
-     * @param injectClasses The list of class from layoutlib_create to inject in layoutlib.
-     * @param stubMethods The list of methods to stub out. Each entry must be in the form
-     *          "package.package.OuterClass$InnerClass#MethodName".
-     * @param renameClasses The list of classes to rename, must be an even list: the binary FQCN
-     *          of class to replace followed by the new FQCN.
-     * @param deleteReturns List of classes for which the methods returning them should be deleted.
-     * The array contains a list of null terminated section starting with the name of the class
-     * to rename in which the methods are deleted, followed by a list of return types identifying
-     * the methods to delete.
+     * @param createInfo Creation parameters. Must not be null.
      */
-    public AsmGenerator(Log log, String osDestJar,
-            Class<?>[] injectClasses,
-            String[] stubMethods,
-            String[] renameClasses, String[] deleteReturns) {
+    public AsmGenerator(Log log, String osDestJar, ICreateInfo createInfo) {
         mLog = log;
         mOsDestJar = osDestJar;
-        mInjectClasses = injectClasses != null ? injectClasses : new Class<?>[0];
-        mStubMethods = stubMethods != null ? new HashSet<String>(Arrays.asList(stubMethods)) :
-                                             new HashSet<String>();
+        mInjectClasses = createInfo.getInjectedClasses();
+        mStubMethods = new HashSet<String>(Arrays.asList(createInfo.getOverriddenMethods()));
+
+        // Create the map/set of methods to change to delegates
+        mDelegateMethods = new HashMap<String, Set<String>>();
+        for (String signature : createInfo.getDelegateMethods()) {
+            int pos = signature.indexOf('#');
+            if (pos <= 0 || pos >= signature.length() - 1) {
+                continue;
+            }
+            String className = binaryToInternalClassName(signature.substring(0, pos));
+            String methodName = signature.substring(pos + 1);
+            Set<String> methods = mDelegateMethods.get(className);
+            if (methods == null) {
+                methods = new HashSet<String>();
+                mDelegateMethods.put(className, methods);
+            }
+            methods.add(methodName);
+        }
+        for (String className : createInfo.getDelegateClassNatives()) {
+            className = binaryToInternalClassName(className);
+            Set<String> methods = mDelegateMethods.get(className);
+            if (methods == null) {
+                methods = new HashSet<String>();
+                mDelegateMethods.put(className, methods);
+            }
+            methods.add(DelegateClassAdapter.ALL_NATIVES);
+        }
 
         // Create the map of classes to rename.
         mRenameClasses = new HashMap<String, String>();
         mClassesNotRenamed = new HashSet<String>();
-        int n = renameClasses == null ? 0 : renameClasses.length;
+        String[] renameClasses = createInfo.getRenamedClasses();
+        int n = renameClasses.length;
         for (int i = 0; i < n; i += 2) {
             assert i + 1 < n;
             // The ASM class names uses "/" separators, whereas regular FQCN use "."
@@ -100,38 +118,37 @@
             mRenameClasses.put(oldFqcn, newFqcn);
             mClassesNotRenamed.add(oldFqcn);
         }
-        
+
         // create the map of renamed class -> return type of method to delete.
         mDeleteReturns = new HashMap<String, Set<String>>();
-        if (deleteReturns != null) {
-            Set<String> returnTypes = null;
-            String renamedClass = null;
-            for (String className : deleteReturns) {
-                // if we reach the end of a section, add it to the main map
-                if (className == null) {
-                    if (returnTypes != null) {
-                        mDeleteReturns.put(renamedClass, returnTypes);
-                    }
-                    
-                    renamedClass = null;
-                    continue;
+        String[] deleteReturns = createInfo.getDeleteReturns();
+        Set<String> returnTypes = null;
+        String renamedClass = null;
+        for (String className : deleteReturns) {
+            // if we reach the end of a section, add it to the main map
+            if (className == null) {
+                if (returnTypes != null) {
+                    mDeleteReturns.put(renamedClass, returnTypes);
                 }
-    
-                // if the renamed class is null, this is the beginning of a section
-                if (renamedClass == null) {
-                    renamedClass = binaryToInternalClassName(className);
-                    continue;
-                }
-    
-                // just a standard return type, we add it to the list.
-                if (returnTypes == null) {
-                    returnTypes = new HashSet<String>();
-                }
-                returnTypes.add(binaryToInternalClassName(className));
+
+                renamedClass = null;
+                continue;
             }
+
+            // if the renamed class is null, this is the beginning of a section
+            if (renamedClass == null) {
+                renamedClass = binaryToInternalClassName(className);
+                continue;
+            }
+
+            // just a standard return type, we add it to the list.
+            if (returnTypes == null) {
+                returnTypes = new HashSet<String>();
+            }
+            returnTypes.add(binaryToInternalClassName(className));
         }
     }
-    
+
     /**
      * Returns the list of classes that have not been renamed yet.
      * <p/>
@@ -163,12 +180,12 @@
     public void setDeps(Map<String, ClassReader> deps) {
         mDeps = deps;
     }
-    
+
     /** Gets the map of classes to output as-is, except if they have native methods */
     public Map<String, ClassReader> getKeep() {
         return mKeep;
     }
-    
+
     /** Gets the map of dependencies that must be completely stubbed */
     public Map<String, ClassReader> getDeps() {
         return mDeps;
@@ -177,7 +194,7 @@
     /** Generates the final JAR */
     public void generate() throws FileNotFoundException, IOException {
         TreeMap<String, byte[]> all = new TreeMap<String, byte[]>();
-        
+
         for (Class<?> clazz : mInjectClasses) {
             String name = classToEntryPath(clazz);
             InputStream is = ClassLoader.getSystemResourceAsStream(name);
@@ -186,7 +203,7 @@
             name = classNameToEntryPath(transformName(cr.getClassName()));
             all.put(name, b);
         }
-        
+
         for (Entry<String, ClassReader> entry : mDeps.entrySet()) {
             ClassReader cr = entry.getValue();
             byte[] b = transform(cr, true /* stubNativesOnly */);
@@ -211,8 +228,8 @@
 
     /**
      * Writes the JAR file.
-     * 
-     * @param outStream The file output stream were to write the JAR. 
+     *
+     * @param outStream The file output stream were to write the JAR.
      * @param all The map of all classes to output.
      * @throws IOException if an I/O error has occurred
      */
@@ -236,7 +253,7 @@
     String classNameToEntryPath(String className) {
         return className.replaceAll("\\.", "/").concat(".class");
     }
-    
+
     /**
      * Utility method to get the JAR entry path from a Class name.
      * e.g. it returns someting like "com/foo/OuterClass$InnerClass1$InnerClass2.class"
@@ -248,30 +265,32 @@
             name = "$" + clazz.getSimpleName() + name;
             clazz = parent;
         }
-        return classNameToEntryPath(clazz.getCanonicalName() + name);        
+        return classNameToEntryPath(clazz.getCanonicalName() + name);
     }
 
     /**
      * Transforms a class.
      * <p/>
      * There are 3 kind of transformations:
-     * 
+     *
      * 1- For "mock" dependencies classes, we want to remove all code from methods and replace
      * by a stub. Native methods must be implemented with this stub too. Abstract methods are
      * left intact. Modified classes must be overridable (non-private, non-final).
      * Native methods must be made non-final, non-private.
-     * 
+     *
      * 2- For "keep" classes, we want to rewrite all native methods as indicated above.
      * If a class has native methods, it must also be made non-private, non-final.
-     * 
+     *
      * Note that unfortunately static methods cannot be changed to non-static (since static and
      * non-static are invoked differently.)
      */
     byte[] transform(ClassReader cr, boolean stubNativesOnly) {
 
         boolean hasNativeMethods = hasNativeMethods(cr);
+
+        // Get the class name, as an internal name (e.g. com/android/SomeClass$InnerClass)
         String className = cr.getClassName();
-        
+
         String newName = transformName(className);
         // transformName returns its input argument if there's no need to rename the class
         if (newName != className) {
@@ -288,16 +307,28 @@
         // Rewrite the new class from scratch, without reusing the constant pool from the
         // original class reader.
         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
-        
+
         ClassVisitor rv = cw;
         if (newName != className) {
             rv = new RenameClassAdapter(cw, className, newName);
         }
-        
-        TransformClassAdapter cv = new TransformClassAdapter(mLog, mStubMethods, 
+
+        ClassVisitor cv = new TransformClassAdapter(mLog, mStubMethods,
                 mDeleteReturns.get(className),
                 newName, rv,
                 stubNativesOnly, stubNativesOnly || hasNativeMethods);
+
+        Set<String> delegateMethods = mDelegateMethods.get(className);
+        if (delegateMethods != null && !delegateMethods.isEmpty()) {
+            // If delegateMethods only contains one entry ALL_NATIVES and the class is
+            // known to have no native methods, just skip this step.
+            if (hasNativeMethods ||
+                    !(delegateMethods.size() == 1 &&
+                            delegateMethods.contains(DelegateClassAdapter.ALL_NATIVES))) {
+                cv = new DelegateClassAdapter(mLog, cv, className, delegateMethods);
+            }
+        }
+
         cr.accept(cv, 0 /* flags */);
         return cw.toByteArray();
     }
@@ -323,7 +354,7 @@
                 return newName + className.substring(pos);
             }
         }
-        
+
         return className;
     }
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java
index 5424efa..722dce2 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java
@@ -16,6 +16,9 @@
 
 package com.android.tools.layoutlib.create;
 
+import com.android.tools.layoutlib.annotations.VisibleForTesting;
+import com.android.tools.layoutlib.annotations.VisibleForTesting.Visibility;
+
 import org.objectweb.asm.AnnotationVisitor;
 import org.objectweb.asm.Attribute;
 import org.objectweb.asm.ClassVisitor;
@@ -27,13 +30,18 @@
  * Indicates if a class contains any native methods.
  */
 public class ClassHasNativeVisitor implements ClassVisitor {
-    
+
     private boolean mHasNativeMethods = false;
-    
+
     public boolean hasNativeMethods() {
         return mHasNativeMethods;
     }
 
+    @VisibleForTesting(visibility=Visibility.PRIVATE)
+    protected void setHasNativeMethods(boolean hasNativeMethods, String methodName) {
+        mHasNativeMethods = hasNativeMethods;
+    }
+
     public void visit(int version, int access, String name, String signature,
             String superName, String[] interfaces) {
         // pass
@@ -65,7 +73,9 @@
 
     public MethodVisitor visitMethod(int access, String name, String desc,
             String signature, String[] exceptions) {
-        mHasNativeMethods |= ((access & Opcodes.ACC_NATIVE) != 0);
+        if ((access & Opcodes.ACC_NATIVE) != 0) {
+            setHasNativeMethods(true, name);
+        }
         return null;
     }
 
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 2ed8641..eff6bbc 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
@@ -16,51 +16,160 @@
 
 package com.android.tools.layoutlib.create;
 
-public class CreateInfo {
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Describes the work to be done by {@link AsmGenerator}.
+ */
+public final class CreateInfo implements ICreateInfo {
+
+    /**
+     * Returns the list of class from layoutlib_create to inject in layoutlib.
+     * The list can be empty but must not be null.
+     */
+    public Class<?>[] getInjectedClasses() {
+        return INJECTED_CLASSES;
+    }
+
+    /**
+     * Returns the list of methods to rewrite as delegates.
+     * The list can be empty but must not be null.
+     */
+    public String[] getDelegateMethods() {
+        return DELEGATE_METHODS;
+    }
+
+    /**
+     * Returns the list of classes on which to delegate all native methods.
+     * The list can be empty but must not be null.
+     */
+    public String[] getDelegateClassNatives() {
+        return DELEGATE_CLASS_NATIVES;
+    }
+
+    /**
+     * Returns The list of methods to stub out. Each entry must be in the form
+     * "package.package.OuterClass$InnerClass#MethodName".
+     * The list can be empty but must not be null.
+     */
+    public String[] getOverriddenMethods() {
+        return OVERRIDDEN_METHODS;
+    }
+
+    /**
+     * Returns the list of classes to rename, must be an even list: the binary FQCN
+     * of class to replace followed by the new FQCN.
+     * The list can be empty but must not be null.
+     */
+    public String[] getRenamedClasses() {
+        return RENAMED_CLASSES;
+    }
+
+    /**
+     * Returns the list of classes for which the methods returning them should be deleted.
+     * The array contains a list of null terminated section starting with the name of the class
+     * to rename in which the methods are deleted, followed by a list of return types identifying
+     * the methods to delete.
+     * The list can be empty but must not be null.
+     */
+    public String[] getDeleteReturns() {
+        return DELETE_RETURNS;
+    }
+
+    //-----
+
     /**
      * The list of class from layoutlib_create to inject in layoutlib.
      */
-    public final static Class<?>[] INJECTED_CLASSES = new Class<?>[] {
+    private final static Class<?>[] INJECTED_CLASSES = new Class<?>[] {
             OverrideMethod.class,
             MethodListener.class,
             MethodAdapter.class,
-            CreateInfo.class
+            ICreateInfo.class,
+            CreateInfo.class,
+            LayoutlibDelegate.class
         };
 
     /**
+     * The list of methods to rewrite as delegates.
+     */
+    private 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$Theme#obtainStyledAttributes",
+        "android.content.res.Resources$Theme#resolveAttribute",
+        "android.graphics.BitmapFactory#finishDecode",
+        "android.os.Handler#sendMessageAtTime",
+        "android.os.Build#getString",
+        "android.view.LayoutInflater#rInflate",
+        "android.view.View#isInEditMode",
+        "com.android.internal.util.XmlUtils#convertValueToInt",
+        // TODO: comment out once DelegateClass is working
+    };
+
+    /**
+     * The list of classes on which to delegate all native methods.
+     */
+    private final static String[] DELEGATE_CLASS_NATIVES = new String[] {
+        "android.animation.PropertyValuesHolder",
+        "android.graphics.AvoidXfermode",
+        "android.graphics.Bitmap",
+        "android.graphics.BitmapFactory",
+        "android.graphics.BitmapShader",
+        "android.graphics.BlurMaskFilter",
+        "android.graphics.Canvas",
+        "android.graphics.ColorFilter",
+        "android.graphics.ColorMatrixColorFilter",
+        "android.graphics.ComposePathEffect",
+        "android.graphics.ComposeShader",
+        "android.graphics.CornerPathEffect",
+        "android.graphics.DashPathEffect",
+        "android.graphics.DiscretePathEffect",
+        "android.graphics.DrawFilter",
+        "android.graphics.EmbossMaskFilter",
+        "android.graphics.LayerRasterizer",
+        "android.graphics.LightingColorFilter",
+        "android.graphics.LinearGradient",
+        "android.graphics.MaskFilter",
+        "android.graphics.Matrix",
+        "android.graphics.NinePatch",
+        "android.graphics.Paint",
+        "android.graphics.PaintFlagsDrawFilter",
+        "android.graphics.Path",
+        "android.graphics.PathDashPathEffect",
+        "android.graphics.PathEffect",
+        "android.graphics.PixelXorXfermode",
+        "android.graphics.PorterDuffColorFilter",
+        "android.graphics.PorterDuffXfermode",
+        "android.graphics.RadialGradient",
+        "android.graphics.Rasterizer",
+        "android.graphics.Region",
+        "android.graphics.Shader",
+        "android.graphics.SumPathEffect",
+        "android.graphics.SweepGradient",
+        "android.graphics.Typeface",
+        "android.graphics.Xfermode",
+        "android.os.SystemClock",
+        "android.util.FloatMath",
+        "libcore.icu.ICU",
+    };
+
+    /**
      * The list of methods to stub out. Each entry must be in the form
      *  "package.package.OuterClass$InnerClass#MethodName".
      */
-    public final static String[] OVERRIDDEN_METHODS = new String[] {
-            "android.view.View#isInEditMode",
-            "android.content.res.Resources$Theme#obtainStyledAttributes",
-        };
+    private final static String[] OVERRIDDEN_METHODS = new String[] {
+    };
 
     /**
      *  The list of classes to rename, must be an even list: the binary FQCN
      *  of class to replace followed by the new FQCN.
      */
-    public final static String[] RENAMED_CLASSES =
+    private final static String[] RENAMED_CLASSES =
         new String[] {
-            "android.graphics.Bitmap",              "android.graphics._Original_Bitmap",
-            "android.graphics.BitmapFactory",       "android.graphics._Original_BitmapFactory",
-            "android.graphics.BitmapShader",        "android.graphics._Original_BitmapShader",
-            "android.graphics.Canvas",              "android.graphics._Original_Canvas",
-            "android.graphics.ComposeShader",       "android.graphics._Original_ComposeShader",
-            "android.graphics.DashPathEffect",       "android.graphics._Original_DashPathEffect",
-            "android.graphics.LinearGradient",      "android.graphics._Original_LinearGradient",
-            "android.graphics.Matrix",              "android.graphics._Original_Matrix",
-            "android.graphics.Paint",               "android.graphics._Original_Paint",
-            "android.graphics.Path",                "android.graphics._Original_Path",
-            "android.graphics.PorterDuffXfermode",  "android.graphics._Original_PorterDuffXfermode",
-            "android.graphics.RadialGradient",      "android.graphics._Original_RadialGradient",
-            "android.graphics.Shader",              "android.graphics._Original_Shader",
-            "android.graphics.SweepGradient",       "android.graphics._Original_SweepGradient",
-            "android.graphics.Typeface",            "android.graphics._Original_Typeface",
             "android.os.ServiceManager",            "android.os._Original_ServiceManager",
-            "android.util.FloatMath",               "android.util._Original_FloatMath",
             "android.view.SurfaceView",             "android.view._Original_SurfaceView",
             "android.view.accessibility.AccessibilityManager", "android.view.accessibility._Original_AccessibilityManager",
+            "android.webkit.WebView",               "android.webkit._Original_WebView",
         };
 
     /**
@@ -69,15 +178,8 @@
      * to rename in which the methods are deleted, followed by a list of return types identifying
      * the methods to delete.
      */
-    public final static String[] REMOVED_METHODS =
+    private final static String[] DELETE_RETURNS =
         new String[] {
-            "android.graphics.Paint",       // class to delete methods from
-            "android.graphics.Paint$Align", // list of type identifying methods to delete
-            "android.graphics.Paint$Style",
-            "android.graphics.Paint$Join",
-            "android.graphics.Paint$Cap",
-            "android.graphics.Paint$FontMetrics",
-            "android.graphics.Paint$FontMetricsInt",
             null };                         // separator, for next class/methods list.
 }
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
new file mode 100644
index 0000000..9cba8a0
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 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.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.util.Set;
+
+/**
+ * A {@link DelegateClassAdapter} can transform some methods from a class into
+ * delegates that defer the call to an associated delegate class.
+ * <p/>
+ * This is used to override specific methods and or all native methods in classes.
+ */
+public class DelegateClassAdapter extends ClassAdapter {
+
+    public final static String ALL_NATIVES = "<<all_natives>>";
+
+    private final String mClassName;
+    private final Set<String> mDelegateMethods;
+    private final Log mLog;
+
+    /**
+     * Creates a new {@link DelegateClassAdapter} that can transform some methods
+     * from a class into delegates that defer the call to an associated delegate class.
+     * <p/>
+     * This is used to override specific methods and or all native methods in classes.
+     *
+     * @param log The logger object. Must not be null.
+     * @param cv the class visitor to which this adapter must delegate calls.
+     * @param className The internal class name of the class to visit,
+     *          e.g. <code>com/android/SomeClass$InnerClass</code>.
+     * @param delegateMethods The set of method names to modify and/or the
+     *          special constant {@link #ALL_NATIVES} to convert all native methods.
+     */
+    public DelegateClassAdapter(Log log,
+            ClassVisitor cv,
+            String className,
+            Set<String> delegateMethods) {
+        super(cv);
+        mLog = log;
+        mClassName = className;
+        mDelegateMethods = delegateMethods;
+    }
+
+    //----------------------------------
+    // Methods from the ClassAdapter
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc,
+            String signature, String[] exceptions) {
+
+        boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
+        boolean isNative = (access & Opcodes.ACC_NATIVE) != 0;
+
+        boolean useDelegate = (isNative && mDelegateMethods.contains(ALL_NATIVES)) ||
+                              mDelegateMethods.contains(name);
+
+        if (useDelegate) {
+            // remove native
+            access = access & ~Opcodes.ACC_NATIVE;
+        }
+
+        MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
+        if (useDelegate) {
+            DelegateMethodAdapter a = new DelegateMethodAdapter(mLog, mw, mClassName,
+                                                                name, desc, isStatic);
+            if (isNative) {
+                // A native has no code to visit, so we need to generate it directly.
+                a.generateCode();
+            } else {
+                return a;
+            }
+        }
+        return mw;
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
new file mode 100644
index 0000000..8d7f016
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2008 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 com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.util.ArrayList;
+
+/**
+ * This method adapter rewrites a method by discarding the original code and generating
+ * a call to a delegate. Original annotations are passed along unchanged.
+ * <p/>
+ * Calls are delegated to a class named <code>&lt;className&gt;_Delegate</code> with
+ * static methods matching the methods to be overridden here. The methods have the
+ * same return type. The argument type list is the same except the "this" reference is
+ * passed first for non-static methods.
+ * <p/>
+ * A new annotation is added.
+ * <p/>
+ * Note that native methods have, by definition, no code so there's nothing a visitor
+ * can visit. That means the caller must call {@link #generateCode()} directly for
+ * a native and use the visitor pattern for non-natives.
+ * <p/>
+ * Instances of this class are not re-usable. You need a new instance for each method.
+ */
+class DelegateMethodAdapter implements MethodVisitor {
+
+    /**
+     * Suffix added to delegate classes.
+     */
+    public static final String DELEGATE_SUFFIX = "_Delegate";
+
+    private static String CONSTRUCTOR = "<init>";
+    private static String CLASS_INIT = "<clinit>";
+
+    /** The parent method writer */
+    private MethodVisitor mParentVisitor;
+    /** Flag to output the first line number. */
+    private boolean mOutputFirstLineNumber = true;
+    /** The original method descriptor (return type + argument types.) */
+    private String mDesc;
+    /** True if the original method is static. */
+    private final boolean mIsStatic;
+    /** The internal class name (e.g. <code>com/android/SomeClass$InnerClass</code>.) */
+    private final String mClassName;
+    /** The method name. */
+    private final String mMethodName;
+    /** Logger object. */
+    private final Log mLog;
+    /** True if {@link #visitCode()} has been invoked. */
+    private boolean mVisitCodeCalled;
+
+    /**
+     * Creates a new {@link DelegateMethodAdapter} that will transform this method
+     * into a delegate call.
+     * <p/>
+     * See {@link DelegateMethodAdapter} for more details.
+     *
+     * @param log The logger object. Must not be null.
+     * @param mv the method visitor to which this adapter must delegate calls.
+     * @param className The internal class name of the class to visit,
+     *          e.g. <code>com/android/SomeClass$InnerClass</code>.
+     * @param methodName The simple name of the method.
+     * @param desc A method descriptor (c.f. {@link Type#getReturnType(String)} +
+     *          {@link Type#getArgumentTypes(String)})
+     * @param isStatic True if the method is declared static.
+     */
+    public DelegateMethodAdapter(Log log,
+            MethodVisitor mv,
+            String className,
+            String methodName,
+            String desc,
+            boolean isStatic) {
+        mLog = log;
+        mParentVisitor = mv;
+        mClassName = className;
+        mMethodName = methodName;
+        mDesc = desc;
+        mIsStatic = isStatic;
+
+        if (CONSTRUCTOR.equals(methodName) || CLASS_INIT.equals(methodName)) {
+            // We're going to simplify by not supporting constructors.
+            // The only trick with a constructor is to find the proper super constructor
+            // and call it (and deciding if we should mirror the original method call to
+            // a custom constructor or call a default one.)
+            throw new UnsupportedOperationException(
+                    String.format("Delegate doesn't support overriding constructor %1$s:%2$s(%3$s)",
+                            className, methodName, desc));
+        }
+    }
+
+    /**
+     * Generates the new code for the method.
+     * <p/>
+     * For native methods, this must be invoked directly by {@link DelegateClassAdapter}
+     * (since they have no code to visit).
+     * <p/>
+     * Otherwise for non-native methods the {@link DelegateClassAdapter} simply needs to
+     * return this instance of {@link DelegateMethodAdapter} and let the normal visitor pattern
+     * invoke it as part of the {@link ClassReader#accept(ClassVisitor, int)} workflow and then
+     * this method will be invoked from {@link MethodVisitor#visitEnd()}.
+     */
+    public void generateCode() {
+        /*
+         * The goal is to generate a call to a static delegate method.
+         * If this method is non-static, the first parameter will be 'this'.
+         * All the parameters must be passed and then the eventual return type returned.
+         *
+         * Example, let's say we have a method such as
+         *   public void method_1(int a, Object b, ArrayList<String> c) { ... }
+         *
+         * We'll want to create a body that calls a delegate method like this:
+         *   TheClass_Delegate.method_1(this, a, b, c);
+         *
+         * If the method is non-static and the class name is an inner class (e.g. has $ in its
+         * last segment), we want to push the 'this' of the outer class first:
+         *   OuterClass_InnerClass_Delegate.method_1(
+         *     OuterClass.this,
+         *     OuterClass$InnerClass.this,
+         *     a, b, c);
+         *
+         * Only one level of inner class is supported right now, for simplicity and because
+         * we don't need more.
+         *
+         * The generated class name is the current class name with "_Delegate" appended to it.
+         * One thing to realize is that we don't care about generics -- since generic types
+         * are erased at runtime, they have no influence on the method name being called.
+         */
+
+        // Add our annotation
+        AnnotationVisitor aw = mParentVisitor.visitAnnotation(
+                Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(),
+                true); // visible at runtime
+        aw.visitEnd();
+
+        if (!mVisitCodeCalled) {
+            // If this is a direct call to generateCode() as done by DelegateClassAdapter
+            // for natives, visitCode() hasn't been called yet.
+            mParentVisitor.visitCode();
+            mVisitCodeCalled = true;
+        }
+
+        ArrayList<Type> paramTypes = new ArrayList<Type>();
+        String delegateClassName = mClassName + DELEGATE_SUFFIX;
+        boolean pushedArg0 = false;
+        int maxStack = 0;
+
+        // For an instance method (e.g. non-static), push the 'this' preceded
+        // by the 'this' of any outer class, if any.
+        if (!mIsStatic) {
+            // Check if the last segment of the class name has inner an class.
+            // Right now we only support one level of inner classes.
+            int slash = mClassName.lastIndexOf('/');
+            int dol = mClassName.lastIndexOf('$');
+            if (dol != -1 && dol > slash && dol == mClassName.indexOf('$')) {
+                String outerClass = mClassName.substring(0, dol);
+                Type outerType = Type.getObjectType(outerClass);
+
+                // Change a delegate class name to "com/foo/Outer_Inner_Delegate"
+                delegateClassName = delegateClassName.replace('$', '_');
+
+                // The first-level inner class has a package-protected member called 'this$0'
+                // that points to the outer class.
+
+                // Push this.getField("this$0") on the call stack.
+                mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0); // var 0 = this
+                mParentVisitor.visitFieldInsn(Opcodes.GETFIELD,
+                        mClassName,                 // class where the field is defined
+                        "this$0",                   // field name
+                        outerType.getDescriptor()); // type of the field
+                maxStack++;
+                paramTypes.add(outerType);
+            }
+
+            // Push "this" for the instance method, which is always ALOAD 0
+            mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+            maxStack++;
+            pushedArg0 = true;
+            paramTypes.add(Type.getObjectType(mClassName));
+        }
+
+        // Push all other arguments. Start at arg 1 if we already pushed 'this' above.
+        Type[] argTypes = Type.getArgumentTypes(mDesc);
+        int maxLocals = pushedArg0 ? 1 : 0;
+        for (Type t : argTypes) {
+            int size = t.getSize();
+            mParentVisitor.visitVarInsn(t.getOpcode(Opcodes.ILOAD), maxLocals);
+            maxLocals += size;
+            maxStack += size;
+            paramTypes.add(t);
+        }
+
+        // Construct the descriptor of the delegate based on the parameters
+        // we pushed on the call stack. The return type remains unchanged.
+        String desc = Type.getMethodDescriptor(
+                Type.getReturnType(mDesc),
+                paramTypes.toArray(new Type[paramTypes.size()]));
+
+        // Invoke the static delegate
+        mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                delegateClassName,
+                mMethodName,
+                desc);
+
+        Type returnType = Type.getReturnType(mDesc);
+        mParentVisitor.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
+
+        mParentVisitor.visitMaxs(maxStack, maxLocals);
+        mParentVisitor.visitEnd();
+
+        // For debugging now. Maybe we should collect these and store them in
+        // a text file for helping create the delegates. We could also compare
+        // the text file to a golden and break the build on unsupported changes
+        // or regressions. Even better we could fancy-print something that looks
+        // like the expected Java method declaration.
+        mLog.debug("Delegate: %1$s # %2$s %3$s", delegateClassName, mMethodName, desc);
+    }
+
+    /* Pass down to visitor writer. In this implementation, either do nothing. */
+    public void visitCode() {
+        mVisitCodeCalled = true;
+        mParentVisitor.visitCode();
+    }
+
+    /*
+     * visitMaxs is called just before visitEnd if there was any code to rewrite.
+     * Skip the original.
+     */
+    public void visitMaxs(int maxStack, int maxLocals) {
+    }
+
+    /**
+     * End of visiting. Generate the messaging code.
+     */
+    public void visitEnd() {
+        generateCode();
+    }
+
+    /* Writes all annotation from the original method. */
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        return mParentVisitor.visitAnnotation(desc, visible);
+    }
+
+    /* Writes all annotation default values from the original method. */
+    public AnnotationVisitor visitAnnotationDefault() {
+        return mParentVisitor.visitAnnotationDefault();
+    }
+
+    public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
+            boolean visible) {
+        return mParentVisitor.visitParameterAnnotation(parameter, desc, visible);
+    }
+
+    /* Writes all attributes from the original method. */
+    public void visitAttribute(Attribute attr) {
+        mParentVisitor.visitAttribute(attr);
+    }
+
+    /*
+     * Only writes the first line number present in the original code so that source
+     * viewers can direct to the correct method, even if the content doesn't match.
+     */
+    public void visitLineNumber(int line, Label start) {
+        if (mOutputFirstLineNumber) {
+            mParentVisitor.visitLineNumber(line, start);
+            mOutputFirstLineNumber = false;
+        }
+    }
+
+    public void visitInsn(int opcode) {
+        // Skip original code.
+    }
+
+    public void visitLabel(Label label) {
+        // Skip original code.
+    }
+
+    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+        // Skip original code.
+    }
+
+    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+        // Skip original code.
+    }
+
+    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+        // Skip original code.
+    }
+
+    public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
+        // Skip original code.
+    }
+
+    public void visitIincInsn(int var, int increment) {
+        // Skip original code.
+    }
+
+    public void visitIntInsn(int opcode, int operand) {
+        // Skip original code.
+    }
+
+    public void visitJumpInsn(int opcode, Label label) {
+        // Skip original code.
+    }
+
+    public void visitLdcInsn(Object cst) {
+        // Skip original code.
+    }
+
+    public void visitLocalVariable(String name, String desc, String signature,
+            Label start, Label end, int index) {
+        // Skip original code.
+    }
+
+    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+        // Skip original code.
+    }
+
+    public void visitMultiANewArrayInsn(String desc, int dims) {
+        // Skip original code.
+    }
+
+    public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
+        // Skip original code.
+    }
+
+    public void visitTypeInsn(int opcode, String type) {
+        // Skip original code.
+    }
+
+    public void visitVarInsn(int opcode, int var) {
+        // Skip original code.
+    }
+
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
new file mode 100644
index 0000000..40c1706
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 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;
+
+/**
+ * Interface describing the work to be done by {@link AsmGenerator}.
+ */
+public interface ICreateInfo {
+
+    /**
+     * Returns the list of class from layoutlib_create to inject in layoutlib.
+     * The list can be empty but must not be null.
+     */
+    public abstract Class<?>[] getInjectedClasses();
+
+    /**
+     * Returns the list of methods to rewrite as delegates.
+     * The list can be empty but must not be null.
+     */
+    public abstract String[] getDelegateMethods();
+
+    /**
+     * Returns the list of classes on which to delegate all native methods.
+     * The list can be empty but must not be null.
+     */
+    public abstract String[] getDelegateClassNatives();
+
+    /**
+     * Returns The list of methods to stub out. Each entry must be in the form
+     * "package.package.OuterClass$InnerClass#MethodName".
+     * The list can be empty but must not be null.
+     */
+    public abstract String[] getOverriddenMethods();
+
+    /**
+     * Returns the list of classes to rename, must be an even list: the binary FQCN
+     * of class to replace followed by the new FQCN.
+     * The list can be empty but must not be null.
+     */
+    public abstract String[] getRenamedClasses();
+
+    /**
+     * Returns the list of classes for which the methods returning them should be deleted.
+     * The array contains a list of null terminated section starting with the name of the class
+     * to rename in which the methods are deleted, followed by a list of return types identifying
+     * the methods to delete.
+     * The list can be empty but must not be null.
+     */
+    public abstract String[] getDeleteReturns();
+
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
index b30e9e5..ce48069 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
@@ -21,7 +21,28 @@
 import java.util.Set;
 
 
-
+/**
+ * Entry point for the layoutlib_create tool.
+ * <p/>
+ * The tool does not currently rely on any external configuration file.
+ * Instead the configuration is mostly done via the {@link CreateInfo} class.
+ * <p/>
+ * For a complete description of the tool and its implementation, please refer to
+ * the "README.txt" file at the root of this project.
+ * <p/>
+ * For a quick test, invoke this as follows:
+ * <pre>
+ * $ make layoutlib
+ * </pre>
+ * which does:
+ * <pre>
+ * $ make layoutlib_create &lt;bunch of framework jars&gt;
+ * $ out/host/linux-x86/framework/bin/layoutlib_create \
+ *        out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar \
+ *        out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar \
+ *        out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar
+ * </pre>
+ */
 public class Main {
 
     public static void main(String[] args) {
@@ -42,15 +63,13 @@
         }
 
         try {
-            AsmGenerator agen = new AsmGenerator(log, osDestJar[0],
-                    CreateInfo.INJECTED_CLASSES,
-                    CreateInfo.OVERRIDDEN_METHODS,
-                    CreateInfo.RENAMED_CLASSES,
-                    CreateInfo.REMOVED_METHODS
-            );
+            AsmGenerator agen = new AsmGenerator(log, osDestJar[0], new CreateInfo());
 
             AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath, agen,
-                    new String[] { "android.view.View" },   // derived from
+                    new String[] {                          // derived from
+                        "android.view.View",
+                        "android.app.Fragment"
+                    },
                     new String[] {                          // include classes
                         "android.*", // for android.R
                         "android.util.*",
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
index e294d56..f2d9755 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
@@ -26,7 +26,7 @@
 import java.util.Set;
 
 /**
- * Class adapter that can stub some or all of the methods of the class. 
+ * Class adapter that can stub some or all of the methods of the class.
  */
 class TransformClassAdapter extends ClassAdapter {
 
@@ -41,12 +41,12 @@
 
     /**
      * Creates a new class adapter that will stub some or all methods.
-     * @param logger 
-     * @param stubMethods 
+     * @param logger
+     * @param stubMethods  list of method signatures to always stub out
      * @param deleteReturns list of types that trigger the deletion of methods returning them.
      * @param className The name of the class being modified
      * @param cv The parent class writer visitor
-     * @param stubNativesOnly True if only native methods should be stubbed. False if all 
+     * @param stubNativesOnly True if only native methods should be stubbed. False if all
      *                        methods should be stubbed.
      * @param hasNative True if the method has natives, in which case its access should be
      *                  changed.
@@ -67,10 +67,10 @@
     @Override
     public void visit(int version, int access, String name,
             String signature, String superName, String[] interfaces) {
-        
+
         // This class might be being renamed.
         name = mClassName;
-        
+
         // remove protected or private and set as public
         access = access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED);
         access |= Opcodes.ACC_PUBLIC;
@@ -82,7 +82,7 @@
         mIsInterface = ((access & Opcodes.ACC_INTERFACE) != 0);
         super.visit(version, access, name, signature, superName, interfaces);
     }
-    
+
     /* Visits the header of an inner class. */
     @Override
     public void visitInnerClass(String name, String outerName, String innerName, int access) {
@@ -101,7 +101,7 @@
     @Override
     public MethodVisitor visitMethod(int access, String name, String desc,
             String signature, String[] exceptions) {
-        
+
         if (mDeleteReturns != null) {
             Type t = Type.getReturnType(desc);
             if (t.getSort() == Type.OBJECT) {
@@ -130,16 +130,16 @@
             (mStubAll ||
              (access & Opcodes.ACC_NATIVE) != 0) ||
              mStubMethods.contains(methodSignature)) {
-            
+
             boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
             boolean isNative = (access & Opcodes.ACC_NATIVE) != 0;
 
             // remove abstract, final and native
             access = access & ~(Opcodes.ACC_ABSTRACT | Opcodes.ACC_FINAL | Opcodes.ACC_NATIVE);
-            
+
             String invokeSignature = methodSignature + desc;
             mLog.debug("  Stub: %s (%s)", invokeSignature, isNative ? "native" : "");
-            
+
             MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
             return new StubMethodAdapter(mw, name, returnType(desc), invokeSignature,
                     isStatic, isNative);
@@ -149,7 +149,7 @@
             return super.visitMethod(access, name, desc, signature, exceptions);
         }
     }
-    
+
     /* Visits a field. Makes it public. */
     @Override
     public FieldVisitor visitField(int access, String name, String desc, String signature,
@@ -157,7 +157,7 @@
         // change access to public
         access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE);
         access |= Opcodes.ACC_PUBLIC;
-        
+
         return super.visitField(access, name, desc, signature, value);
     }
 
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
index 603284e..d6dba6a 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
@@ -22,7 +22,6 @@
 import static org.junit.Assert.assertNotNull;
 
 import com.android.tools.layoutlib.create.AsmAnalyzer.DependencyVisitor;
-import com.android.tools.layoutlib.create.LogTest.MockLog;
 
 import org.junit.After;
 import org.junit.Before;
@@ -46,9 +45,9 @@
 
     @Before
     public void setUp() throws Exception {
-        mLog = new LogTest.MockLog();
+        mLog = new MockLog();
         URL url = this.getClass().getClassLoader().getResource("data/mock_android.jar");
-        
+
         mOsJarPath = new ArrayList<String>();
         mOsJarPath.add(url.getFile());
 
@@ -69,9 +68,9 @@
                 "mock_android.dummy.InnerTest$DerivingClass",
                 "mock_android.dummy.InnerTest$MyGenerics1",
                 "mock_android.dummy.InnerTest$MyIntEnum",
-                "mock_android.dummy.InnerTest$MyStaticInnerClass",   
-                "mock_android.dummy.InnerTest$NotStaticInner1", 
-                "mock_android.dummy.InnerTest$NotStaticInner2",  
+                "mock_android.dummy.InnerTest$MyStaticInnerClass",
+                "mock_android.dummy.InnerTest$NotStaticInner1",
+                "mock_android.dummy.InnerTest$NotStaticInner2",
                 "mock_android.view.View",
                 "mock_android.view.ViewGroup",
                 "mock_android.view.ViewGroup$LayoutParams",
@@ -83,7 +82,7 @@
             },
             map.keySet().toArray());
     }
-    
+
     @Test
     public void testFindClass() throws IOException, LogAbortException {
         Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
@@ -91,7 +90,7 @@
 
         ClassReader cr = mAa.findClass("mock_android.view.ViewGroup$LayoutParams",
                 zipClasses, found);
-        
+
         assertNotNull(cr);
         assertEquals("mock_android/view/ViewGroup$LayoutParams", cr.getClassName());
         assertArrayEquals(new String[] { "mock_android.view.ViewGroup$LayoutParams" },
@@ -172,14 +171,14 @@
                 "mock_android.widget.TableLayout",
             },
             found.keySet().toArray());
-        
+
         for (String key : found.keySet()) {
             ClassReader value = found.get(key);
             assertNotNull("No value for " + key, value);
             assertEquals(key, AsmAnalyzer.classReaderToClassName(value));
         }
     }
-    
+
     @Test
     public void testDependencyVisitor() throws IOException, LogAbortException {
         Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
@@ -190,7 +189,7 @@
 
         ClassReader cr = mAa.findClass("mock_android.widget.TableLayout", zipClasses, keep);
         DependencyVisitor visitor = mAa.getVisitor(zipClasses, keep, new_keep, in_deps, out_deps);
-        
+
         // get first level dependencies
         cr.accept(visitor, 0 /* flags */);
 
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
index 7cdf79a..f4ff389 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -20,8 +20,6 @@
 
 import static org.junit.Assert.assertArrayEquals;
 
-import com.android.tools.layoutlib.create.LogTest.MockLog;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -44,9 +42,9 @@
 
     @Before
     public void setUp() throws Exception {
-        mLog = new LogTest.MockLog();
+        mLog = new MockLog();
         URL url = this.getClass().getClassLoader().getResource("data/mock_android.jar");
-        
+
         mOsJarPath = new ArrayList<String>();
         mOsJarPath.add(url.getFile());
 
@@ -65,16 +63,41 @@
 
     @Test
     public void testClassRenaming() throws IOException, LogAbortException {
-        
-        AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar,
-            null, // classes to inject in the final JAR
-            null,  // methods to force override
-            new String[] {  // classes to rename (so that we can replace them)
-                "mock_android.view.View", "mock_android.view._Original_View",
-                "not.an.actual.ClassName", "anoter.fake.NewClassName",
-            },
-            null // methods deleted from their return type.
-            );
+
+        ICreateInfo ci = new ICreateInfo() {
+            public Class<?>[] getInjectedClasses() {
+                // classes to inject in the final JAR
+                return new Class<?>[0];
+            }
+
+            public String[] getDelegateMethods() {
+                return new String[0];
+            }
+
+            public String[] getDelegateClassNatives() {
+                return new String[0];
+            }
+
+            public String[] getOverriddenMethods() {
+                // methods to force override
+                return new String[0];
+            }
+
+            public String[] getRenamedClasses() {
+                // classes to rename (so that we can replace them)
+                return new String[] {
+                        "mock_android.view.View", "mock_android.view._Original_View",
+                        "not.an.actual.ClassName", "anoter.fake.NewClassName",
+                };
+            }
+
+            public String[] getDeleteReturns() {
+                 // methods deleted from their return type.
+                return new String[0];
+            }
+        };
+
+        AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci);
 
         AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath, agen,
                 null,                 // derived from
@@ -83,7 +106,7 @@
                 });
         aa.analyze();
         agen.generate();
-        
+
         Set<String> notRenamed = agen.getClassesNotRenamed();
         assertArrayEquals(new String[] { "not/an/actual/ClassName" }, notRenamed.toArray());
     }
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java
new file mode 100644
index 0000000..0135c40
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2010 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 static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+
+/**
+ * Tests {@link ClassHasNativeVisitor}.
+ */
+public class ClassHasNativeVisitorTest {
+
+    @Test
+    public void testHasNative() throws IOException {
+        MockClassHasNativeVisitor cv = new MockClassHasNativeVisitor();
+        String className =
+                this.getClass().getCanonicalName() + "$" + ClassWithNative.class.getSimpleName();
+        ClassReader cr = new ClassReader(className);
+
+        cr.accept(cv, 0 /* flags */);
+        assertArrayEquals(new String[] { "native_method" }, cv.getMethodsFound());
+        assertTrue(cv.hasNativeMethods());
+    }
+
+    @Test
+    public void testHasNoNative() throws IOException {
+        MockClassHasNativeVisitor cv = new MockClassHasNativeVisitor();
+        String className =
+            this.getClass().getCanonicalName() + "$" + ClassWithoutNative.class.getSimpleName();
+        ClassReader cr = new ClassReader(className);
+
+        cr.accept(cv, 0 /* flags */);
+        assertArrayEquals(new String[0], cv.getMethodsFound());
+        assertFalse(cv.hasNativeMethods());
+    }
+
+    //-------
+
+    /**
+     * Overrides {@link ClassHasNativeVisitor} to collec the name of the native methods found.
+     */
+    private static class MockClassHasNativeVisitor extends ClassHasNativeVisitor {
+        private ArrayList<String> mMethodsFound = new ArrayList<String>();
+
+        public String[] getMethodsFound() {
+            return mMethodsFound.toArray(new String[mMethodsFound.size()]);
+        }
+
+        @Override
+        protected void setHasNativeMethods(boolean hasNativeMethods, String methodName) {
+            if (hasNativeMethods) {
+                mMethodsFound.add(methodName);
+            }
+            super.setHasNativeMethods(hasNativeMethods, methodName);
+        }
+    }
+
+    /**
+     * Dummy test class with a native method.
+     */
+    public static class ClassWithNative {
+        public ClassWithNative() {
+        }
+
+        public void callTheNativeMethod() {
+            native_method();
+        }
+
+        private native void native_method();
+    }
+
+    /**
+     * Dummy test class with no native method.
+     */
+    public static class ClassWithoutNative {
+        public ClassWithoutNative() {
+        }
+
+        public void someMethod() {
+        }
+    }
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
new file mode 100644
index 0000000..e8b3ea8
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2010 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.layoutlib.create.dataclass.ClassWithNative;
+import com.android.tools.layoutlib.create.dataclass.OuterClass;
+import com.android.tools.layoutlib.create.dataclass.OuterClass.InnerClass;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class DelegateClassAdapterTest {
+
+    private MockLog mLog;
+
+    private static final String NATIVE_CLASS_NAME = ClassWithNative.class.getCanonicalName();
+    private static final String OUTER_CLASS_NAME = OuterClass.class.getCanonicalName();
+    private static final String INNER_CLASS_NAME = OuterClass.class.getCanonicalName() + "$" +
+                                                   InnerClass.class.getSimpleName();
+
+    @Before
+    public void setUp() throws Exception {
+        mLog = new MockLog();
+        mLog.setVerbose(true); // capture debug error too
+    }
+
+    /**
+     * Tests that a class not being modified still works.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testNoOp() throws Throwable {
+        // create an instance of the class that will be modified
+        // (load the class in a distinct class loader so that we can trash its definition later)
+        ClassLoader cl1 = new ClassLoader(this.getClass().getClassLoader()) { };
+        Class<ClassWithNative> clazz1 = (Class<ClassWithNative>) cl1.loadClass(NATIVE_CLASS_NAME);
+        ClassWithNative instance1 = clazz1.newInstance();
+        assertEquals(42, instance1.add(20, 22));
+        try {
+            instance1.callNativeInstance(10, 3.1415, new Object[0] );
+            fail("Test should have failed to invoke callTheNativeMethod [1]");
+        } catch (UnsatisfiedLinkError e) {
+            // This is expected to fail since the native method is not implemented.
+        }
+
+        // Now process it but tell the delegate to not modify any method
+        ClassWriter cw = new ClassWriter(0 /*flags*/);
+
+        HashSet<String> delegateMethods = new HashSet<String>();
+        String internalClassName = NATIVE_CLASS_NAME.replace('.', '/');
+        DelegateClassAdapter cv = new DelegateClassAdapter(
+                mLog, cw, internalClassName, delegateMethods);
+
+        ClassReader cr = new ClassReader(NATIVE_CLASS_NAME);
+        cr.accept(cv, 0 /* flags */);
+
+        // Load the generated class in a different class loader and try it again
+
+        ClassLoader2 cl2 = null;
+        try {
+            cl2 = new ClassLoader2() {
+                @Override
+                public void testModifiedInstance() throws Exception {
+                    Class<?> clazz2 = loadClass(NATIVE_CLASS_NAME);
+                    Object i2 = clazz2.newInstance();
+                    assertNotNull(i2);
+                    assertEquals(42, callAdd(i2, 20, 22));
+
+                    try {
+                        callCallNativeInstance(i2, 10, 3.1415, new Object[0]);
+                        fail("Test should have failed to invoke callTheNativeMethod [2]");
+                    } catch (InvocationTargetException e) {
+                        // This is expected to fail since the native method has NOT been
+                        // overridden here.
+                        assertEquals(UnsatisfiedLinkError.class, e.getCause().getClass());
+                    }
+
+                    // Check that the native method does NOT have the new annotation
+                    Method[] m = clazz2.getDeclaredMethods();
+                    assertEquals("native_instance", m[2].getName());
+                    assertTrue(Modifier.isNative(m[2].getModifiers()));
+                    Annotation[] a = m[2].getAnnotations();
+                    assertEquals(0, a.length);
+                }
+            };
+            cl2.add(NATIVE_CLASS_NAME, cw);
+            cl2.testModifiedInstance();
+        } catch (Throwable t) {
+            throw dumpGeneratedClass(t, cl2);
+        }
+    }
+
+    /**
+     * {@link DelegateMethodAdapter} does not support overriding constructors yet,
+     * so this should fail with an {@link UnsupportedOperationException}.
+     *
+     * Although not tested here, the message of the exception should contain the
+     * constructor signature.
+     */
+    @Test(expected=UnsupportedOperationException.class)
+    public void testConstructorsNotSupported() throws IOException {
+        ClassWriter cw = new ClassWriter(0 /*flags*/);
+
+        String internalClassName = NATIVE_CLASS_NAME.replace('.', '/');
+
+        HashSet<String> delegateMethods = new HashSet<String>();
+        delegateMethods.add("<init>");
+        DelegateClassAdapter cv = new DelegateClassAdapter(
+                mLog, cw, internalClassName, delegateMethods);
+
+        ClassReader cr = new ClassReader(NATIVE_CLASS_NAME);
+        cr.accept(cv, 0 /* flags */);
+    }
+
+    @Test
+    public void testDelegateNative() throws Throwable {
+        ClassWriter cw = new ClassWriter(0 /*flags*/);
+        String internalClassName = NATIVE_CLASS_NAME.replace('.', '/');
+
+        HashSet<String> delegateMethods = new HashSet<String>();
+        delegateMethods.add(DelegateClassAdapter.ALL_NATIVES);
+        DelegateClassAdapter cv = new DelegateClassAdapter(
+                mLog, cw, internalClassName, delegateMethods);
+
+        ClassReader cr = new ClassReader(NATIVE_CLASS_NAME);
+        cr.accept(cv, 0 /* flags */);
+
+        // Load the generated class in a different class loader and try it
+        ClassLoader2 cl2 = null;
+        try {
+            cl2 = new ClassLoader2() {
+                @Override
+                public void testModifiedInstance() throws Exception {
+                    Class<?> clazz2 = loadClass(NATIVE_CLASS_NAME);
+                    Object i2 = clazz2.newInstance();
+                    assertNotNull(i2);
+
+                    // Use reflection to access inner methods
+                    assertEquals(42, callAdd(i2, 20, 22));
+
+                     Object[] objResult = new Object[] { null };
+                     int result = callCallNativeInstance(i2, 10, 3.1415, objResult);
+                     assertEquals((int)(10 + 3.1415), result);
+                     assertSame(i2, objResult[0]);
+
+                     // Check that the native method now has the new annotation and is not native
+                     Method[] m = clazz2.getDeclaredMethods();
+                     assertEquals("native_instance", m[2].getName());
+                     assertFalse(Modifier.isNative(m[2].getModifiers()));
+                     Annotation[] a = m[2].getAnnotations();
+                     assertEquals("LayoutlibDelegate", a[0].annotationType().getSimpleName());
+                }
+            };
+            cl2.add(NATIVE_CLASS_NAME, cw);
+            cl2.testModifiedInstance();
+        } catch (Throwable t) {
+            throw dumpGeneratedClass(t, cl2);
+        }
+    }
+
+    @Test
+    public void testDelegateInner() throws Throwable {
+        // We'll delegate the "get" method of both the inner and outer class.
+        HashSet<String> delegateMethods = new HashSet<String>();
+        delegateMethods.add("get");
+
+        // Generate the delegate for the outer class.
+        ClassWriter cwOuter = new ClassWriter(0 /*flags*/);
+        String outerClassName = OUTER_CLASS_NAME.replace('.', '/');
+        DelegateClassAdapter cvOuter = new DelegateClassAdapter(
+                mLog, cwOuter, outerClassName, delegateMethods);
+        ClassReader cr = new ClassReader(OUTER_CLASS_NAME);
+        cr.accept(cvOuter, 0 /* flags */);
+
+        // Generate the delegate for the inner class.
+        ClassWriter cwInner = new ClassWriter(0 /*flags*/);
+        String innerClassName = INNER_CLASS_NAME.replace('.', '/');
+        DelegateClassAdapter cvInner = new DelegateClassAdapter(
+                mLog, cwInner, innerClassName, delegateMethods);
+        cr = new ClassReader(INNER_CLASS_NAME);
+        cr.accept(cvInner, 0 /* flags */);
+
+        // Load the generated classes in a different class loader and try them
+        ClassLoader2 cl2 = null;
+        try {
+            cl2 = new ClassLoader2() {
+                @Override
+                public void testModifiedInstance() throws Exception {
+
+                    // Check the outer class
+                    Class<?> outerClazz2 = loadClass(OUTER_CLASS_NAME);
+                    Object o2 = outerClazz2.newInstance();
+                    assertNotNull(o2);
+
+                    // The original Outer.get returns 1+10+20,
+                    // but the delegate makes it return 4+10+20
+                    assertEquals(4+10+20, callGet(o2, 10, 20));
+
+                    // Check the inner class. Since it's not a static inner class, we need
+                    // to use the hidden constructor that takes the outer class as first parameter.
+                    Class<?> innerClazz2 = loadClass(INNER_CLASS_NAME);
+                    Constructor<?> innerCons = innerClazz2.getConstructor(
+                                                                new Class<?>[] { outerClazz2 });
+                    Object i2 = innerCons.newInstance(new Object[] { o2 });
+                    assertNotNull(i2);
+
+                    // The original Inner.get returns 3+10+20,
+                    // but the delegate makes it return 6+10+20
+                    assertEquals(6+10+20, callGet(i2, 10, 20));
+                }
+            };
+            cl2.add(OUTER_CLASS_NAME, cwOuter.toByteArray());
+            cl2.add(INNER_CLASS_NAME, cwInner.toByteArray());
+            cl2.testModifiedInstance();
+        } catch (Throwable t) {
+            throw dumpGeneratedClass(t, cl2);
+        }
+    }
+
+    //-------
+
+    /**
+     * A class loader than can define and instantiate our modified classes.
+     * <p/>
+     * The trick here is that this class loader will test our <em>modified</em> version
+     * of the classes, the one with the delegate calls.
+     * <p/>
+     * Trying to do so in the original class loader generates all sort of link issues because
+     * there are 2 different definitions of the same class name. This class loader will
+     * define and load the class when requested by name and provide helpers to access the
+     * instance methods via reflection.
+     */
+    private abstract class ClassLoader2 extends ClassLoader {
+
+        private final Map<String, byte[]> mClassDefs = new HashMap<String, byte[]>();
+
+        public ClassLoader2() {
+            super(null);
+        }
+
+        public ClassLoader2 add(String className, byte[] definition) {
+            mClassDefs.put(className, definition);
+            return this;
+        }
+
+        public ClassLoader2 add(String className, ClassWriter rewrittenClass) {
+            mClassDefs.put(className, rewrittenClass.toByteArray());
+            return this;
+        }
+
+        private Set<Entry<String, byte[]>> getByteCode() {
+            return mClassDefs.entrySet();
+        }
+
+        @SuppressWarnings("unused")
+        @Override
+        protected Class<?> findClass(String name) throws ClassNotFoundException {
+            try {
+                return super.findClass(name);
+            } catch (ClassNotFoundException e) {
+
+                byte[] def = mClassDefs.get(name);
+                if (def != null) {
+                    // Load the modified ClassWithNative from its bytes representation.
+                    return defineClass(name, def, 0, def.length);
+                }
+
+                try {
+                    // Load everything else from the original definition into the new class loader.
+                    ClassReader cr = new ClassReader(name);
+                    ClassWriter cw = new ClassWriter(0);
+                    cr.accept(cw, 0);
+                    byte[] bytes = cw.toByteArray();
+                    return defineClass(name, bytes, 0, bytes.length);
+
+                } catch (IOException ioe) {
+                    throw new RuntimeException(ioe);
+                }
+            }
+        }
+
+        /**
+         * Accesses {@link OuterClass#get()} or {@link InnerClass#get() }via reflection.
+         */
+        public int callGet(Object instance, int a, long b) throws Exception {
+            Method m = instance.getClass().getMethod("get",
+                    new Class<?>[] { int.class, long.class } );
+
+            Object result = m.invoke(instance, new Object[] { a, b });
+            return ((Integer) result).intValue();
+        }
+
+        /**
+         * Accesses {@link ClassWithNative#add(int, int)} via reflection.
+         */
+        public int callAdd(Object instance, int a, int b) throws Exception {
+            Method m = instance.getClass().getMethod("add",
+                    new Class<?>[] { int.class, int.class });
+
+            Object result = m.invoke(instance, new Object[] { a, b });
+            return ((Integer) result).intValue();
+        }
+
+        /**
+         * Accesses {@link ClassWithNative#callNativeInstance(int, double, Object[])}
+         * via reflection.
+         */
+        public int callCallNativeInstance(Object instance, int a, double d, Object[] o)
+                throws Exception {
+            Method m = instance.getClass().getMethod("callNativeInstance",
+                    new Class<?>[] { int.class, double.class, Object[].class });
+
+            Object result = m.invoke(instance, new Object[] { a, d, o });
+            return ((Integer) result).intValue();
+        }
+
+        public abstract void testModifiedInstance() throws Exception;
+    }
+
+    /**
+     * For debugging, it's useful to dump the content of the generated classes
+     * along with the exception that was generated.
+     *
+     * However to make it work you need to pull in the org.objectweb.asm.util.TraceClassVisitor
+     * class and associated utilities which are found in the ASM source jar. Since we don't
+     * want that dependency in the source code, we only put it manually for development and
+     * access the TraceClassVisitor via reflection if present.
+     *
+     * @param t The exception thrown by {@link ClassLoader2#testModifiedInstance()}
+     * @param cl2 The {@link ClassLoader2} instance with the generated bytecode.
+     * @return Either original {@code t} or a new wrapper {@link Throwable}
+     */
+    private Throwable dumpGeneratedClass(Throwable t, ClassLoader2 cl2) {
+        try {
+            // For debugging, dump the bytecode of the class in case of unexpected error
+            // if we can find the TraceClassVisitor class.
+            Class<?> tcvClass = Class.forName("org.objectweb.asm.util.TraceClassVisitor");
+
+            StringBuilder sb = new StringBuilder();
+            sb.append('\n').append(t.getClass().getCanonicalName());
+            if (t.getMessage() != null) {
+                sb.append(": ").append(t.getMessage());
+              }
+
+            for (Entry<String, byte[]> entry : cl2.getByteCode()) {
+                String className = entry.getKey();
+                byte[] bytes = entry.getValue();
+
+                StringWriter sw = new StringWriter();
+                PrintWriter pw = new PrintWriter(sw);
+                // next 2 lines do: TraceClassVisitor tcv = new TraceClassVisitor(pw);
+                Constructor<?> cons = tcvClass.getConstructor(new Class<?>[] { pw.getClass() });
+                Object tcv = cons.newInstance(new Object[] { pw });
+                ClassReader cr2 = new ClassReader(bytes);
+                cr2.accept((ClassVisitor) tcv, 0 /* flags */);
+
+                sb.append("\nBytecode dump: <").append(className).append(">:\n")
+                  .append(sw.toString());
+            }
+
+            // Re-throw exception with new message
+            RuntimeException ex = new RuntimeException(sb.toString(), t);
+            return ex;
+        } catch (Throwable ignore) {
+            // In case of problem, just throw the original exception as-is.
+            return t;
+        }
+    }
+
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/LogTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/LogTest.java
index 3f13158..1a5f653 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/LogTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/LogTest.java
@@ -24,33 +24,8 @@
 
 public class LogTest {
 
-    public static class MockLog extends Log {
-        StringBuilder mOut = new StringBuilder();
-        StringBuilder mErr = new StringBuilder();
-        
-        public String getOut() {
-            return mOut.toString();
-        }
-        
-        public String getErr() {
-            return mErr.toString();
-        }
-        
-        @Override
-        protected void outPrintln(String msg) {
-            mOut.append(msg);
-            mOut.append('\n');
-        }
-        
-        @Override
-        protected void errPrintln(String msg) {
-            mErr.append(msg);
-            mErr.append('\n');
-        }
-    }
-
     private MockLog mLog;
-    
+
     @Before
     public void setUp() throws Exception {
         mLog = new MockLog();
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/MockLog.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/MockLog.java
new file mode 100644
index 0000000..de750a3
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/MockLog.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 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;
+
+
+public class MockLog extends Log {
+    StringBuilder mOut = new StringBuilder();
+    StringBuilder mErr = new StringBuilder();
+
+    public String getOut() {
+        return mOut.toString();
+    }
+
+    public String getErr() {
+        return mErr.toString();
+    }
+
+    @Override
+    protected void outPrintln(String msg) {
+        mOut.append(msg);
+        mOut.append('\n');
+    }
+
+    @Override
+    protected void errPrintln(String msg) {
+        mErr.append(msg);
+        mErr.append('\n');
+    }
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative.java
new file mode 100644
index 0000000..c314853
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 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.dataclass;
+
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
+
+/**
+ * Dummy test class with a native method.
+ * The native method is not defined and any attempt to invoke it will
+ * throw an {@link UnsatisfiedLinkError}.
+ *
+ * Used by {@link DelegateClassAdapterTest}.
+ */
+public class ClassWithNative {
+    public ClassWithNative() {
+    }
+
+    public int add(int a, int b) {
+        return a + b;
+    }
+
+    // Note: it's good to have a long or double for testing parameters since they take
+    // 2 slots in the stack/locals maps.
+
+    public int callNativeInstance(int a, double d, Object[] o) {
+        return native_instance(a, d, o);
+    }
+
+    private native int native_instance(int a, double d, Object[] o);
+}
+
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative_Delegate.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative_Delegate.java
new file mode 100644
index 0000000..a3d4dc6
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative_Delegate.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 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.dataclass;
+
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
+
+/**
+ * The delegate that receives the call to {@link ClassWithNative_Delegate}'s overridden methods.
+ *
+ * Used by {@link DelegateClassAdapterTest}.
+ */
+public class ClassWithNative_Delegate {
+    public static int native_instance(ClassWithNative instance, int a, double d, Object[] o) {
+        if (o != null && o.length > 0) {
+            o[0] = instance;
+        }
+        return (int)(a + d);
+    }
+}
+
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
new file mode 100644
index 0000000..9dc2f69
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011 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.dataclass;
+
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
+
+/**
+ * Test class with an inner class.
+ *
+ * Used by {@link DelegateClassAdapterTest}.
+ */
+public class OuterClass {
+    private int mOuterValue = 1;
+    public OuterClass() {
+    }
+
+    // Outer.get returns 1 + a + b
+    // Note: it's good to have a long or double for testing parameters since they take
+    // 2 slots in the stack/locals maps.
+    public int get(int a, long b) {
+        return mOuterValue + a + (int) b;
+    }
+
+    public class InnerClass {
+        public InnerClass() {
+        }
+
+        // Inner.get returns 1+2=3 + a + b
+        public int get(int a, long b) {
+            return 2 + mOuterValue + a + (int) b;
+        }
+    }
+}
+
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java
new file mode 100644
index 0000000..3252d87
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 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.dataclass;
+
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
+
+/**
+ * Used by {@link DelegateClassAdapterTest}.
+ */
+public class OuterClass_Delegate {
+    // The delegate override of Outer.get returns 4 + a + b
+    public static int get(OuterClass instance, int a, long b) {
+        return 4 + a + (int) b;
+    }
+}
+
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_InnerClass_Delegate.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_InnerClass_Delegate.java
new file mode 100644
index 0000000..b472220
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_InnerClass_Delegate.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 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.dataclass;
+
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
+import com.android.tools.layoutlib.create.dataclass.OuterClass.InnerClass;
+
+/**
+ * Used by {@link DelegateClassAdapterTest}.
+ */
+public class OuterClass_InnerClass_Delegate {
+    // The delegate override of Inner.get return 6 + a + b
+    public static int get(OuterClass outer, InnerClass inner, int a, long b) {
+        return 6 + a + (int) b;
+    }
+}