auto import from //depot/cupcake/@135843
diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk
new file mode 100644
index 0000000..6d606a9
--- /dev/null
+++ b/tools/layoutlib/Android.mk
@@ -0,0 +1,67 @@
+#
+# 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.
+#
+LOCAL_PATH := $(my-dir)
+include $(CLEAR_VARS)
+
+#
+# Define rules to build temp_layoutlib.jar, which contains a subset of
+# the classes in framework.jar.  The layoutlib_create tool is used to
+# transform the framework jar into the temp_layoutlib jar.
+#
+
+# We need to process the framework classes.jar file, but we can't
+# depend directly on it (private vars won't be inherited correctly).
+# So, we depend on framework's BUILT file.
+built_framework_dep := \
+	$(call intermediates-dir-for,JAVA_LIBRARIES,framework)/javalib.jar
+built_framework_classes := \
+	$(call intermediates-dir-for,JAVA_LIBRARIES,framework)/classes.jar
+
+built_core_dep := \
+	$(call intermediates-dir-for,JAVA_LIBRARIES,core)/javalib.jar
+built_core_classes := \
+	$(call intermediates-dir-for,JAVA_LIBRARIES,core)/classes.jar
+
+built_layoutlib_create_jar := $(call intermediates-dir-for, \
+			JAVA_LIBRARIES,layoutlib_create,HOST)/javalib.jar
+
+# This is mostly a copy of config/host_java_library.mk
+LOCAL_MODULE := temp_layoutlib
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_MODULE_SUFFIX := $(COMMON_JAVA_PACKAGE_SUFFIX)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_BUILT_MODULE_STEM := javalib.jar
+
+#######################################
+include $(BUILD_SYSTEM)/base_rules.mk
+#######################################
+
+$(LOCAL_BUILT_MODULE): $(built_core_dep) \
+                       $(built_framework_dep) \
+                       $(built_layoutlib_create_jar)
+	@echo "host layoutlib_create: $@"
+	@mkdir -p $(dir $@)
+	@rm -f $@
+	$(hide) java -jar $(built_layoutlib_create_jar) \
+	             $@ \
+	             $(built_core_classes) \
+	             $(built_framework_classes)
+
+
+#
+# Include the subdir makefiles.
+#
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/layoutlib/api/.classpath b/tools/layoutlib/api/.classpath
new file mode 100644
index 0000000..a09ce5f
--- /dev/null
+++ b/tools/layoutlib/api/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<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="output" path="bin"/>
+</classpath>
diff --git a/tools/layoutlib/api/.project b/tools/layoutlib/api/.project
new file mode 100644
index 0000000..4e4ca3b
--- /dev/null
+++ b/tools/layoutlib/api/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>layoutlib_api</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/tools/layoutlib/api/Android.mk b/tools/layoutlib/api/Android.mk
new file mode 100644
index 0000000..d60987c
--- /dev/null
+++ b/tools/layoutlib/api/Android.mk
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+
+LOCAL_JAVA_LIBRARIES := \
+	kxml2-2.3.0
+
+LOCAL_MODULE := layoutlib_api
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java
new file mode 100644
index 0000000..df1876d
--- /dev/null
+++ b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java
@@ -0,0 +1,169 @@
+/*
+ * 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.api;
+
+import java.util.Map;
+
+/**
+ * Entry point of the Layout Lib. Implementations of this interface provide a method to compute
+ * and render a layout.
+ * <p/>
+ * <p/>{@link #getApiLevel()} gives the ability to know which methods are available.
+ * <p/>
+ * Changes in API level 3:
+ * <ul>
+ * <li>{@link #computeLayout(IXmlPullParser, Object, int, int, int, float, float, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li>
+ * <li> deprecated {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li>
+ * </ul>
+ * Changes in API level 2:
+ * <ul>
+ * <li>{@link #getApiLevel()}</li>
+ * <li>{@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li>
+ * <li>deprecated {@link #computeLayout(IXmlPullParser, Object, int, int, String, Map, Map, IProjectCallback, ILayoutLog)}</li>
+ * </ul>
+ */
+public interface ILayoutBridge {
+    
+    final int API_CURRENT = 3;
+
+    /**
+     * Returns the API level of the layout library.
+     * While no methods will ever be removed, some may become deprecated, and some new ones
+     * will appear.
+     * <p/>If calling this method throws an {@link AbstractMethodError}, then the API level
+     * should be considered to be 1. 
+     */
+    int getApiLevel();
+
+    /**
+     * Initializes the Bridge object.
+     * @param fontOsLocation the location of the fonts.
+     * @param enumValueMap map attrName => { map enumFlagName => Integer value }. 
+     * @return true if success.
+     * @since 1
+     */
+    boolean init(String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap);
+
+    /**
+     * Computes and renders a layout
+     * @param layoutDescription the {@link IXmlPullParser} letting the LayoutLib Bridge visit the
+     * layout file.
+     * @param projectKey An Object identifying the project. This is used for the cache mechanism.
+     * @param screenWidth the screen width
+     * @param screenHeight the screen height
+     * @param density the density factor for the screen.
+     * @param xdpi the screen actual dpi in X
+     * @param ydpi the screen actual dpi in Y
+     * @param themeName The name of the theme to use.
+     * @param isProjectTheme true if the theme is a project theme, false if it is a framework theme.
+     * @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 projectCallback The {@link IProjectCallback} object to get information from
+     * the project.
+     * @param logger the object responsible for displaying warning/errors to the user.
+     * @return an {@link ILayoutResult} object that contains the result of the layout.
+     * @since 3
+     */
+    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 projectCallback, ILayoutLog logger);
+
+    /**
+     * Computes and renders a layout
+     * @param layoutDescription the {@link IXmlPullParser} letting the LayoutLib Bridge visit the
+     * layout file.
+     * @param projectKey An Object identifying the project. This is used for the cache mechanism.
+     * @param screenWidth the screen width
+     * @param screenHeight the screen height
+     * @param themeName The name of the theme to use.
+     * @param isProjectTheme true if the theme is a project theme, false if it is a framework theme.
+     * @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 projectCallback The {@link IProjectCallback} object to get information from
+     * the project.
+     * @param logger the object responsible for displaying warning/errors to the user.
+     * @return an {@link ILayoutResult} object that contains the result of the layout.
+     * @deprecated Use {@link #computeLayout(IXmlPullParser, Object, int, int, int, float, float, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}
+     * @since 2
+     */
+    @Deprecated
+    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 projectCallback, ILayoutLog logger);
+
+    /**
+     * Computes and renders a layout
+     * @param layoutDescription the {@link IXmlPullParser} letting the LayoutLib Bridge visit the
+     * layout file.
+     * @param projectKey An Object identifying the project. This is used for the cache mechanism.
+     * @param screenWidth
+     * @param screenHeight
+     * @param themeName The name of the theme to use. In order to differentiate project and platform
+     * themes sharing the same name, all project themes must be prepended with a '*' character.
+     * @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 projectCallback The {@link IProjectCallback} object to get information from
+     * the project.
+     * @param logger the object responsible for displaying warning/errors to the user.
+     * @return an {@link ILayoutResult} object that contains the result of the layout.
+     * @deprecated Use {@link #computeLayout(IXmlPullParser, Object, int, int, int, float, float, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}
+     * @since 1
+     */
+    @Deprecated
+    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 projectCallback, ILayoutLog logger);
+    
+    /**
+     * Clears the resource cache for a specific project.
+     * <p/>This cache contains bitmaps and nine patches that are loaded from the disk and reused
+     * until this method is called.
+     * <p/>The cache is not configuration dependent and should only be cleared when a
+     * resource changes (at this time only bitmaps and 9 patches go into the cache).
+     * @param objectKey the key for the project.
+     * @since 1
+     */
+    void clearCaches(Object projectKey);
+}
diff --git a/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutLog.java b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutLog.java
new file mode 100644
index 0000000..cae15d3
--- /dev/null
+++ b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutLog.java
@@ -0,0 +1,42 @@
+/*
+ * 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.api;
+
+/**
+ * Callback interface to display warnings/errors that happened during the computation and
+ * rendering of the layout. 
+ */
+public interface ILayoutLog {
+    
+    /**
+     * Displays a warning message.
+     * @param message the message to display.
+     */
+    void warning(String message);
+    
+    /**
+     * Displays an error message.
+     * @param message the message to display.
+     */
+    void error(String message);
+    
+    /**
+     * Displays an exception
+     * @param t the {@link Throwable} to display.
+     */
+    void error(Throwable t);
+}
diff --git a/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutResult.java b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutResult.java
new file mode 100644
index 0000000..5a06349
--- /dev/null
+++ b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutResult.java
@@ -0,0 +1,97 @@
+/*
+ * 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.api;
+
+import java.awt.image.BufferedImage;
+
+/**
+ * The result of a layout computation through
+ * {@link ILayoutLibBridge#computeLayout(IXmlPullParser, int, int, String, java.util.Map, java.util.Map, java.util.Map, IFontLoader, ILayoutLibLog, ICustomViewLoader)}
+ */
+public interface ILayoutResult {
+    /** Sucess return code */
+    final static int SUCCESS = 0;
+    /** Error return code.
+     *  <p/>See {@link #getErrorMessage()}
+     */ 
+    final static int ERROR = 1;
+    
+    /**
+     * Returns the result code.
+     * @see #SUCCESS
+     * @see #ERROR
+     */
+    int getSuccess();
+
+    /**
+     * Returns the {@link ILayoutViewInfo} object for the top level view.
+     */
+    ILayoutViewInfo getRootView();
+
+    /**
+     * Returns the rendering of the full layout.
+     */
+    BufferedImage getImage();
+
+    /**
+     * Returns the error message.
+     * <p/>Only valid when {@link #getSuccess()} returns {@link #ERROR}
+     */
+    String getErrorMessage();
+
+    /**
+     * Layout information for a specific view.
+     */
+    public interface ILayoutViewInfo {
+
+        /**
+         * Returns the list of children views.
+         */
+        ILayoutViewInfo[] getChildren();
+        
+        /**
+         * Returns the key associated with the node.
+         * @see IXmlPullParser#getViewKey()
+         */
+        Object getViewKey();
+        
+        /**
+         * Returns the name of the view.
+         */
+        String getName();
+        
+        /**
+         * Returns the left of the view bounds.
+         */
+        int getLeft();
+
+        /**
+         * Returns the top of the view bounds.
+         */
+        int getTop();
+
+        /**
+         * Returns the right of the view bounds.
+         */
+        int getRight();
+
+        /**
+         * Returns the bottom of the view bounds.
+         */
+        int getBottom();
+    }
+}
diff --git a/tools/layoutlib/api/src/com/android/layoutlib/api/IProjectCallback.java b/tools/layoutlib/api/src/com/android/layoutlib/api/IProjectCallback.java
new file mode 100644
index 0000000..5ad5082
--- /dev/null
+++ b/tools/layoutlib/api/src/com/android/layoutlib/api/IProjectCallback.java
@@ -0,0 +1,74 @@
+/*
+ * 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.api;
+
+/**
+ * Callback for project information needed by the Layout Library.
+ * Classes implementing this interface provide methods giving access to some project data, like
+ * resource resolution, namespace information, and instantiation of custom view.
+ */
+public interface IProjectCallback {
+    
+    /**
+     * Loads a custom view with the given constructor signature and arguments.
+     * @param name The fully qualified name of the class.
+     * @param constructorSignature The signature of the class to use
+     * @param constructorArgs The arguments to use on the constructor
+     * @return A newly instantiated android.view.View object.
+     * @throws ClassNotFoundException.
+     * @throws Exception 
+     */
+    @SuppressWarnings("unchecked")
+    Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
+        throws ClassNotFoundException, Exception;
+    
+    /**
+     * Returns the namespace of the application.
+     * <p/>This lets the Layout Lib load custom attributes for custom views.
+     */
+    String getNamespace();
+    
+    /**
+     * Resolves the id of a resource Id.
+     * <p/>The resource id is the value of a <code>R.&lt;type&gt;.&lt;name&gt;</code>, and
+     * this method will return both the type and name of the resource.
+     * @param id the Id to resolve.
+     * @return an array of 2 strings containing the resource name and type, or null if the id
+     * does not match any resource. 
+     */
+    String[] resolveResourceValue(int id);
+    
+    /**
+     * Resolves the id of a resource Id of type int[]
+     * <p/>The resource id is the value of a R.styleable.&lt;name&gt;, and this method will
+     * return the name of the resource.
+     * @param id the Id to resolve.
+     * @return the name of the resource or <code>null</code> if not found.
+     */
+    String resolveResourceValue(int[] id);
+    
+    /**
+     * Returns the id of a resource.
+     * <p/>The provided type and name must match an existing constant defined as
+     * <code>R.&lt;type&gt;.&lt;name&gt;</code>.
+     * @param type the type of the resource
+     * @param name the name of the resource
+     * @return an Integer containing the resource Id, or <code>null</code> if not found.
+     */
+    Integer getResourceValue(String type, String name);
+
+}
diff --git a/tools/layoutlib/api/src/com/android/layoutlib/api/IResourceValue.java b/tools/layoutlib/api/src/com/android/layoutlib/api/IResourceValue.java
new file mode 100644
index 0000000..1da9508
--- /dev/null
+++ b/tools/layoutlib/api/src/com/android/layoutlib/api/IResourceValue.java
@@ -0,0 +1,44 @@
+/*
+ * 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.api;
+
+/**
+ * Represents an android resource with a name and a string value.
+ */
+public interface IResourceValue {
+    
+    /**
+     * Returns the type of the resource. For instance "drawable", "color", etc...
+     */
+    String getType();
+
+    /**
+     * Returns the name of the resource, as defined in the XML.
+     */
+    String getName();
+
+    /**
+     * Returns the value of the resource, as defined in the XML. This can be <code>null</code>
+     */
+    String getValue();
+    
+    /**
+     * Returns whether the resource is a framework resource (<code>true</code>) or a project
+     * resource (<code>false</false>).
+     */
+    boolean isFramework();
+}
diff --git a/tools/layoutlib/api/src/com/android/layoutlib/api/IStyleResourceValue.java b/tools/layoutlib/api/src/com/android/layoutlib/api/IStyleResourceValue.java
new file mode 100644
index 0000000..2f17e69
--- /dev/null
+++ b/tools/layoutlib/api/src/com/android/layoutlib/api/IStyleResourceValue.java
@@ -0,0 +1,35 @@
+/*
+ * 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.api;
+
+
+/**
+ * Represents an android style resources with a name and a list of children {@link IResourceValue}.
+ */
+public interface IStyleResourceValue extends IResourceValue {
+    
+    /**
+     * Returns the parent style name or <code>null</code> if unknown.
+     */
+    String getParentStyle();
+
+    /**
+     * Find an item in the list by name
+     * @param name
+     */
+    IResourceValue findItem(String name);
+}
diff --git a/tools/layoutlib/api/src/com/android/layoutlib/api/IXmlPullParser.java b/tools/layoutlib/api/src/com/android/layoutlib/api/IXmlPullParser.java
new file mode 100644
index 0000000..cd43c56
--- /dev/null
+++ b/tools/layoutlib/api/src/com/android/layoutlib/api/IXmlPullParser.java
@@ -0,0 +1,36 @@
+/*
+ * 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.api;
+
+import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo;
+
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Extended version of {@link XmlPullParser} to use with 
+ * {@link ILayoutLibBridge#computeLayout(XmlPullParser, int, int, String, java.util.Map, java.util.Map, java.util.Map, com.android.layoutlib.api.ILayoutLibBridge.IFontInfo)}
+ */
+public interface IXmlPullParser extends XmlPullParser {
+    
+    /**
+     * Returns a key for the current XML node.
+     * <p/>This key will be passed back in the {@link ILayoutViewInfo} objects, allowing association
+     * of a particular XML node with its result from the layout computation.
+     */
+    Object getViewKey();
+}
+
diff --git a/tools/layoutlib/bridge/.classpath b/tools/layoutlib/bridge/.classpath
new file mode 100644
index 0000000..175a98b
--- /dev/null
+++ b/tools/layoutlib/bridge/.classpath
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry excluding="org/kxml2/io/" kind="src" path="src"/>
+	<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="output" path="bin"/>
+</classpath>
diff --git a/tools/layoutlib/bridge/.project b/tools/layoutlib/bridge/.project
new file mode 100644
index 0000000..e36e71b
--- /dev/null
+++ b/tools/layoutlib/bridge/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>layoutlib_bridge</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/tools/layoutlib/bridge/Android.mk b/tools/layoutlib/bridge/Android.mk
new file mode 100644
index 0000000..b2010d5
--- /dev/null
+++ b/tools/layoutlib/bridge/Android.mk
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+
+LOCAL_JAVA_LIBRARIES := \
+	kxml2-2.3.0 \
+	layoutlib_api \
+	ninepatch
+
+LOCAL_STATIC_JAVA_LIBRARIES := temp_layoutlib
+
+LOCAL_MODULE := layoutlib
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
new file mode 100644
index 0000000..6bc01b1
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
@@ -0,0 +1,242 @@
+/*
+ * 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.BridgeCanvas;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+public final class Bitmap extends _Original_Bitmap {
+    
+    private BufferedImage mImage;
+
+    public Bitmap(File input) throws IOException {
+        super(1, true, null);
+        
+        mImage = ImageIO.read(input);
+    }
+    
+    Bitmap(BufferedImage image) {
+        super(1, true, null);
+        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());
+
+        BridgeCanvas canvas = new BridgeCanvas(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);
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java
new file mode 100644
index 0000000..8bf7fcc
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java
@@ -0,0 +1,41 @@
+/*
+ * 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 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;
+    }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java
new file mode 100644
index 0000000..968a597
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+/** 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
+    }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
new file mode 100644
index 0000000..1a0dc05
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
@@ -0,0 +1,71 @@
+/*
+ * 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.GradientPaint;
+import java.awt.Color;
+import java.awt.Paint;
+
+public class LinearGradient extends Shader {
+    
+    private GradientPaint mGradientPaint;
+
+    /** 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) {
+        if (colors.length < 2) {
+            throw new IllegalArgumentException("needs >= 2 number of colors");
+        }
+        if (positions != null && colors.length != positions.length) {
+            throw new IllegalArgumentException("color and position arrays must be of equal length");
+        }
+        
+        // FIXME implement multi color linear gradient
+    }
+
+    /** 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) {
+        mGradientPaint = new GradientPaint(x0, y0, new Color(color0, true /* hasalpha */),
+                x1,y1, new Color(color1, true /* hasalpha */), tile != TileMode.CLAMP);
+    }
+    
+    //---------- Custom Methods
+    
+    public Paint getPaint() {
+        return mGradientPaint;
+    }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix.java b/tools/layoutlib/bridge/src/android/graphics/Matrix.java
new file mode 100644
index 0000000..3f9a993
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix.java
@@ -0,0 +1,984 @@
+/*
+ * 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;
+
+
+/**
+ * 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;
+    }
+    
+    @Override
+    public void finalize() throws Throwable {
+        // pass
+    }
+    
+    //---------- 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() {
+        return new AffineTransform(mValues[0], mValues[1], mValues[2],
+                mValues[3], mValues[4], 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[7] = 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[7] = 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[7] = 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[7] = 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[7] = 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[7] = 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[7] = 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[7] = 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[7] = 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) {
+        throw new UnsupportedOperationException("STUB NEEDED");
+    }
+    
+    @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);
+        throw new UnsupportedOperationException("STUB NEEDED");
+    }
+    
+    /**
+    * 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();
+        }
+        throw new UnsupportedOperationException("STUB NEEDED");
+    }
+
+    /**
+     * 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/Paint.java b/tools/layoutlib/bridge/src/android/graphics/Paint.java
new file mode 100644
index 0000000..ade07d6
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint.java
@@ -0,0 +1,864 @@
+/*
+ * 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.Font;
+import java.awt.Toolkit;
+import java.awt.font.FontRenderContext;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * A paint implementation overridden by the LayoutLib bridge.
+ */
+public class Paint extends _Original_Paint {
+
+    private int mColor = 0xFFFFFFFF;
+    private float mTextSize = 20;
+    private float mScaleX = 1;
+    private float mSkewX = 0;
+    private Align mAlign = Align.LEFT;
+    private Style mStyle = Style.FILL;
+    private int mFlags = 0;
+    
+    private Font mFont;
+    private final FontRenderContext mFontContext = new FontRenderContext(
+            new AffineTransform(), true, true);
+    private java.awt.FontMetrics mMetrics;
+
+    @SuppressWarnings("hiding")
+    public static final int ANTI_ALIAS_FLAG       = _Original_Paint.ANTI_ALIAS_FLAG;
+    @SuppressWarnings("hiding")
+    public static final int FILTER_BITMAP_FLAG    = _Original_Paint.FILTER_BITMAP_FLAG;
+    @SuppressWarnings("hiding")
+    public static final int DITHER_FLAG           = _Original_Paint.DITHER_FLAG;
+    @SuppressWarnings("hiding")
+    public static final int UNDERLINE_TEXT_FLAG   = _Original_Paint.UNDERLINE_TEXT_FLAG;
+    @SuppressWarnings("hiding")
+    public static final int STRIKE_THRU_TEXT_FLAG = _Original_Paint.STRIKE_THRU_TEXT_FLAG;
+    @SuppressWarnings("hiding")
+    public static final int FAKE_BOLD_TEXT_FLAG   = _Original_Paint.FAKE_BOLD_TEXT_FLAG;
+    @SuppressWarnings("hiding")
+    public static final int LINEAR_TEXT_FLAG      = _Original_Paint.LINEAR_TEXT_FLAG;
+    @SuppressWarnings("hiding")
+    public static final int SUBPIXEL_TEXT_FLAG    = _Original_Paint.SUBPIXEL_TEXT_FLAG;
+    @SuppressWarnings("hiding")
+    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;
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * 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);
+    }
+
+    public Paint(int flags) {
+        setFlags(flags | DEFAULT_PAINT_FLAGS);
+        initFont();
+    }
+
+    public Paint(Paint paint) {
+        set(paint);
+        initFont();
+    }
+    
+    @Override
+    public void finalize() throws Throwable {
+        // pass
+    }
+    
+    /**
+     * Returns the {@link Font} object.
+     */
+    public Font getFont() {
+        return mFont;
+    }
+    
+    private void initFont() {
+        mTypeface = Typeface.DEFAULT;
+        updateFontObject();
+    }
+    
+    /**
+     * Update the {@link Font} object from the typeface, text size and scaling
+     */
+    private void updateFontObject() {
+        if (mTypeface != null) {
+            // get the typeface font object, and get our font object from it, based on the current size
+            mFont = mTypeface.getFont().deriveFont(mTextSize);
+            if (mScaleX != 1.0 || mSkewX != 0) {
+                // TODO: support skew
+                mFont = mFont.deriveFont(new AffineTransform(
+                        mScaleX, mSkewX, 0, 0, 1, 0));
+            }
+            
+            mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(mFont);
+        }
+    }
+    
+    //----------------------------------------
+    
+    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;
+
+            super.set(src);
+        }
+    }
+
+    @Override
+    public int getFlags() {
+        return mFlags;
+    }
+
+    @Override
+    public void setFlags(int flags) {
+        mFlags = flags;
+    }
+    
+    /**
+     * 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 (mMetrics != null) {
+            if (metrics != null) {
+                // ascent stuff should be negatif, but awt returns them as positive.
+                metrics.top = - mMetrics.getMaxAscent();
+                metrics.ascent = - mMetrics.getAscent();
+                metrics.descent = mMetrics.getDescent();
+                metrics.bottom = mMetrics.getMaxDescent();
+                metrics.leading = mMetrics.getLeading();
+            }
+    
+            return mMetrics.getHeight();
+        }
+        
+        return 0;
+    }
+
+    public int getFontMetricsInt(FontMetricsInt metrics) {
+        if (mMetrics != null) {
+            if (metrics != null) {
+                // ascent stuff should be negatif, but awt returns them as positive.
+                metrics.top = - mMetrics.getMaxAscent();
+                metrics.ascent = - mMetrics.getAscent();
+                metrics.descent = mMetrics.getDescent();
+                metrics.bottom = mMetrics.getMaxDescent();
+                metrics.leading = mMetrics.getLeading();
+            }
+    
+            return mMetrics.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) {
+        // TODO implement if needed
+        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
+    }
+
+    @Override
+    public int getFontMetricsInt(_Original_Paint.FontMetricsInt metrics) {
+        // TODO implement if needed
+        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 int getColor() {
+        return mColor;
+    }
+
+    @Override
+    public void setColor(int color) {
+        mColor = color;
+    }
+
+
+    @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;
+    }
+
+    /**
+     * 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) {
+        int filterNative = 0;
+        if (filter != null)
+            filterNative = filter.native_instance;
+        mColorFilter = filter;
+        return filter;
+    }
+
+    /**
+     * 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;
+    }
+    
+    public void setTextAlign(Align align) {
+        mAlign = align;
+    }
+    
+    @Override
+    public void setTextAlign(android.graphics._Original_Paint.Align align) {
+        // TODO implement if needed
+        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) {
+        // TODO implement if needed
+        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;
+    }
+
+    /**
+     * 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();
+    }
+
+    /**
+     * 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 (mMetrics != null) {
+            // ascent stuff should be negatif, but awt returns them as positive.
+            return - mMetrics.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 (mMetrics != null) {
+            return mMetrics.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) {
+        if (mFont != null && text != null && text.length > 0) {
+            Rectangle2D bounds = mFont.getStringBounds(text, index, index + count, mFontContext);
+            
+            return (float)bounds.getWidth();
+        }
+        
+        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) {
+        // NOTE: javadoc doesn't match. Just a guess.
+        return breakText(text,
+                0 /* start */, text.length() /* end */,
+                measureForwards, maxWidth, measuredWidth);
+    }
+
+    /**
+     * 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 (mMetrics != null) {
+            if ((index | count) < 0 || index + count > text.length
+                    || count > widths.length) {
+                throw new ArrayIndexOutOfBoundsException();
+            }
+    
+            for (int i = 0; i < count; i++) {
+                widths[i] = mMetrics.charWidth(text[i + index]);
+            }
+            
+            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;
+    }
+
+
+    /**
+     * 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) {
+        if (mFont != null) {
+            if ((index | count) < 0 || index + count > text.length) {
+                throw new ArrayIndexOutOfBoundsException();
+            }
+            if (bounds == null) {
+                throw new NullPointerException("need bounds Rect");
+            }
+            
+            Rectangle2D rect = mFont.getStringBounds(text, index, index + count, mFontContext);
+            bounds.set(0, 0, (int)rect.getWidth(), (int)rect.getHeight());
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path.java b/tools/layoutlib/bridge/src/android/graphics/Path.java
new file mode 100644
index 0000000..12d2cde
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Path.java
@@ -0,0 +1,611 @@
+/*
+ * 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/PorterDuffXfermode.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode.java
new file mode 100644
index 0000000..974ae49
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode.java
@@ -0,0 +1,40 @@
+/*
+ * 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/RadialGradient.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
new file mode 100644
index 0000000..61b693a
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
@@ -0,0 +1,62 @@
+/*
+ * 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 Shader {
+
+   /** 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) {
+       if (radius <= 0) {
+           throw new IllegalArgumentException("radius must be > 0");
+       }
+       if (colors.length < 2) {
+           throw new IllegalArgumentException("needs >= 2 number of colors");
+       }
+       if (positions != null && colors.length != positions.length) {
+           throw new IllegalArgumentException("color and position arrays must be of equal length");
+       }
+
+       // FIXME Implement shader
+   }
+
+   /** 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) {
+       if (radius <= 0) {
+           throw new IllegalArgumentException("radius must be > 0");
+       }
+       // FIXME Implement shader
+   }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader.java b/tools/layoutlib/bridge/src/android/graphics/Shader.java
new file mode 100644
index 0000000..3a9fda5
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Shader.java
@@ -0,0 +1,76 @@
+/*
+ * 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 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();
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
new file mode 100644
index 0000000..e79e970
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
@@ -0,0 +1,60 @@
+/*
+ * 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 Shader {
+
+    /**
+     * 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[]) {
+        if (colors.length < 2) {
+            throw new IllegalArgumentException("needs >= 2 number of colors");
+        }
+        if (positions != null && colors.length != positions.length) {
+            throw new IllegalArgumentException(
+                        "color and position arrays must be of equal length");
+        }
+        
+        // FIXME Implement shader
+    }
+
+    /**
+     * 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) {
+        // FIXME Implement shader
+    }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface.java b/tools/layoutlib/bridge/src/android/graphics/Typeface.java
new file mode 100644
index 0000000..e878b04
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface.java
@@ -0,0 +1,180 @@
+/*
+ * 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;
+
+/**
+ * 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 Font mFont;
+    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} object.
+     */
+    public Font getFont() {
+        return mFont;
+    }
+    
+    /** 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) {
+            return new Typeface(familyName, styleBuffer[0], font);
+        }
+        
+        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) {
+            return new Typeface(family.mFamily, styleBuffer[0], font);
+        }
+
+        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, Font f) {
+        mFamily = family;
+        mFont = f;
+        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/util/FloatMath.java b/tools/layoutlib/bridge/src/android/util/FloatMath.java
new file mode 100644
index 0000000..aae44f2
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/util/FloatMath.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2007 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.util;
+
+/**
+ * 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}.
+ *
+ * <p>On one platform, {@code FloatMath.sqrt(100)} executes in one third of the
+ * time required by {@code java.lang.Math.sqrt(100)}.</p>
+ */
+public class FloatMath {
+
+    /** Prevents instantiation. */
+    private FloatMath() {}
+
+    /**
+     * Returns the float conversion of the most positive (i.e. closest to
+     * positive infinity) integer value which is less than the argument.
+     *
+     * @param value to be converted
+     * @return the floor of value
+     */
+    public static float floor(float value) {
+        return (float)Math.floor(value);
+    }
+
+    /**
+     * Returns the float conversion of the most negative (i.e. closest to
+     * negative infinity) integer value which is greater than the argument.
+     *
+     * @param value to be converted
+     * @return the ceiling of value
+     */
+    public static float ceil(float value) {
+        return (float)Math.ceil(value);
+    }
+
+    /**
+     * Returns the closest float approximation of the sine of the argument.
+     *
+     * @param angle to compute the cosine of, in radians
+     * @return the sine of angle
+     */
+    public static  float sin(float angle) {
+        return (float)Math.sin(angle);
+    }
+
+    /**
+     * Returns the closest float approximation of the cosine of the argument.
+     *
+     * @param angle to compute the cosine of, in radians
+     * @return the cosine of angle
+     */
+    public static float cos(float angle) {
+        return (float)Math.cos(angle);
+    }
+
+    /**
+     * Returns the closest float approximation of the square root of the
+     * argument.
+     *
+     * @param value to compute sqrt of
+     * @return the square root of value
+     */
+    public static float sqrt(float value) {
+        return (float)Math.sqrt(value);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
new file mode 100644
index 0000000..0910d79
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -0,0 +1,227 @@
+/*
+ * 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.view;
+
+import com.android.layoutlib.api.IProjectCallback;
+import com.android.layoutlib.api.IResourceValue;
+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 org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import java.io.File;
+import java.io.FileReader;
+
+/**
+ * Custom implementation of {@link LayoutInflater} to handle custom views. 
+ */
+public final class BridgeInflater extends LayoutInflater {
+    
+    private final IProjectCallback mProjectCallback;
+
+    /**
+     * 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."
+    };
+
+    protected BridgeInflater(LayoutInflater original, Context newContext) {
+        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.
+     */
+    public BridgeInflater(Context context, IProjectCallback projectCallback) {
+        super(context);
+        mProjectCallback = projectCallback;
+        mConstructorArgs[0] = context;
+    }
+
+    @Override
+    public View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
+        View view = null;
+
+        try {
+            // First try to find a class using the default Android prefixes
+            for (String prefix : sClassPrefixList) {
+                try {
+                    view = createView(name, prefix, attrs);
+                    if (view != null) {
+                        break;
+                    }
+                } catch (ClassNotFoundException e) {
+                    // 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 {
+                if (view == null) {
+                    view = super.onCreateView(name, attrs);
+                }
+            } 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) {
+                    view = loadCustomView(name, attrs);
+                }
+            } catch (ClassNotFoundException e) {
+                // If the class was not found, we throw the exception directly, because this
+                // method is already expected to throw it.
+                throw e;
+            }
+        } catch (Exception e) {
+            // Wrap the real exception in a ClassNotFoundException, so that the calling method
+            // can deal with it.
+            ClassNotFoundException exception = new ClassNotFoundException("onCreateView", e);
+            throw exception;
+        }
+        
+        setupViewInContext(view, attrs);
+        
+        return view;
+    }
+    
+    @Override
+    public View createViewFromTag(String name, AttributeSet attrs) {
+        View view = null;
+        try {
+            view = super.createViewFromTag(name, attrs);
+        } catch (InflateException e) {
+            // try to load the class from using the custom view loader
+            try {
+                view = loadCustomView(name, attrs);
+            } catch (Exception e2) {
+                // 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) { 
+                    exception.initCause(e2);
+                } else {
+                    exception.initCause(e);
+                }
+                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);
+            if (layoutInfo != null) {
+                value = bridgeContext.getFrameworkResource(BridgeConstants.RES_LAYOUT,
+                        layoutInfo[0]);
+            } else {
+                layoutInfo = mProjectCallback.resolveResourceValue(resource);
+                
+                if (layoutInfo != null) {
+                    value = bridgeContext.getProjectResource(BridgeConstants.RES_LAYOUT,
+                            layoutInfo[0]);
+                }
+            }
+
+            if (value != null) {
+                File f = new File(value.getValue());
+                if (f.isFile()) {
+                    try {
+                        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.
+                    }
+                }
+            }
+        }
+        return null;
+    }
+    
+    private View loadCustomView(String name, AttributeSet attrs) throws ClassNotFoundException,
+            Exception{
+        if (mProjectCallback != null) {
+            // first get the classname in case it's not the node name
+            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;
+            }
+        }
+
+        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();
+                if (viewKey != null) {
+                    bc.addViewKey(view, viewKey);
+                }
+            }
+        }
+    }
+
+    @Override
+    public LayoutInflater cloneInContext(Context newContext) {
+        return new BridgeInflater(this, newContext);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/SurfaceView.java b/tools/layoutlib/bridge/src/android/view/SurfaceView.java
new file mode 100644
index 0000000..ce32da9
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/SurfaceView.java
@@ -0,0 +1,99 @@
+/*
+ * 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.view;
+
+import com.android.layoutlib.bridge.MockView;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+
+/**
+ * Mock version of the SurfaceView.
+ * Only non override public methods from the real SurfaceView have been added in there.
+ * Methods that take an unknown class as parameter or as return object, have been removed for now.
+ * 
+ * TODO: generate automatically.
+ *
+ */
+public class SurfaceView extends MockView {
+
+    public SurfaceView(Context context) {
+        this(context, null);
+    }
+    
+    public SurfaceView(Context context, AttributeSet attrs) {
+        this(context, attrs , 0);
+    }
+
+    public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+    
+    public SurfaceHolder getHolder() {
+        return mSurfaceHolder;
+    }
+
+    private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
+        
+        public boolean isCreating() {
+            return false;
+        }
+
+        public void addCallback(Callback callback) {
+        }
+
+        public void removeCallback(Callback callback) {
+        }
+        
+        public void setFixedSize(int width, int height) {
+        }
+
+        public void setSizeFromLayout() {
+        }
+
+        public void setFormat(int format) {
+        }
+
+        public void setType(int type) {
+        }
+
+        public void setKeepScreenOn(boolean screenOn) {
+        }
+        
+        public Canvas lockCanvas() {
+            return null;
+        }
+
+        public Canvas lockCanvas(Rect dirty) {
+            return null;
+        }
+
+        public void unlockCanvasAndPost(Canvas canvas) {
+        }
+
+        public Surface getSurface() {
+            return null;
+        }
+
+        public Rect getSurfaceFrame() {
+            return null;
+        }
+    };
+}
+
diff --git a/tools/layoutlib/bridge/src/android/webkit/WebView.java b/tools/layoutlib/bridge/src/android/webkit/WebView.java
new file mode 100644
index 0000000..42e4dfa
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/webkit/WebView.java
@@ -0,0 +1,261 @@
+/*
+ * 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.webkit;
+
+import com.android.layoutlib.bridge.MockView;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Picture;
+import android.os.Bundle;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Mock version of the WebView.
+ * Only non override public methods from the real WebView have been added in there.
+ * Methods that take an unknown class as parameter or as return object, have been removed for now.
+ * 
+ * TODO: generate automatically.
+ *
+ */
+public class WebView extends MockView {
+
+    /**
+     * Construct a new WebView with a Context object.
+     * @param context A Context object used to access application assets.
+     */
+    public WebView(Context context) {
+        this(context, null);
+    }
+
+    /**
+     * Construct a new WebView with layout parameters.
+     * @param context A Context object used to access application assets.
+     * @param attrs An AttributeSet passed to our parent.
+     */
+    public WebView(Context context, AttributeSet attrs) {
+        this(context, attrs, com.android.internal.R.attr.webViewStyle);
+    }
+
+    /**
+     * Construct a new WebView with layout parameters and a default style.
+     * @param context A Context object used to access application assets.
+     * @param attrs An AttributeSet passed to our parent.
+     * @param defStyle The default style resource ID.
+     */
+    public WebView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+    
+    // START FAKE PUBLIC METHODS
+    
+    public void setHorizontalScrollbarOverlay(boolean overlay) {
+    }
+
+    public void setVerticalScrollbarOverlay(boolean overlay) {
+    }
+
+    public boolean overlayHorizontalScrollbar() {
+        return false;
+    }
+
+    public boolean overlayVerticalScrollbar() {
+        return false;
+    }
+
+    public void savePassword(String host, String username, String password) {
+    }
+
+    public void setHttpAuthUsernamePassword(String host, String realm,
+            String username, String password) {
+    }
+
+    public String[] getHttpAuthUsernamePassword(String host, String realm) {
+        return null;
+    }
+
+    public void destroy() {
+    }
+
+    public static void enablePlatformNotifications() {
+    }
+
+    public static void disablePlatformNotifications() {
+    }
+
+    public WebBackForwardList saveState(Bundle outState) {
+        return null;
+    }
+
+    public WebBackForwardList restoreState(Bundle inState) {
+        return null;
+    }
+
+    public void loadUrl(String url) {
+    }
+
+    public void loadData(String data, String mimeType, String encoding) {
+    }
+
+    public void loadDataWithBaseURL(String baseUrl, String data,
+            String mimeType, String encoding, String failUrl) {
+    }
+
+    public void stopLoading() {
+    }
+
+    public void reload() {
+    }
+
+    public boolean canGoBack() {
+        return false;
+    }
+
+    public void goBack() {
+    }
+
+    public boolean canGoForward() {
+        return false;
+    }
+
+    public void goForward() {
+    }
+
+    public boolean canGoBackOrForward(int steps) {
+        return false;
+    }
+
+    public void goBackOrForward(int steps) {
+    }
+
+    public boolean pageUp(boolean top) {
+        return false;
+    }
+    
+    public boolean pageDown(boolean bottom) {
+        return false;
+    }
+
+    public void clearView() {
+    }
+    
+    public Picture capturePicture() {
+        return null;
+    }
+
+    public float getScale() {
+        return 0;
+    }
+
+    public void setInitialScale(int scaleInPercent) {
+    }
+
+    public void invokeZoomPicker() {
+    }
+
+    public void requestFocusNodeHref(Message hrefMsg) {
+    }
+
+    public void requestImageRef(Message msg) {
+    }
+
+    public String getUrl() {
+        return null;
+    }
+
+    public String getTitle() {
+        return null;
+    }
+
+    public Bitmap getFavicon() {
+        return null;
+    }
+
+    public int getProgress() {
+        return 0;
+    }
+    
+    public int getContentHeight() {
+        return 0;
+    }
+
+    public void pauseTimers() {
+    }
+
+    public void resumeTimers() {
+    }
+
+    public void clearCache() {
+    }
+
+    public void clearFormData() {
+    }
+
+    public void clearHistory() {
+    }
+
+    public void clearSslPreferences() {
+    }
+
+    public WebBackForwardList copyBackForwardList() {
+        return null;
+    }
+
+    public static String findAddress(String addr) {
+        return null;
+    }
+
+    public void documentHasImages(Message response) {
+    }
+
+    public void setWebViewClient(WebViewClient client) {
+    }
+
+    public void setDownloadListener(DownloadListener listener) {
+    }
+
+    public void setWebChromeClient(WebChromeClient client) {
+    }
+
+    public void addJavascriptInterface(Object obj, String interfaceName) {
+    }
+
+    public WebSettings getSettings() {
+        return null;
+    }
+
+    public static synchronized PluginList getPluginList() {
+        return null;
+    }
+
+    public void refreshPlugins(boolean reloadOpenPages) {
+    }
+
+    public View getZoomControls() {
+        return null;
+    }
+
+    public boolean zoomIn() {
+        return false;
+    }
+
+    public boolean zoomOut() {
+        return false;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
new file mode 100644
index 0000000..6abc452d
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -0,0 +1,932 @@
+/*
+ * 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.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 com.android.tools.layoutlib.create.MethodAdapter;
+import com.android.tools.layoutlib.create.OverrideMethod;
+
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.IBinder;
+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.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 java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 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)}.
+ */
+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 static class StaticMethodNotImplementedException extends RuntimeException {
+        private static final long serialVersionUID = 1L;
+
+        public StaticMethodNotImplementedException(String msg) {
+            super(msg);
+        }
+    }
+
+    /**
+     * Maps from id to resource name/type.
+     */
+    private final static Map<Integer, String[]> sRMap = new HashMap<Integer, String[]>();
+    /**
+     * Same as sRMap except for int[] instead of int resources.
+     */
+    private final static Map<int[], String> sRArrayMap = new HashMap<int[], String>();
+    /**
+     * Reverse map compared to sRMap, resource type -> (resource name -> id)
+     */
+    private final static Map<String, Map<String, Integer>> sRFullMap =
+        new HashMap<String, Map<String,Integer>>();
+    
+    private final static Map<Object, Map<String, Bitmap>> sProjectBitmapCache =
+        new HashMap<Object, Map<String, Bitmap>>();
+    private final static Map<Object, Map<String, NinePatch>> sProject9PatchCache =
+        new HashMap<Object, Map<String, NinePatch>>();
+
+    private final static Map<String, Bitmap> sFrameworkBitmapCache = new HashMap<String, Bitmap>();
+    private final static Map<String, NinePatch> sFramework9PatchCache =
+        new HashMap<String, NinePatch>();
+    
+    private static Map<String, Map<String, Integer>> sEnumValueMap;
+
+    /**
+     * A default logger than prints to stdout/stderr.
+     */
+    private final static ILayoutLog sDefaultLogger = new ILayoutLog() {
+        public void error(String message) {
+            System.err.println(message);
+        }
+
+        public void error(Throwable t) {
+            String message = t.getMessage();
+            if (message == null) {
+                message = t.getClass().getName();
+            }
+
+            System.err.println(message);
+        }
+
+        public void warning(String message) {
+            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}.
+     */
+    private static ILayoutLog sLogger = sDefaultLogger;
+
+    /*
+     * (non-Javadoc)
+     * @see com.android.layoutlib.api.ILayoutBridge#getApiLevel()
+     */
+    public int getApiLevel() {
+        return 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);
+    }
+    
+    private static synchronized boolean sinit(String fontOsLocation,
+            Map<String, Map<String, Integer>> enumValueMap) {
+
+        // 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
+        // throws an exception.
+        // This is useful when testing the rendering in ADT to identify static native 
+        // methods that are ignored -- layoutlib_create makes them returns 0/false/null
+        // which is generally OK yet might be a problem, so this is how you'd find out.
+        //
+        // Currently layoutlib_create only overrides static native method.
+        // Static non-natives are not overridden and thus do not get here.
+        final String debug = System.getenv("DEBUG_LAYOUT");
+        if (debug != null && !debug.equals("0") && !debug.equals("false")) {
+
+            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)" : ""));
+                        }
+                    }
+
+                    if (debug.equalsIgnoreCase("throw")) {
+                        // Throwing this exception doesn't seem that useful. It breaks
+                        // the layout editor yet doesn't display anything meaningful to the
+                        // user. Having the error in the console is just as useful. We'll
+                        // throw it only if the environment variable is "throw" or "THROW".
+                        throw new StaticMethodNotImplementedException(signature);
+                    }
+                }
+            });
+        }
+
+        // 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);
+        if (fontLoader != null) {
+            Typeface.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();
+
+                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;
+                        }
+                    }
+                }
+            }
+        } 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();
+            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;
+        }
+        
+        return computeLayout(layoutDescription, projectKey,
+                screenWidth, screenHeight, DisplayMetrics.DEFAULT_DENSITY,
+                DisplayMetrics.DEFAULT_DENSITY, DisplayMetrics.DEFAULT_DENSITY,
+                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, 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, 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.DEFAULT_DENSITY,
+                DisplayMetrics.DEFAULT_DENSITY, DisplayMetrics.DEFAULT_DENSITY,
+                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, 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) {
+        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; 
+        try {
+            // setup the display Metrics.
+            DisplayMetrics metrics = new DisplayMetrics();
+            metrics.density = density / (float) DisplayMetrics.DEFAULT_DENSITY;
+            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(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);
+            
+            // 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.getValue(),
+                        context, true /* isFramework */);
+                root.setBackgroundDrawable(d);
+            }
+
+            int w_spec = MeasureSpec.makeMeasureSpec(screenWidth, MeasureSpec.EXACTLY);
+            int h_spec = MeasureSpec.makeMeasureSpec(screenHeight - screenOffset,
+                    MeasureSpec.EXACTLY);
+
+            // measure the views
+            view.measure(w_spec, h_spec);
+            view.layout(0, screenOffset, screenWidth, screenHeight);
+            
+            // draw them
+            BridgeCanvas canvas = new BridgeCanvas(screenWidth, screenHeight - screenOffset,
+                    logger);
+            
+            root.draw(canvas);
+            canvas.dispose();
+            
+            return new LayoutResult(visit(((ViewGroup)view).getChildAt(0), context),
+                    canvas.getImage());
+        } catch (Throwable e) {
+            // get the real cause of the exception.
+            Throwable t = e;
+            while (t.getCause() != null) {
+                t = 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;
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see com.android.layoutlib.api.ILayoutLibBridge#clearCaches(java.lang.Object)
+     */
+    public void clearCaches(Object projectKey) {
+        if (projectKey != null) {
+            sProjectBitmapCache.remove(projectKey);
+            sProject9PatchCache.remove(projectKey);
+        }
+    }
+    
+    /**
+     * 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. 
+     */
+    public static String[] resolveResourceValue(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);
+    }
+    
+    /**
+     * Returns the integer id of a framework resource, from a given resource type and resource name.
+     * @param type the type of the resource
+     * @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) {
+        Map<String, Integer> map = sRFullMap.get(type);
+        if (map != null) {
+            return map.get(name);
+        }
+        
+        return null;
+    }
+    
+    static Map<String, Integer> getEnumValues(String attributeName) {
+        if (sEnumValueMap != null) {
+            return sEnumValueMap.get(attributeName);
+        }
+        
+        return null;
+    }
+
+    /**
+     * 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.
+     */
+    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 style/<name>. we want only the name
+        if (name.startsWith(BridgeConstants.REFERENCE_STYLE)) {
+            name = name.substring(BridgeConstants.REFERENCE_STYLE.length());
+        }
+
+        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: ", 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.
+     * @return the pixel height offset
+     */
+    private int getScreenOffset(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) {
+            // get value from the theme.
+            value = context.findItemInStyle(currentTheme, "windowTitleSize");
+            
+            // resolve it
+            value = context.resolveResValue(value);
+            
+            // default value
+            offset = DEFAULT_TITLE_BAR_HEIGHT;
+
+            // get the real value;
+            if (value != null) {
+                TypedValue typedValue = ResourceHelper.getValue(value.getValue());
+                if (typedValue != null) {
+                    offset = (int)typedValue.getDimension(context.getResources().mMetrics);   
+                }
+            }
+        }
+        
+        // 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) {
+            // FIXME: Right now this is hard-coded in the platform, but once there's a constant, we'll need to use it.
+            offset += DEFAULT_STATUS_BAR_HEIGHT;
+        }
+
+        return offset;
+    }
+
+    /**
+     * Returns the bitmap for a specific path, from a specific project cache, or from the
+     * framework cache.
+     * @param value the path of the bitmap
+     * @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) {
+        if (projectKey != null) {
+            Map<String, Bitmap> map = sProjectBitmapCache.get(projectKey);
+            if (map != null) {
+                return map.get(value);
+            }
+            
+            return null;
+        }
+        
+        return sFrameworkBitmapCache.get(value);
+    }
+
+    /**
+     * Sets a bitmap in a project cache or in the framework cache.
+     * @param value the path of the bitmap
+     * @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) {
+        if (projectKey != null) {
+            Map<String, Bitmap> map = sProjectBitmapCache.get(projectKey);
+
+            if (map == null) {
+                map = new HashMap<String, Bitmap>();
+                sProjectBitmapCache.put(projectKey, map);
+            }
+            
+            map.put(value, bmp);
+        }
+        
+        sFrameworkBitmapCache.put(value, bmp);
+    }
+
+    /**
+     * Returns the 9 patch 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) {
+        if (projectKey != null) {
+            Map<String, NinePatch> map = sProject9PatchCache.get(projectKey);
+            
+            if (map != null) {
+                return map.get(value);
+            }
+            
+            return null;
+        }
+        
+        return sFramework9PatchCache.get(value);
+    }
+
+    /**
+     * Sets a 9 patch 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) {
+        if (projectKey != null) {
+            Map<String, NinePatch> map = sProject9PatchCache.get(projectKey);
+
+            if (map == null) {
+                map = new HashMap<String, NinePatch>();
+                sProject9PatchCache.put(projectKey, map);
+            }
+            
+            map.put(value, ninePatch);
+        }
+        
+        sFramework9PatchCache.put(value, ninePatch);
+    }
+    
+    /**
+     * 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)
+                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, 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.
+        }
+
+        public void setInsets(IWindow window, int touchable, Rect contentInsets,
+                Rect visibleInsets) {
+            // 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) throws RemoteException {
+            // pass for now.
+        }
+
+        @SuppressWarnings("unused")
+        public void dispatchTrackball(MotionEvent arg0, long arg1) 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)
+                throws RemoteException {
+            // pass for now.
+        }
+
+        @SuppressWarnings("unused")
+        public void windowFocusChanged(boolean arg0, boolean arg1) throws RemoteException {
+            // pass for now.
+        }
+
+        public IBinder asBinder() {
+            // pass for now.
+            return null;
+        }
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
new file mode 100644
index 0000000..1fa11af
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
@@ -0,0 +1,74 @@
+/*
+ * 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 android.content.res.AssetManager;
+import android.content.res.Configuration;
+
+import java.util.Locale;
+
+public class BridgeAssetManager extends AssetManager {
+    
+    /**
+     * This initializes the static field {@link AssetManager#mSystem} which is used
+     * by methods who get a global asset manager using {@link AssetManager#getSystem()}.
+     * <p/>
+     * They will end up using our bridge asset manager.
+     * <p/>
+     * {@link Bridge} calls this method after setting up a new bridge.
+     */
+    /*package*/ static AssetManager initSystem() {
+        if (!(AssetManager.mSystem instanceof BridgeAssetManager)) {
+            // Note that AssetManager() creates a system AssetManager and we override it
+            // with our BridgeAssetManager.
+            AssetManager.mSystem = new BridgeAssetManager();
+            AssetManager.mSystem.makeStringBlocks(false);
+        }
+        return AssetManager.mSystem;
+    }
+    
+    /**
+     * Clears the static {@link AssetManager#mSystem} to make sure we don't leave objects
+     * around that would prevent us from unloading the library.
+     */
+    /*package*/ static void clearSystem() {
+        AssetManager.mSystem = null;
+    }
+    
+    private BridgeAssetManager() {
+    }
+    
+    /**
+     * Change the configuration used when retrieving resources.  Not for use by applications.
+     */
+    @Override
+    public void setConfiguration(int mcc, int mnc, String locale,
+            int orientation, int touchscreen, int density, int keyboard,
+            int keyboardHidden, int navigation, int screenWidth, int screenHeight,
+            int version)  {
+        
+        Configuration c = new Configuration();
+        c.mcc = mcc;
+        c.mnc = mnc;
+        c.locale = new Locale(locale);
+        c.touchscreen = touchscreen;
+        c.keyboard = keyboard;
+        c.keyboardHidden = keyboardHidden;
+        c.navigation = navigation;
+        c.orientation = orientation;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeCanvas.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeCanvas.java
new file mode 100644
index 0000000..70c26a7
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeCanvas.java
@@ -0,0 +1,1099 @@
+/*
+ * 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 android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.DrawFilter;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Picture;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.graphics.Shader;
+import android.graphics.Xfermode;
+import android.graphics.Paint.Align;
+import android.graphics.Paint.Style;
+import android.graphics.Region.Op;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+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 BridgeCanvas extends Canvas {
+    
+    private BufferedImage mBufferedImage;
+    private final Stack<Graphics2D> mGraphicsStack = new Stack<Graphics2D>();
+    private final ILayoutLog mLogger;
+
+    public BridgeCanvas(int width, int height, ILayoutLog logger) {
+        mLogger = logger;
+        mBufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+        mGraphicsStack.push(mBufferedImage.createGraphics());
+    }
+    
+    public BridgeCanvas(int width, int height) {
+        this(width, height, null /* logger*/);
+    }
+    
+    public BufferedImage getImage() {
+        return mBufferedImage;
+    }
+    
+    Graphics2D getGraphics2d() {
+        return mGraphicsStack.peek();
+    }
+    
+    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 getNewGraphics(Paint paint, Graphics2D g) {
+        // make new one
+        g = (Graphics2D)g.create();
+        g.setColor(new Color(paint.getColor()));
+        int alpha = paint.getAlpha();
+        float falpha = alpha / 255.f;
+        
+        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 instanceof LinearGradient) {
+            g.setPaint(((LinearGradient)shader).getPaint());
+        } else {
+            if (mLogger != null && shader != 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;
+        }
+    }
+    
+    // --------------------
+    
+    @Override
+    public void finalize() throws Throwable {
+        // pass
+    }
+    
+    /* (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() {
+        Graphics2D g = (Graphics2D)getGraphics2d().create();
+        mGraphicsStack.push(g);
+        
+        return mGraphicsStack.size() - 1;
+    }
+
+    /* (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() - 1;
+    }
+    
+    
+    /* (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);
+    }
+    
+    @Override
+    public boolean quickReject(RectF rect, EdgeType type) {
+        return false;
+    }
+
+    @Override
+    public boolean quickReject(Path path, EdgeType type) {
+        return false;
+    }
+
+    @Override
+    public boolean quickReject(float left, float top, float right, float bottom,
+                               EdgeType type) {
+        return false;
+    }
+
+    /**
+     * 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));
+        
+        getGraphics2d().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) {
+        throw new UnsupportedOperationException();
+    }
+
+    /* (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.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.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) {
+        Graphics2D g = getGraphics2d();
+        
+        g = (Graphics2D)g.create();
+        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        
+        g.setFont(paint.getFont());
+        
+        // set the color. because this only handles RGB we have to handle the alpha separately
+        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;
+            }
+        }
+        
+        g.drawChars(text, index, count, (int)x, (int)y);
+        
+        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) {
+        // get current graphisc
+        Graphics2D g = getGraphics2d();
+        
+        g = getNewGraphics(paint, g);
+
+        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) {
+        // get current graphisc
+        Graphics2D g = getGraphics2d();
+        
+        g = getNewGraphics(paint, g);
+
+        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.right, (int)rect.width(), (int)rect.height(),
+                    arcWidth, arcHeight);
+        }
+
+        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
+            g.drawRoundRect((int)rect.left, (int)rect.right, (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 current graphisc
+        Graphics2D g = getGraphics2d();
+        
+        g = getNewGraphics(paint, g);
+
+        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 current graphisc
+        Graphics2D g = getGraphics2d();
+        
+        g = getNewGraphics(paint, g);
+
+        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 current graphisc
+        Graphics2D g = getGraphics2d();
+        
+        g = getNewGraphics(paint, g);
+
+        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 current graphics
+        Graphics2D g = getGraphics2d();
+        
+        g = getNewGraphics(paint, g);
+
+        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 current graphics
+        Graphics2D g = getGraphics2d();
+        
+        g = getNewGraphics(paint, g);
+
+        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) {
+        // since SetMatrix *replaces* all the other transformation, we have to restore/save
+        restore();
+        save();
+
+        // 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#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#concat(android.graphics.Matrix)
+     */
+    @Override
+    public void concat(Matrix matrix) {
+        // TODO Auto-generated method stub
+        super.concat(matrix);
+    }
+
+    /* (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#setBitmap(android.graphics.Bitmap)
+     */
+    @Override
+    public void setBitmap(Bitmap bitmap) {
+        // TODO Auto-generated method stub
+        super.setBitmap(bitmap);
+    }
+
+    /* (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/com/android/layoutlib/bridge/BridgeConstants.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java
new file mode 100644
index 0000000..b426247
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java
@@ -0,0 +1,64 @@
+/*
+ * 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;
+
+/**
+ * Constant definition class.<br>
+ * <br>
+ * Most constants have a prefix defining the content.
+ * <ul>
+ * <li><code>WS_</code> Workspace path constant. Those are absolute paths,
+ * from the project root.</li>
+ * <li><code>OS_</code> OS path constant. These paths are different depending on the platform.</li>
+ * <li><code>FN_</code> File name constant.</li>
+ * <li><code>FD_</code> Folder name constant.</li>
+ * <li><code>EXT_</code> File extension constant. This does NOT include a dot.</li>
+ * <li><code>DOT_</code> File extension constant. This start with a dot.</li>
+ * <li><code>RE_</code> Regexp constant.</li>
+ * <li><code>NS_</code> Namespace constant.</li>
+ * <li><code>CLASS_</code> Fully qualified class name.</li>
+ * </ul>
+ *
+ */
+public class BridgeConstants {
+
+    /** Namespace for the resource XML */
+    public final static String NS_RESOURCES = "http://schemas.android.com/apk/res/android";
+
+    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_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 FILL_PARENT = "fill_parent";
+    public final static String WRAP_CONTENT = "wrap_content";
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
new file mode 100644
index 0000000..727d6f2
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
@@ -0,0 +1,107 @@
+/*
+ * 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.ContentResolver;
+import android.content.ContentServiceNative;
+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 {
+
+    public BridgeContentResolver(Context context) {
+        super(context);
+    }
+
+    @Override
+    public IContentProvider acquireProvider(Context c, String name) {
+        // ignore
+        return null;
+    }
+
+    @Override
+    public boolean releaseProvider(IContentProvider icp) {
+        // ignore
+        return false;
+    }
+    
+    /**
+     * Stub for the layoutlib bridge content resolver.
+     * <p/>
+     * The super implementation accesses the {@link ContentServiceNative#getDefault()}
+     * which returns null and would make the call crash. Instead we do nothing.
+     */
+    @Override
+    public void registerContentObserver(Uri uri, boolean notifyForDescendents,
+            ContentObserver observer) {
+        // pass
+    }
+    
+    /**
+     * Stub for the layoutlib bridge content resolver.
+     * <p/>
+     * The super implementation accesses the {@link ContentServiceNative#getDefault()}
+     * which returns null and would make the call crash. Instead we do nothing.
+     */
+    @Override
+    public void unregisterContentObserver(ContentObserver observer) {
+        // pass
+    }
+    
+    /**
+     * Stub for the layoutlib bridge content resolver.
+     * <p/>
+     * The super implementation accesses the {@link ContentServiceNative#getDefault()}
+     * which returns null and would make the call crash. Instead we do nothing.
+     */
+    @Override
+    public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+        // pass
+    }
+    
+    /**
+     * Stub for the layoutlib bridge content resolver.
+     * <p/>
+     * The super implementation accesses the {@link ContentServiceNative#getDefault()}
+     * which returns null and would make the call crash. Instead we do nothing.
+     */
+    @Override
+    public void startSync(Uri uri, Bundle extras) {
+        // pass
+    }
+    
+    /**
+     * Stub for the layoutlib bridge content resolver.
+     * <p/>
+     * The super implementation accesses the {@link ContentServiceNative#getDefault()}
+     * which returns null and would make the call crash. Instead we do nothing.
+     */
+    @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
new file mode 100644
index 0000000..baa3d53
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
@@ -0,0 +1,1148 @@
+/*
+ * 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.ServiceConnection;
+import android.content.SharedPreferences;
+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 Resources mResources;
+    private Theme mTheme;
+    private HashMap<View, Object> mViewKeyMap = new HashMap<View, Object>();
+    private IStyleResourceValue mThemeValues;
+    private final Object mProjectKey;
+    private Map<String, Map<String, IResourceValue>> mProjectResources;
+    private Map<String, Map<String, IResourceValue>> mFrameworkResources;
+    private 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 {
+            // reall 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.isPlatformFile());
+        
+        // resolve the defStyleAttr value into a IStyleResourceValue
+        IStyleResourceValue defStyleValues = null;
+        if (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());
+
+                    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 = 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;
+    }
+    
+    
+    // ------------- 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);
+        
+        // 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());
+
+        // 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.
+     * @return a {@link IResourceValue} or <code>null</code>.
+     */
+    IResourceValue findResValue(String reference) {
+        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], 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 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 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 String getPackageResourcePath() {
+        // 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 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 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 Looper getMainLooper() {
+        throw new UnsupportedOperationException();
+    }
+    
+    @Override
+    public Context getApplicationContext() {
+        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/BridgeResources.java
new file mode 100644
index 0000000..0bcc7fd
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java
@@ -0,0 +1,498 @@
+/*
+ * 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.IProjectCallback;
+import com.android.layoutlib.api.IResourceValue;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+import android.content.res.ColorStateList;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.ViewGroup.LayoutParams;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.InputStream;
+
+/**
+ * 
+ */
+public final class BridgeResources extends Resources {
+
+    private BridgeContext mContext;
+    private IProjectCallback mProjectCallback;
+    private boolean[] mPlatformResourceFlag = new boolean[1];
+    
+    /**
+     * This initializes the static field {@link Resources#mSystem} which is used
+     * by methods who get global resources using {@link Resources#getSystem()}.
+     * <p/>
+     * They will end up using our bridge resources.
+     * <p/>
+     * {@link Bridge} calls this method after setting up a new bridge.
+     */
+    /*package*/ static Resources initSystem(BridgeContext context, 
+            AssetManager assets,
+            DisplayMetrics metrics,
+            Configuration config,
+            IProjectCallback projectCallback) {
+        if (!(Resources.mSystem instanceof BridgeResources)) {
+            Resources.mSystem = new BridgeResources(context,
+                    assets,
+                    metrics,
+                    config,
+                    projectCallback);
+        }
+        return Resources.mSystem;
+    }
+    
+    /**
+     * Clears 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() {
+        if (Resources.mSystem instanceof BridgeResources) {
+            ((BridgeResources)(Resources.mSystem)).mContext = null;
+            ((BridgeResources)(Resources.mSystem)).mProjectCallback = null;
+        }
+        Resources.mSystem = null;
+    }
+
+    private BridgeResources(BridgeContext context, AssetManager assets, DisplayMetrics metrics,
+            Configuration config, IProjectCallback projectCallback) {
+        super(assets, metrics, config);
+        mContext = context;
+        mProjectCallback = projectCallback;
+    }
+    
+    public BridgeTypedArray newTypeArray(int numEntries, boolean platformFile) {
+        return new BridgeTypedArray(this, mContext, numEntries, platformFile);
+    }
+    
+    private IResourceValue getResourceValue(int id, boolean[] platformResFlag_out) {
+        // first get the String related to this id in the framework
+        String[] resourceInfo = Bridge.resolveResourceValue(id);
+        
+        if (resourceInfo != null) {
+            platformResFlag_out[0] = true;
+            return mContext.getFrameworkResource(resourceInfo[1], resourceInfo[0]);
+        }
+
+        // didn't find a match in the framework? look in the project.
+        if (mProjectCallback != null) {
+            resourceInfo = mProjectCallback.resolveResourceValue(id);
+            
+            if (resourceInfo != null) {
+                platformResFlag_out[0] = false;
+                return mContext.getProjectResource(resourceInfo[1], resourceInfo[0]);
+            }
+        }
+
+        return null;
+    }
+    
+    @Override
+    public Drawable getDrawable(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        
+        if (value != null) {
+            return ResourceHelper.getDrawable(value.getValue(), mContext, value.isFramework());
+        }
+        
+        // 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 int getColor(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        
+        if (value != null) {
+            try {
+                return ResourceHelper.getColor(value.getValue());
+            } catch (NumberFormatException e) {
+                return 0;
+            }
+        }
+        
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+        
+        // this is not used since the method above always throws
+        return 0;
+    }
+    
+    @Override
+    public ColorStateList getColorStateList(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        
+        if (value != null) {
+            try {
+                int color = ResourceHelper.getColor(value.getValue());
+                return ColorStateList.valueOf(color);
+            } catch (NumberFormatException e) {
+                return null;
+            }
+        }
+        
+        // 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 CharSequence getText(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        
+        if (value != null) {
+            return value.getValue();
+        }
+        
+        // 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 getLayout(int id) throws NotFoundException {
+        IResourceValue 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();
+                    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.
+                }
+            }
+        }
+
+        // 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 TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
+        return mContext.obtainStyledAttributes(set, attrs);
+    }
+    
+    @Override
+    public TypedArray obtainTypedArray(int id) throws NotFoundException {
+        throw new UnsupportedOperationException();
+    }
+    
+    
+    @Override
+    public float getDimension(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+
+        if (value != null) {
+            String v = value.getValue();
+
+            if (v != null) {
+                if (v.equals(BridgeConstants.FILL_PARENT)) {
+                    return LayoutParams.FILL_PARENT;
+                } else if (v.equals(BridgeConstants.WRAP_CONTENT)) {
+                    return LayoutParams.WRAP_CONTENT;
+                }
+            
+                if (ResourceHelper.stringToFloat(v, mTmpValue) &&
+                        mTmpValue.type == TypedValue.TYPE_DIMENSION) {
+                    return mTmpValue.getDimension(mMetrics);
+                }
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+        
+        // this is not used since the method above always throws
+        return 0;
+    }
+
+    @Override
+    public int getDimensionPixelOffset(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+
+        if (value != null) {
+            String v = value.getValue();
+
+            if (v != null) {
+                if (ResourceHelper.stringToFloat(v, mTmpValue) &&
+                        mTmpValue.type == TypedValue.TYPE_DIMENSION) {
+                    return TypedValue.complexToDimensionPixelOffset(mTmpValue.data, mMetrics);
+                }
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+        
+        // this is not used since the method above always throws
+        return 0;
+    }
+
+    @Override
+    public int getDimensionPixelSize(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+
+        if (value != null) {
+            String v = value.getValue();
+
+            if (v != null) {
+                if (ResourceHelper.stringToFloat(v, mTmpValue) &&
+                        mTmpValue.type == TypedValue.TYPE_DIMENSION) {
+                    return TypedValue.complexToDimensionPixelSize(mTmpValue.data, mMetrics);
+                }
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+        
+        // this is not used since the method above always throws
+        return 0;
+    }
+
+    @Override
+    public int getInteger(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        
+        if (value != null && value.getValue() != null) {
+            String v = value.getValue();
+            int radix = 10;
+            if (v.startsWith("0x")) {
+                v = v.substring(2);
+                radix = 16;
+            }
+            try {
+                return Integer.parseInt(v, radix);
+            } catch (NumberFormatException e) {
+                // return exception below
+            }
+        }
+        
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+        
+        // this is not used since the method above always throws
+        return 0;
+    }
+
+    @Override
+    public String getResourceEntryName(int resid) throws NotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getResourceName(int resid) throws NotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getResourceTypeName(int resid) throws NotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getString(int id, Object... formatArgs) throws NotFoundException {
+        String s = getString(id);
+        if (s != null) {
+            return String.format(s, formatArgs);
+            
+        }
+
+        // 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 String getString(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+        
+        if (value != null && value.getValue() != null) {
+            return value.getValue();
+        }
+
+        // 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 void getValue(int id, TypedValue outValue, boolean resolveRefs)
+            throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+
+        if (value != null) {
+            String v = value.getValue();
+
+            if (v != null) {
+                if (ResourceHelper.stringToFloat(v, outValue)) {
+                    return;
+                }
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+    }
+
+    @Override
+    public void getValue(String name, TypedValue outValue, boolean resolveRefs)
+            throws NotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public XmlResourceParser getXml(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+
+        if (value != null) {
+            String v = value.getValue();
+
+            if (v != null) {
+                // check this is a file
+                File f = new File(value.getValue());
+                if (f.isFile()) {
+                    try {
+                        KXmlParser parser = new KXmlParser();
+                        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+                        parser.setInput(new FileReader(f));
+                        
+                        return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+                    } catch (XmlPullParserException e) {
+                        NotFoundException newE = new NotFoundException();
+                        newE.initCause(e);
+                        throw newE;
+                    } catch (FileNotFoundException e) {
+                        NotFoundException newE = new NotFoundException();
+                        newE.initCause(e);
+                        throw newE;
+                    }
+                }
+            }
+        }
+
+        // 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 InputStream openRawResource(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+
+        if (value != null) {
+            String v = value.getValue();
+
+            if (v != null) {
+                // check this is a file
+                File f = new File(value.getValue());
+                if (f.isFile()) {
+                    try {
+                        return new FileInputStream(f);
+                    } catch (FileNotFoundException e) {
+                        NotFoundException newE = new NotFoundException();
+                        newE.initCause(e);
+                        throw newE;
+                    }
+                }
+            }
+        }
+
+        // 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 AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Builds and throws a {@link Resources.NotFoundException} based on a resource id and a resource type.
+     * @param id the id of the resource
+     * @throws NotFoundException
+     */
+    private void throwException(int id) throws NotFoundException {
+        // first get the String related to this id in the framework
+        String[] resourceInfo = Bridge.resolveResourceValue(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);
+        }
+        
+        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]);
+        } else {
+            message = String.format(
+                    "Could not resolve resource value: 0x%1$X.", id);
+        }
+        
+        throw new NotFoundException(message);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java
new file mode 100644
index 0000000..f5da91d
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java
@@ -0,0 +1,781 @@
+/*
+ * 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.internal.util.XmlUtils;
+import com.android.layoutlib.api.IResourceValue;
+import com.android.layoutlib.api.IStyleResourceValue;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.ViewGroup.LayoutParams;
+
+import java.io.File;
+import java.io.FileReader;
+import java.util.Map;
+
+/**
+ * TODO: describe.
+ */
+public final class BridgeTypedArray extends TypedArray {
+    
+    @SuppressWarnings("hiding")
+    private BridgeResources mResources;
+    private BridgeContext mContext;
+    @SuppressWarnings("hiding")
+    private IResourceValue[] mData;
+    private String[] mNames;
+    private final boolean mPlatformFile;
+    
+    public BridgeTypedArray(BridgeResources resources, BridgeContext context, int len,
+            boolean platformFile) {
+        super(null, null, null, 0);
+        mResources = resources;
+        mContext = context;
+        mPlatformFile = platformFile;
+        mData = new IResourceValue[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;
+        mNames[index] = name;
+    }
+    
+    /**
+     * Seals the array after all calls to {@link #bridgeSetValue(int, String, IResourceValue)} have
+     * been done.
+     * <p/>This allows to compute the list of non default values, permitting
+     * {@link #getIndexCount()} to return the proper value.
+     */
+    public void sealArray() {
+        // fills TypedArray.mIndices which is used to implement getIndexCount/getIndexAt
+        // first count the array size
+        int count = 0;
+        for (IResourceValue data : mData) {
+            if (data != null) {
+                count++;
+            }
+        }
+        
+        // allocate the table with an extra to store the size
+        mIndices = new int[count+1];
+        mIndices[0] = count;
+        
+        // fill the array with the indices.
+        int index = 1;
+        for (int i = 0 ; i < mData.length ; i++) {
+            if (mData[i] != null) {
+                mIndices[index++] = i;
+            }
+        }
+    }
+
+    /**
+     * Return the number of values in this array.
+     */
+    @Override
+    public int length() {
+        return mData.length;
+    }
+    
+    /**
+     * Return the Resources object this array was loaded from.
+     */
+    @Override
+    public Resources getResources() {
+        return mResources;
+    }
+    
+    /**
+     * Retrieve the styled string value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return CharSequence holding string data.  May be styled.  Returns 
+     *         null if the attribute is not defined.
+     */
+    @Override
+    public CharSequence getText(int index) {
+        if (mData[index] != null) {
+            // FIXME: handle styled strings!
+            return mData[index].getValue();
+        }
+
+        return null;
+    }
+
+    /**
+     * Retrieve the string value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return String holding string data.  Any styling information is
+     * removed.  Returns null if the attribute is not defined.
+     */
+    @Override
+    public String getString(int index) {
+        if (mData[index] != null) {
+            return mData[index].getValue();
+        }
+        
+        return null;
+    }
+
+    /**
+     * Retrieve the boolean value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined.
+     * 
+     * @return Attribute boolean value, or defValue if not defined.
+     */
+    @Override
+    public boolean getBoolean(int index, boolean defValue) {
+        if (mData[index] == null) {
+            return defValue;
+        }
+
+        String s = mData[index].getValue();
+        if (s != null) {
+            return XmlUtils.convertValueToBoolean(s, defValue);
+        }
+        
+        return defValue;
+    }
+
+    /**
+     * Retrieve the integer value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined.
+     * 
+     * @return Attribute int value, or defValue if not defined.
+     */
+    @Override
+    public int getInt(int index, int defValue) {
+        if (mData[index] == null) {
+            return defValue;
+        }
+
+        String s = mData[index].getValue();
+        
+        try {
+            return (s == null) ? defValue : XmlUtils.convertValueToInt(s, defValue);
+        } catch (NumberFormatException e) {
+            // pass
+        }
+
+        // Field is not null and is not an integer.
+        // Check for possible constants and try to find them.
+        // Get the map of attribute-constant -> IntegerValue
+        Map<String, Integer> map = Bridge.getEnumValues(mNames[index]);
+        
+        if (map != null) {
+            // accumulator to store the value of the 1+ constants.
+            int result = 0;
+            
+            // split the value in case this is a mix of several flags.
+            String[] keywords = s.split("\\|");
+            for (String keyword : keywords) {
+                Integer i = map.get(keyword.trim());
+                if (i != null) {
+                    result |= i.intValue();
+                } else {
+                    mContext.getLogger().warning(String.format(
+                            "Unknown constant \"%s\" in attribute \"%2$s\"",
+                            keyword, mNames[index]));
+                }
+            }
+            return result;
+        }
+
+        return defValue;
+    }
+
+    /**
+     * Retrieve the float value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return Attribute float value, or defValue if not defined..
+     */
+    @Override
+    public float getFloat(int index, float defValue) {
+        if (mData[index] == null) {
+            return defValue;
+        }
+
+        String s = mData[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]));
+                
+                // we'll return the default value below.
+            }
+        }
+        return defValue;
+    }
+    
+    /**
+     * Retrieve the color value for the attribute at <var>index</var>.  If
+     * the attribute references a color resource holding a complex
+     * {@link android.content.res.ColorStateList}, then the default color from
+     * the set is returned.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute color value, or defValue if not defined.
+     */
+    @Override
+    public int getColor(int index, int defValue) {
+        if (mData[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.
+        }
+
+        return defValue;
+    }
+
+    /**
+     * Retrieve the ColorStateList for the attribute at <var>index</var>.
+     * The value may be either a single solid color or a reference to
+     * a color or complex {@link android.content.res.ColorStateList} description.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return ColorStateList for the attribute, or null if not defined.
+     */
+    @Override
+    public ColorStateList getColorStateList(int index) {
+        if (mData[index] == null) {
+            return null;
+        }
+
+        String value = mData[index].getValue();
+
+        if (value == null) {
+            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.
+        }
+        
+        // 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;
+    }
+    
+    /**
+     * Retrieve the integer value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute integer value, or defValue if not defined.
+     */
+    @Override
+    public int getInteger(int index, int defValue) {
+        if (mData[index] == null) {
+            return defValue;
+        }
+
+        String s = mData[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]));
+
+                // The default value is returned below.
+            }
+        }
+        
+        return defValue;
+    }
+
+    /**
+     * Retrieve a dimensional unit attribute at <var>index</var>.  Unit 
+     * conversions are based on the current {@link DisplayMetrics} 
+     * associated with the resources this {@link TypedArray} object 
+     * came from. 
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute dimension value multiplied by the appropriate 
+     * metric, or defValue if not defined.
+     * 
+     * @see #getDimensionPixelOffset
+     * @see #getDimensionPixelSize
+     */
+    @Override
+    public float getDimension(int index, float defValue) {
+        if (mData[index] == null) {
+            return defValue;
+        }
+
+        String s = mData[index].getValue();
+
+        if (s == null) {
+            return defValue;
+        } else if (s.equals(BridgeConstants.FILL_PARENT)) {
+            return LayoutParams.FILL_PARENT;
+        } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
+            return LayoutParams.WRAP_CONTENT;
+        }
+        
+        if (ResourceHelper.stringToFloat(s, mValue)) {
+            return mValue.getDimension(mResources.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]));
+
+        return defValue;
+    }
+
+    /**
+     * Retrieve a dimensional unit attribute at <var>index</var> for use
+     * as an offset in raw pixels.  This is the same as
+     * {@link #getDimension}, except the returned value is converted to
+     * integer pixels for you.  An offset conversion involves simply
+     * truncating the base value to an integer.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute dimension value multiplied by the appropriate 
+     * metric and truncated to integer pixels, or defValue if not defined.
+     * 
+     * @see #getDimension
+     * @see #getDimensionPixelSize
+     */
+    @Override
+    public int getDimensionPixelOffset(int index, int defValue) {
+        return (int) getDimension(index, defValue);
+    }
+
+    /**
+     * Retrieve a dimensional unit attribute at <var>index</var> for use
+     * as a size in raw pixels.  This is the same as
+     * {@link #getDimension}, except the returned value is converted to
+     * integer pixels for use as a size.  A size conversion involves
+     * rounding the base value, and ensuring that a non-zero base value
+     * is at least one pixel in size.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute dimension value multiplied by the appropriate 
+     * metric and truncated to integer pixels, or defValue if not defined.
+     *  
+     * @see #getDimension
+     * @see #getDimensionPixelOffset
+     */
+    @Override
+    public int getDimensionPixelSize(int index, int defValue) {
+        if (mData[index] == null) {
+            return defValue;
+        }
+
+        String s = mData[index].getValue();
+
+        if (s == null) {
+            return defValue;
+        } else if (s.equals(BridgeConstants.FILL_PARENT)) {
+            return LayoutParams.FILL_PARENT;
+        } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
+            return LayoutParams.WRAP_CONTENT;
+        }
+        
+        // FIXME huh?
+
+        float f = getDimension(index, defValue);
+        final int res = (int)(f+0.5f);
+        if (res != 0) return res;
+        if (f == 0) return 0;
+        if (f > 0) return 1;
+
+        throw new UnsupportedOperationException("Can't convert to dimension: " +
+                Integer.toString(index));
+    }
+
+    /**
+     * Special version of {@link #getDimensionPixelSize} for retrieving
+     * {@link android.view.ViewGroup}'s layout_width and layout_height
+     * attributes.  This is only here for performance reasons; applications
+     * should use {@link #getDimensionPixelSize}.
+     * 
+     * @param index Index of the attribute to retrieve.
+     * @param name Textual name of attribute for error reporting.
+     * 
+     * @return Attribute dimension value multiplied by the appropriate 
+     * metric and truncated to integer pixels.
+     */
+    @Override
+    public int getLayoutDimension(int index, String name) {
+        return getDimensionPixelSize(index, 0);
+    }
+
+    /**
+     * Retrieve a fractional unit attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve. 
+     * @param base The base value of this fraction.  In other words, a 
+     *             standard fraction is multiplied by this value.
+     * @param pbase The parent base value of this fraction.  In other 
+     *             words, a parent fraction (nn%p) is multiplied by this
+     *             value.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute fractional value multiplied by the appropriate 
+     * base value, or defValue if not defined. 
+     */
+    @Override
+    public float getFraction(int index, int base, int pbase, float defValue) {
+        if (mData[index] == null) {
+            return defValue;
+        }
+
+        String value = mData[index].getValue();
+        if (value == null) {
+            return defValue;
+        }
+        
+        if (ResourceHelper.stringToFloat(value, mValue)) {
+            return mValue.getFraction(base, pbase);
+        }
+
+        // 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]));
+        
+        return defValue;
+    }
+
+    /**
+     * Retrieve the resource identifier for the attribute at
+     * <var>index</var>.  Note that attribute resource as resolved when 
+     * the overall {@link TypedArray} object is retrieved.  As a 
+     * result, this function will return the resource identifier of the 
+     * final resource value that was found, <em>not</em> necessarily the 
+     * original resource that was specified by the attribute. 
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute resource identifier, or defValue if not defined.
+     */
+    @Override
+    public int getResourceId(int index, int defValue) {
+        // get the IResource for this index
+        IResourceValue resValue = mData[index];
+        
+        // no data, return the default value.
+        if (resValue == null) {
+            return defValue;
+        }
+
+        // check if this is a style resource
+        if (resValue instanceof IStyleResourceValue) {
+            // get the id that will represent this style.
+            return mContext.getDynamicIdByStyle((IStyleResourceValue)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
+        // (and getValue() returning null!). We need to handle this!
+        if (resValue.getType() != null && resValue.getType().equals(BridgeConstants.RES_ID)) {
+            // if this is a framework id
+            if (mPlatformFile || resValue.isFramework()) {
+                // look for idName in the android R classes
+                return mContext.getFrameworkIdValue(resValue.getName(), defValue);
+            }
+
+            // look for idName in the project R class.
+            return mContext.getProjectIdValue(resValue.getName(), defValue);
+        }
+
+        // else, try to get the value, and resolve it somehow.
+        String value = resValue.getValue();
+        if (value == null) {
+            return defValue;
+        }
+        
+        // if the value is just an integer, return it.
+        try {
+            int i = Integer.parseInt(value);
+            if (Integer.toString(i).equals(value)) {
+                return i;
+            }
+        } catch (NumberFormatException e) {
+            // pass
+        }
+
+        // Handle the @id/<name>, @+id/<name> and @android:id/<name>
+        // We need to return the exact value that was compiled (from the various R classes),
+        // as these values can be reused internally with calls to findViewById().
+        // There's a trick with platform layouts that not use "android:" but their IDs are in
+        // fact in the android.R and com.android.internal.R classes.
+        // The field mPlatformFile will indicate that all IDs are to be looked up in the android R
+        // classes exclusively.
+        
+        // if this is a reference to an id, find it.
+        if (value.startsWith("@id/") || value.startsWith("@+") ||
+                value.startsWith("@android:id/")) {
+            
+            int pos = value.indexOf('/');
+            String idName = value.substring(pos + 1);
+            
+            // 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);
+            }
+
+            // look for idName in the project R class.
+            return mContext.getProjectIdValue(idName, defValue);
+        }
+
+        // not a direct id valid reference? resolve it
+        Integer idValue = null;
+        
+        if (resValue.isFramework()) {
+            idValue = Bridge.getResourceValue(resValue.getType(), resValue.getName());
+        } else {
+            idValue = mContext.getProjectCallback().getResourceValue(
+                    resValue.getType(), 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]));
+        return defValue;
+    }
+
+    /**
+     * Retrieve the Drawable for the attribute at <var>index</var>.  This
+     * gets the resource ID of the selected attribute, and uses
+     * {@link Resources#getDrawable Resources.getDrawable} of the owning
+     * Resources object to retrieve its Drawable.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return Drawable for the attribute, or null if not defined.
+     */
+    @Override
+    public Drawable getDrawable(int index) {
+        if (mData[index] == null) {
+            return null;
+        }
+
+        String value = mData[index].getValue();
+        if (value == null || BridgeConstants.REFERENCE_NULL.equals(value)) {
+            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\"", value, mNames[index]));
+
+        return null;
+    }
+
+
+    /**
+     * Retrieve the CharSequence[] for the attribute at <var>index</var>.
+     * This gets the resource ID of the selected attribute, and uses
+     * {@link Resources#getTextArray Resources.getTextArray} of the owning
+     * Resources object to retrieve its String[].
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return CharSequence[] for the attribute, or null if not defined.
+     */
+    @Override
+    public CharSequence[] getTextArray(int index) {
+        if (mData[index] == null) {
+            return null;
+        }
+
+        String value = mData[index].getValue();
+        if (value == null) {
+            return null;
+        }
+        
+        throw new UnsupportedOperationException(
+                String.format("BridgeTypedArray: UNKNOWN VALUE FOR getTextArray(%d) => %s", //DEBUG
+                index, value));
+
+    }
+
+    /**
+     * Retrieve the raw TypedValue for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param outValue TypedValue object in which to place the attribute's
+     *                 data.
+     * 
+     * @return Returns true if the value was retrieved, else false. 
+     */
+    @Override
+    public boolean getValue(int index, TypedValue outValue) {
+        if (mData[index] == null) {
+            return false;
+        }
+        
+        String s = mData[index].getValue();
+        
+        return ResourceHelper.stringToFloat(s, outValue);
+    }
+
+    /**
+     * Determines whether there is an attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return True if the attribute has a value, false otherwise.
+     */
+    @Override
+    public boolean hasValue(int index) {
+        return mData[index] != null;
+    }
+    
+    /**
+     * Retrieve the raw TypedValue for the attribute at <var>index</var> 
+     * and return a temporary object holding its data.  This object is only 
+     * valid until the next call on to {@link TypedArray}. 
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return Returns a TypedValue object if the attribute is defined, 
+     *         containing its data; otherwise returns null.  (You will not
+     *         receive a TypedValue whose type is TYPE_NULL.)
+     */
+    @Override
+    public TypedValue peekValue(int index) {
+        if (getValue(index, mValue)) {
+            return mValue;
+        }
+        
+        return null;
+    }
+
+    /**
+     * Returns a message about the parser state suitable for printing error messages.
+     */
+    @Override
+    public String getPositionDescription() {
+        return "<internal -- stub if needed>";
+    }
+
+    /**
+     * Give back a previously retrieved StyledAttributes, for later re-use.
+     */
+    @Override
+    public void recycle() {
+        // pass
+    }
+
+    @Override
+    public boolean getValueAt(int index, TypedValue outValue) {
+        // pass
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return mData.toString();
+    }
+ }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlBlockParser.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlBlockParser.java
new file mode 100644
index 0000000..d842a66
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlBlockParser.java
@@ -0,0 +1,378 @@
+/*
+ * 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.IXmlPullParser;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.res.XmlResourceParser;
+import android.util.AttributeSet;
+import android.util.XmlPullAttributes;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+/**
+ * {@link BridgeXmlBlockParser} reimplements most of android.xml.XmlBlock.Parser.
+ * It delegates to both an instance of {@link XmlPullParser} and an instance of
+ * {@link XmlPullAttributes} (for the {@link AttributeSet} part).
+ */
+public class BridgeXmlBlockParser implements XmlResourceParser {
+
+    private XmlPullParser mParser;
+    private XmlPullAttributes mAttrib;
+
+    private boolean mStarted = false;
+    private boolean mDecNextDepth = false;
+    private int mDepth = 0;
+    private int mEventType = START_DOCUMENT;
+    private final boolean mPlatformFile;
+
+    /**
+     * Builds a {@link BridgeXmlBlockParser}.
+     * @param parser The XmlPullParser to get the content from.
+     * @param context the Context.
+     * @param platformFile Indicates whether the the file is a platform file or not.
+     */
+    public BridgeXmlBlockParser(XmlPullParser parser, BridgeContext context, boolean platformFile) {
+        mParser = parser;
+        mPlatformFile = platformFile;
+        mAttrib = new BridgeXmlPullAttributes(parser, context, mPlatformFile);
+    }
+    
+    public boolean isPlatformFile() {
+        return mPlatformFile;
+    }
+
+    public Object getViewKey() {
+        if (mParser instanceof IXmlPullParser) {
+            return ((IXmlPullParser)mParser).getViewKey();
+        }
+
+        return null;
+    }
+    
+    
+    // ------- XmlResourceParser implementation
+    
+    public void setFeature(String name, boolean state)
+            throws XmlPullParserException {
+        if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) {
+            return;
+        }
+        if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name) && state) {
+            return;
+        }
+        throw new XmlPullParserException("Unsupported feature: " + name);
+    }
+
+    public boolean getFeature(String name) {
+        if (FEATURE_PROCESS_NAMESPACES.equals(name)) {
+            return true;
+        }
+        if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) {
+            return true;
+        }
+        return false;
+    }
+
+    public void setProperty(String name, Object value) throws XmlPullParserException {
+        throw new XmlPullParserException("setProperty() not supported");
+    }
+
+    public Object getProperty(String name) {
+        return null;
+    }
+
+    public void setInput(Reader in) throws XmlPullParserException {
+        mParser.setInput(in);
+    }
+
+    public void setInput(InputStream inputStream, String inputEncoding)
+            throws XmlPullParserException {
+        mParser.setInput(inputStream, inputEncoding);
+    }
+
+    public void defineEntityReplacementText(String entityName,
+            String replacementText) throws XmlPullParserException {
+        throw new XmlPullParserException(
+                "defineEntityReplacementText() not supported");
+    }
+
+    public String getNamespacePrefix(int pos) throws XmlPullParserException {
+        throw new XmlPullParserException("getNamespacePrefix() not supported");
+    }
+
+    public String getInputEncoding() {
+        return null;
+    }
+
+    public String getNamespace(String prefix) {
+        throw new RuntimeException("getNamespace() not supported");
+    }
+
+    public int getNamespaceCount(int depth) throws XmlPullParserException {
+        throw new XmlPullParserException("getNamespaceCount() not supported");
+    }
+
+    public String getPositionDescription() {
+        return "Binary XML file line #" + getLineNumber();
+    }
+
+    public String getNamespaceUri(int pos) throws XmlPullParserException {
+        throw new XmlPullParserException("getNamespaceUri() not supported");
+    }
+
+    public int getColumnNumber() {
+        return -1;
+    }
+
+    public int getDepth() {
+        return mDepth;
+    }
+
+    public String getText() {
+        return mParser.getText();
+    }
+
+    public int getLineNumber() {
+        return mParser.getLineNumber();
+    }
+
+    public int getEventType() {
+        return mEventType;
+    }
+
+    public boolean isWhitespace() throws XmlPullParserException {
+        // Original comment: whitespace was stripped by aapt.
+        return mParser.isWhitespace();
+    }
+
+    public String getPrefix() {
+        throw new RuntimeException("getPrefix not supported");
+    }
+
+    public char[] getTextCharacters(int[] holderForStartAndLength) {
+        String txt = getText();
+        char[] chars = null;
+        if (txt != null) {
+            holderForStartAndLength[0] = 0;
+            holderForStartAndLength[1] = txt.length();
+            chars = new char[txt.length()];
+            txt.getChars(0, txt.length(), chars, 0);
+        }
+        return chars;
+    }
+
+    public String getNamespace() {
+        return mParser.getNamespace();
+    }
+
+    public String getName() {
+        return mParser.getName();
+    }
+
+    public String getAttributeNamespace(int index) {
+        return mParser.getAttributeNamespace(index);
+    }
+
+    public String getAttributeName(int index) {
+        return mParser.getAttributeName(index);
+    }
+
+    public String getAttributePrefix(int index) {
+        throw new RuntimeException("getAttributePrefix not supported");
+    }
+
+    public boolean isEmptyElementTag() {
+        // XXX Need to detect this.
+        return false;
+    }
+
+    public int getAttributeCount() {
+        return mParser.getAttributeCount();
+    }
+
+    public String getAttributeValue(int index) {
+        return mParser.getAttributeValue(index);
+    }
+
+    public String getAttributeType(int index) {
+        return "CDATA";
+    }
+
+    public boolean isAttributeDefault(int index) {
+        return false;
+    }
+
+    public int nextToken() throws XmlPullParserException, IOException {
+        return next();
+    }
+
+    public String getAttributeValue(String namespace, String name) {
+        return mParser.getAttributeValue(namespace, name);
+    }
+
+    public int next() throws XmlPullParserException, IOException {
+        if (!mStarted) {
+            mStarted = true;
+            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;
+        }
+        mEventType = ev;
+        return ev;
+    }
+
+    public void require(int type, String namespace, String name)
+            throws XmlPullParserException {
+        if (type != getEventType()
+                || (namespace != null && !namespace.equals(getNamespace()))
+                || (name != null && !name.equals(getName())))
+            throw new XmlPullParserException("expected " + TYPES[type]
+                    + getPositionDescription());
+    }
+
+    public String nextText() throws XmlPullParserException, IOException {
+        if (getEventType() != START_TAG) {
+            throw new XmlPullParserException(getPositionDescription()
+                    + ": parser must be on START_TAG to read next text", this,
+                    null);
+        }
+        int eventType = next();
+        if (eventType == TEXT) {
+            String result = getText();
+            eventType = next();
+            if (eventType != END_TAG) {
+                throw new XmlPullParserException(
+                        getPositionDescription()
+                                + ": event TEXT it must be immediately followed by END_TAG",
+                        this, null);
+            }
+            return result;
+        } else if (eventType == END_TAG) {
+            return "";
+        } else {
+            throw new XmlPullParserException(getPositionDescription()
+                    + ": parser must be on START_TAG or TEXT to read text",
+                    this, null);
+        }
+    }
+
+    public int nextTag() throws XmlPullParserException, IOException {
+        int eventType = next();
+        if (eventType == TEXT && isWhitespace()) { // skip whitespace
+            eventType = next();
+        }
+        if (eventType != START_TAG && eventType != END_TAG) {
+            throw new XmlPullParserException(getPositionDescription()
+                    + ": expected start or end tag", this, null);
+        }
+        return eventType;
+    }
+
+    // AttributeSet implementation
+
+    
+    public void close() {
+        // pass
+    }
+
+    public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
+        return mAttrib.getAttributeBooleanValue(index, defaultValue);
+    }
+
+    public boolean getAttributeBooleanValue(String namespace, String attribute,
+            boolean defaultValue) {
+        return mAttrib.getAttributeBooleanValue(namespace, attribute, defaultValue);
+    }
+
+    public float getAttributeFloatValue(int index, float defaultValue) {
+        return mAttrib.getAttributeFloatValue(index, defaultValue);
+    }
+
+    public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) {
+        return mAttrib.getAttributeFloatValue(namespace, attribute, defaultValue);
+    }
+
+    public int getAttributeIntValue(int index, int defaultValue) {
+        return mAttrib.getAttributeIntValue(index, defaultValue);
+    }
+
+    public int getAttributeIntValue(String namespace, String attribute, int defaultValue) {
+        return mAttrib.getAttributeIntValue(namespace, attribute, defaultValue);
+    }
+
+    public int getAttributeListValue(int index, String[] options, int defaultValue) {
+        return mAttrib.getAttributeListValue(index, options, defaultValue);
+    }
+
+    public int getAttributeListValue(String namespace, String attribute,
+            String[] options, int defaultValue) {
+        return mAttrib.getAttributeListValue(namespace, attribute, options, defaultValue);
+    }
+
+    public int getAttributeNameResource(int index) {
+        return mAttrib.getAttributeNameResource(index);
+    }
+
+    public int getAttributeResourceValue(int index, int defaultValue) {
+        return mAttrib.getAttributeResourceValue(index, defaultValue);
+    }
+
+    public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
+        return mAttrib.getAttributeResourceValue(namespace, attribute, defaultValue);
+    }
+
+    public int getAttributeUnsignedIntValue(int index, int defaultValue) {
+        return mAttrib.getAttributeUnsignedIntValue(index, defaultValue);
+    }
+
+    public int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue) {
+        return mAttrib.getAttributeUnsignedIntValue(namespace, attribute, defaultValue);
+    }
+
+    public String getClassAttribute() {
+        return mAttrib.getClassAttribute();
+    }
+
+    public String getIdAttribute() {
+        return mAttrib.getIdAttribute();
+    }
+
+    public int getIdAttributeResourceValue(int defaultValue) {
+        return mAttrib.getIdAttributeResourceValue(defaultValue);
+    }
+
+    public int getStyleAttribute() {
+        return mAttrib.getStyleAttribute();
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java
new file mode 100644
index 0000000..4be6eabc
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java
@@ -0,0 +1,122 @@
+/*
+ * 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;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import android.util.AttributeSet;
+import android.util.XmlPullAttributes;
+
+/**
+ * A correct implementation of the {@link AttributeSet} interface on top of a XmlPullParser
+ */
+public class BridgeXmlPullAttributes extends XmlPullAttributes {
+
+    private final BridgeContext mContext;
+    private final boolean mPlatformFile;
+
+    public BridgeXmlPullAttributes(XmlPullParser parser, BridgeContext context,
+            boolean platformFile) {
+        super(parser);
+        mContext = context;
+        mPlatformFile = platformFile;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see android.util.XmlPullAttributes#getAttributeNameResource(int)
+     * 
+     * This methods must return com.android.internal.R.attr.<name> matching
+     * the name of the attribute.
+     * It returns 0 if it doesn't find anything.
+     */
+    @Override
+    public int getAttributeNameResource(int index) {
+        // get the attribute name.
+        String name = getAttributeName(index);
+        
+        // get the attribute namespace
+        String ns = mParser.getAttributeNamespace(index);
+        
+        if (BridgeConstants.NS_RESOURCES.equals(ns)) {
+            Integer v = Bridge.getResourceValue(BridgeConstants.RES_ATTR, name);
+            if (v != null) {
+                return v.intValue();
+            }
+            
+            return 0;
+        }
+        
+        // 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);
+            if (v != null) {
+                return v.intValue();
+            }
+        }
+
+        return 0;
+    }
+    
+    /*
+     * (non-Javadoc)
+     * @see android.util.XmlPullAttributes#getAttributeResourceValue(int, int)
+     */
+    @Override
+    public int getAttributeResourceValue(int index, int defaultValue) {
+        String value = getAttributeValue(index);
+        
+        return resolveResourceValue(value, defaultValue);
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see android.util.XmlPullAttributes#getAttributeResourceValue(java.lang.String, java.lang.String, int)
+     */
+    @Override
+    public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
+        String value = getAttributeValue(namespace, attribute);
+        
+        return resolveResourceValue(value, defaultValue);
+    }
+
+    private int resolveResourceValue(String value, int defaultValue) {
+        // now look for this particular value
+        IResourceValue resource = mContext.resolveResValue(mContext.findResValue(value));
+        
+        if (resource != null) {
+            Integer id = null;
+            if (mPlatformFile || resource.isFramework()) {
+                id = Bridge.getResourceValue(resource.getType(), resource.getName());
+            } else {
+                id = mContext.getProjectCallback().getResourceValue(
+                        resource.getType(), resource.getName());
+            }
+
+            if (id != null) {
+                return id;
+            }
+        }
+        
+        return defaultValue;
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java
new file mode 100644
index 0000000..1bdd1cc
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java
@@ -0,0 +1,349 @@
+/*
+ * 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 org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import android.graphics.Typeface;
+
+import java.awt.Font;
+import java.awt.FontFormatException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+/**
+ * Provides {@link Font} object to the layout lib.
+ * <p/>
+ * The fonts are loaded from the SDK directory. Family/style mapping is done by parsing the
+ * fonts.xml file located alongside the ttf files.
+ */
+public final class FontLoader {
+    private static final String FONTS_DEFINITIONS = "fonts.xml";
+    
+    private static final String NODE_FONTS = "fonts";
+    private static final String NODE_FONT = "font";
+    private static final String NODE_NAME = "name";
+    
+    private static final String ATTR_TTF = "ttf";
+
+    private static final String[] NODE_LEVEL = { NODE_FONTS, NODE_FONT, NODE_NAME };
+
+    private static final String FONT_EXT = ".ttf";
+
+    private static final String[] FONT_STYLE_DEFAULT = { "", "-Regular" };
+    private static final String[] FONT_STYLE_BOLD = { "-Bold" };
+    private static final String[] FONT_STYLE_ITALIC = { "-Italic" };
+    private static final String[] FONT_STYLE_BOLDITALIC = { "-BoldItalic" };
+    
+    // list of font style, in the order matching the Typeface Font style
+    private static final String[][] FONT_STYLES = {
+        FONT_STYLE_DEFAULT,
+        FONT_STYLE_BOLD,
+        FONT_STYLE_ITALIC,
+        FONT_STYLE_BOLDITALIC
+    };
+    
+    private final Map<String, String> mFamilyToTtf = new HashMap<String, String>();
+    private final Map<String, Map<Integer, Font>> mTtfToFontMap =
+        new HashMap<String, Map<Integer, Font>>();
+    
+    public static FontLoader create(String fontOsLocation) {
+        try {
+            SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+                parserFactory.setNamespaceAware(true);
+    
+            SAXParser parser = parserFactory.newSAXParser();
+            File f = new File(fontOsLocation + File.separator + FONTS_DEFINITIONS);
+            
+            FontDefinitionParser definitionParser = new FontDefinitionParser(
+                    fontOsLocation + File.separator);
+            parser.parse(new FileInputStream(f), definitionParser);
+            
+            return definitionParser.getFontLoader();
+        } catch (ParserConfigurationException e) {
+            // return null below
+        } catch (SAXException e) {
+            // return null below
+        } catch (FileNotFoundException e) {
+            // return null below
+        } catch (IOException e) {
+            // return null below
+        }
+
+        return null;
+    }
+
+    private FontLoader(List<FontInfo> fontList) {
+        for (FontInfo info : fontList) {
+            for (String family : info.families) {
+                mFamilyToTtf.put(family, info.ttf);
+            }
+        }
+    }
+
+    public synchronized Font getFont(String family, int[] style) {
+        if (family == null) {
+            return null;
+        }
+
+        // get the ttf name from the family
+        String ttf = mFamilyToTtf.get(family);
+        
+        if (ttf == null) {
+            return null;
+        }
+        
+        // get the font from the ttf
+        Map<Integer, Font> styleMap = mTtfToFontMap.get(ttf);
+        
+        if (styleMap == null) {
+            styleMap = new HashMap<Integer, Font>();
+            mTtfToFontMap.put(ttf, styleMap);
+        }
+        
+        Font f = styleMap.get(style);
+        
+        if (f != null) {
+            return f;
+        }
+        
+        // if it doesn't exist, we create it, and we can't, we try with a simpler style
+        switch (style[0]) {
+            case Typeface.NORMAL:
+                f = getFont(ttf, FONT_STYLES[Typeface.NORMAL]);
+                break;
+            case Typeface.BOLD:
+            case Typeface.ITALIC:
+                f = getFont(ttf, FONT_STYLES[style[0]]);
+                if (f == null) {
+                    f = getFont(ttf, FONT_STYLES[Typeface.NORMAL]);
+                    style[0] = Typeface.NORMAL;
+                }
+                break;
+            case Typeface.BOLD_ITALIC:
+                f = getFont(ttf, FONT_STYLES[style[0]]);
+                if (f == null) {
+                    f = getFont(ttf, FONT_STYLES[Typeface.BOLD]);
+                    if (f != null) {
+                        style[0] = Typeface.BOLD;
+                    } else {
+                        f = getFont(ttf, FONT_STYLES[Typeface.ITALIC]);
+                        if (f != null) {
+                            style[0] = Typeface.ITALIC;
+                        } else {
+                            f = getFont(ttf, FONT_STYLES[Typeface.NORMAL]);
+                            style[0] = Typeface.NORMAL;
+                        }
+                    }
+                }
+                break;
+        }
+
+        if (f != null) {
+            styleMap.put(style[0], f);
+            return f;
+        }
+
+        return null;
+    }
+
+    private Font getFont(String ttf, String[] fontFileSuffix) {
+        for (String suffix : fontFileSuffix) {
+            String name = ttf + suffix + FONT_EXT;
+            
+            File f = new File(name);
+            if (f.isFile()) {
+                try {
+                    Font font = Font.createFont(Font.TRUETYPE_FONT, f);
+                    if (font != null) {
+                        return font;
+                    }
+                } catch (FontFormatException e) {
+                    // skip this font name
+                } catch (IOException e) {
+                    // skip this font name
+                }
+            }
+        }
+        
+        return null;
+    }
+
+    private final static class FontInfo {
+        String ttf;
+        final Set<String> families;
+        
+        FontInfo() {
+            families = new HashSet<String>();
+        }
+    }
+
+    private final static class FontDefinitionParser extends DefaultHandler {
+        private final String mOsFontsLocation;
+        
+        private int mDepth = 0;
+        private FontInfo mFontInfo = null;
+        private final StringBuilder mBuilder = new StringBuilder();
+        private final List<FontInfo> mFontList = new ArrayList<FontInfo>();
+        
+        private FontDefinitionParser(String osFontsLocation) {
+            super();
+            mOsFontsLocation = osFontsLocation;
+        }
+        
+        FontLoader getFontLoader() {
+            return new FontLoader(mFontList);
+        }
+
+        /* (non-Javadoc)
+         * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
+         */
+        @Override
+        public void startElement(String uri, String localName, String name, Attributes attributes)
+                throws SAXException {
+            if (localName.equals(NODE_LEVEL[mDepth])) {
+                mDepth++;
+                
+                if (mDepth == 2) { // font level.
+                    String ttf = attributes.getValue(ATTR_TTF);
+                    if (ttf != null) {
+                        mFontInfo = new FontInfo();
+                        mFontInfo.ttf = mOsFontsLocation + ttf;
+                        mFontList.add(mFontInfo);
+                    }
+                }
+            }
+
+            super.startElement(uri, localName, name, attributes);
+        }
+
+        /* (non-Javadoc)
+         * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
+         */
+        @SuppressWarnings("unused")
+        @Override
+        public void characters(char[] ch, int start, int length) throws SAXException {
+            if (mFontInfo != null) {
+                mBuilder.append(ch, start, length);
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
+         */
+        @SuppressWarnings("unused")
+        @Override
+        public void endElement(String uri, String localName, String name) throws SAXException {
+            if (localName.equals(NODE_LEVEL[mDepth-1])) {
+                mDepth--;
+                if (mDepth == 2) { // end of a <name> node
+                    if (mFontInfo != null) {
+                        String family = trimXmlWhitespaces(mBuilder.toString());
+                        mFontInfo.families.add(family);
+                        mBuilder.setLength(0);
+                    }
+                } else if (mDepth == 1) { // end of a <font> node
+                    mFontInfo = null;
+                }
+            }
+        }
+        
+        private String trimXmlWhitespaces(String value) {
+            if (value == null) {
+                return null;
+            }
+
+            // look for carriage return and replace all whitespace around it by just 1 space.
+            int index;
+            
+            while ((index = value.indexOf('\n')) != -1) {
+                // look for whitespace on each side
+                int left = index - 1;
+                while (left >= 0) {
+                    if (Character.isWhitespace(value.charAt(left))) {
+                        left--;
+                    } else {
+                        break;
+                    }
+                }
+                
+                int right = index + 1;
+                int count = value.length();
+                while (right < count) {
+                    if (Character.isWhitespace(value.charAt(right))) {
+                        right++;
+                    } else {
+                        break;
+                    }
+                }
+                
+                // remove all between left and right (non inclusive) and replace by a single space.
+                String leftString = null;
+                if (left >= 0) {
+                    leftString = value.substring(0, left + 1);
+                }
+                String rightString = null;
+                if (right < count) {
+                    rightString = value.substring(right);
+                }
+                
+                if (leftString != null) {
+                    value = leftString;
+                    if (rightString != null) {
+                        value += " " + rightString;
+                    }
+                } else {
+                    value = rightString != null ? rightString : "";
+                }
+            }
+            
+            // now we un-escape the string
+            int length = value.length();
+            char[] buffer = value.toCharArray();
+            
+            for (int i = 0 ; i < length ; i++) {
+                if (buffer[i] == '\\') {
+                    if (buffer[i+1] == 'n') {
+                        // replace the char with \n
+                        buffer[i+1] = '\n';
+                    }
+                    
+                    // offset the rest of the buffer since we go from 2 to 1 char
+                    System.arraycopy(buffer, i+1, buffer, i, length - i - 1);
+                    length--;
+                }
+            }
+            
+            return new String(buffer, 0, length);
+        }
+
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/LayoutResult.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/LayoutResult.java
new file mode 100644
index 0000000..c4c5225
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/LayoutResult.java
@@ -0,0 +1,126 @@
+/*
+ * 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/MockView.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
new file mode 100644
index 0000000..1ca3182
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
@@ -0,0 +1,45 @@
+/*
+ * 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 android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+/**
+ * Base class for mocked views.
+ * 
+ * TODO: implement onDraw and draw a rectangle in a random color with the name of the class
+ * (or better the id of the view).
+ */
+public class MockView extends TextView {
+    
+    public MockView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        
+        setText(this.getClass().getSimpleName());
+        setTextColor(0xFF000000);
+    }
+
+    @Override
+    public void onDraw(Canvas canvas) {
+        canvas.drawARGB(0xFF, 0x7F, 0x7F, 0x7F);
+
+        super.onDraw(canvas);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java
new file mode 100644
index 0000000..5f0852e
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java
@@ -0,0 +1,113 @@
+/*
+ * 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) {
+        if (canvas instanceof BridgeCanvas) {
+            BridgeCanvas bridgeCanvas = (BridgeCanvas)canvas;
+            
+            Rect r = getBounds();
+            m9Patch.draw(bridgeCanvas.getGraphics2d(), r.left, r.top, r.width(), r.height());
+            
+            return;
+        }
+
+        throw new UnsupportedOperationException();
+    }
+
+    
+    // ----------- 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
new file mode 100644
index 0000000..fbdf8dc
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java
@@ -0,0 +1,353 @@
+/*
+ * 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 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. 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(String value, BridgeContext context, boolean isFramework) {
+        Drawable d = null;
+        
+        String lowerCaseValue = value.toLowerCase();
+
+        if (lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) {
+            File f = new File(value);
+            if (f.isFile()) {
+                NinePatch ninePatch = Bridge.getCached9Patch(value,
+                        isFramework ? null : context.getProjectKey());
+                
+                if (ninePatch == null) {
+                    try {
+                        ninePatch = NinePatch.load(new File(value).toURL(), false /* convert */);
+                        
+                        Bridge.setCached9Patch(value, 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(value);
+            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(),
+                            // FIXME: we need to know if this resource is platform or not
+                            new BridgeXmlBlockParser(parser, context, false));
+                    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(value);
+            if (bmpFile.isFile()) {
+                try {
+                    Bitmap bitmap = Bridge.getCachedBitmap(value,
+                            isFramework ? null : context.getProjectKey());
+                    
+                    if (bitmap == null) {
+                        bitmap = new Bitmap(bmpFile);
+                        Bridge.setCachedBitmap(value, bitmap,
+                                isFramework ? null : context.getProjectKey());
+                    }
+                    
+                    return new BitmapDrawable(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(value);
+                    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
new file mode 100644
index 0000000..01a4871
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceValue.java
@@ -0,0 +1,67 @@
+/*
+ * 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/google/android/maps/MapView.java b/tools/layoutlib/bridge/src/com/google/android/maps/MapView.java
new file mode 100644
index 0000000..6d013bb
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/google/android/maps/MapView.java
@@ -0,0 +1,121 @@
+/*
+ * 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.google.android.maps;
+
+import com.android.layoutlib.bridge.MockView;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Mock version of the MapView.
+ * Only non override public methods from the real MapView have been added in there.
+ * Methods that take an unknown class as parameter or as return object, have been removed for now.
+ * 
+ * TODO: generate automatically.
+ *
+ */
+public class MapView extends MockView {
+
+    /**
+     * Construct a new WebView with a Context object.
+     * @param context A Context object used to access application assets.
+     */
+    public MapView(Context context) {
+        this(context, null);
+    }
+
+    /**
+     * Construct a new WebView with layout parameters.
+     * @param context A Context object used to access application assets.
+     * @param attrs An AttributeSet passed to our parent.
+     */
+    public MapView(Context context, AttributeSet attrs) {
+        this(context, attrs, com.android.internal.R.attr.mapViewStyle);
+    }
+
+    /**
+     * Construct a new WebView with layout parameters and a default style.
+     * @param context A Context object used to access application assets.
+     * @param attrs An AttributeSet passed to our parent.
+     * @param defStyle The default style resource ID.
+     */
+    public MapView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+    
+    // START FAKE PUBLIC METHODS
+    
+    public void displayZoomControls(boolean takeFocus) {
+    }
+
+    public boolean canCoverCenter() {
+        return false;
+    }
+
+    public void preLoad() {
+    }
+
+    public int getZoomLevel() {
+        return 0;
+    }
+
+    public void setSatellite(boolean on) {
+    }
+
+    public boolean isSatellite() {
+        return false;
+    }
+
+    public void setTraffic(boolean on) {
+    }
+
+    public boolean isTraffic() {
+        return false;
+    }
+
+    public void setStreetView(boolean on) {
+    }
+
+    public boolean isStreetView() {
+        return false;
+    }
+
+    public int getLatitudeSpan() {
+        return 0;
+    }
+
+    public int getLongitudeSpan() {
+        return 0;
+    }
+
+    public int getMaxZoomLevel() {
+        return 0;
+    }
+
+    public void onSaveInstanceState(Bundle state) {
+    }
+
+    public void onRestoreInstanceState(Bundle state) {
+    }
+
+    public View getZoomControls() {
+        return null;
+    }
+}
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java
new file mode 100644
index 0000000..ac144e7
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java
@@ -0,0 +1,73 @@
+/*
+ * 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 android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics._Original_Paint;
+import android.text.TextPaint;
+
+import junit.framework.TestCase;
+
+/**
+ * 
+ */
+public class AndroidGraphicsTests extends TestCase {
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testMatrix() {
+        Matrix m1 = new Matrix();
+        
+        assertFalse(m1.isIdentity());
+        
+        m1.setValues(new float[] { 1,0,0, 0,1,0, 0,0,1 });
+        assertTrue(m1.isIdentity());
+        
+        Matrix m2 = new Matrix(m1);
+        
+        float[] v1 = new float[9];
+        float[] v2 = new float[9];
+        m1.getValues(v1);
+        m2.getValues(v2);
+        
+        for (int i = 0 ; i < 9; i++) {
+            assertEquals(v1[i], v2[i]);
+        }
+    }
+    
+    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/BridgeTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeTest.java
new file mode 100644
index 0000000..e424f1d
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeTest.java
@@ -0,0 +1,229 @@
+/*
+ * 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 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 org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.File;
+import java.io.FileReader;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+public class BridgeTest extends TestCase {
+
+    /** the class being tested */
+    private Bridge mBridge;
+    /** the path to the sample layout.xml file */
+    private String mLayoutXml1Path;
+    private String mTextOnlyXmlPath;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mBridge = new Bridge();
+        
+        // FIXME: need some fonts somewhere.
+        mBridge.init(null /* fontOsLocation */, getAttributeValues());
+        
+        URL url = this.getClass().getClassLoader().getResource("data/layout1.xml");
+        mLayoutXml1Path = url.getFile();
+
+        url = this.getClass().getClassLoader().getResource("data/textonly.xml");
+        mTextOnlyXmlPath = url.getFile();
+    }
+    
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    // ---------------
+
+    /**
+     * Test parser that implements {@link IXmlPullParser}.
+     */
+    private static class TestParser extends KXmlParser implements IXmlPullParser {
+        public Object getViewKey() {
+            return null;
+        }
+    }
+
+    public void testComputeLayout() throws Exception {
+        
+        TestParser parser = new TestParser();
+        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+        parser.setInput(new FileReader(new File(mLayoutXml1Path)));
+
+        Map<String, Map<String, IResourceValue>> projectResources = getProjectResources();
+
+        Map<String, Map<String, IResourceValue>> frameworkResources = getFrameworkResources();
+        
+        int screenWidth = 320;
+        int screenHeight = 480;
+        
+        // FIXME need a dummy font for the tests!
+        ILayoutResult result = mBridge.computeLayout(parser, new Integer(1) /* projectKey */, 
+                screenWidth, screenHeight,
+                "Theme", projectResources, frameworkResources, null, null);
+                
+        display(result.getRootView(), "");
+    }
+
+    private Map<String, Map<String, Integer>> getAttributeValues() {
+        Map<String, Map<String, Integer>> attributeValues =
+            new HashMap<String, Map<String,Integer>>();
+        
+        // lets create a map for the orientation attribute
+        Map<String, Integer> attributeMap = new HashMap<String, Integer>();
+        
+        attributeMap.put("horizontal", Integer.valueOf(0));
+        attributeMap.put("vertical", Integer.valueOf(1));
+        
+        attributeValues.put("orientation", attributeMap);
+        
+        return attributeValues;
+    }
+
+    private Map<String, Map<String, IResourceValue>> getFrameworkResources() {
+        Map<String, Map<String, IResourceValue>> frameworkResources =
+            new HashMap<String, Map<String, IResourceValue>>();
+        
+        // create the style map
+        Map<String, IResourceValue> styleMap = new HashMap<String, IResourceValue>();
+        frameworkResources.put("style", styleMap);
+        
+        // create a button style.
+        IStyleResourceValue style = createStyle("Widget.Button",
+                "background",        "@android:drawable/something",
+                "focusable",        "true",
+                "clickable",        "true",
+                "textAppearance",   "?android:attr/textAppearanceSmallInverse",
+                "textColor",        "?android:attr/textColorBrightInverseNoDisable",
+                "gravity",          "center_vertical|center_horizontal"
+                );
+        styleMap.put(style.getName(), style);
+
+        // create the parent style of button style
+        style = createStyle("Widget",
+                "textAppearance", "?textAppearance");
+        styleMap.put(style.getName(), style);
+
+        // link the buttonStyle info in the default theme.
+        style = createStyle("Theme",
+                BridgeConstants.RES_STYLE, "buttonStyle",                      "@android:style/Widget.Button",
+                BridgeConstants.RES_STYLE, "textAppearance",                   "@android:style/TextAppearance",
+                BridgeConstants.RES_STYLE, "textAppearanceSmallInverse",       "@android:style/TextAppearance.Small.Inverse",
+                BridgeConstants.RES_COLOR, "textColorBrightInverseNoDisable",  "@android:color/bright_text_light_nodisable"
+                );
+        styleMap.put(style.getName(), style);
+
+        // create a dummy drawable to go with it
+        Map<String, IResourceValue> drawableMap = new HashMap<String, IResourceValue>();
+        frameworkResources.put("drawable", drawableMap);
+        
+        // get the 9 patch test location
+        URL url = this.getClass().getClassLoader().getResource("data/button.9.png");
+
+        IResourceValue drawable = new ResourceValue(BridgeConstants.RES_DRAWABLE, "something",
+                url.getPath());
+        drawableMap.put(drawable.getName(), drawable);
+        return frameworkResources;
+    }
+    
+    private Map<String, Map<String, IResourceValue>> getProjectResources() {
+        Map<String, Map<String, IResourceValue>> projectResources =
+            new HashMap<String, Map<String, IResourceValue>>();
+
+        // create the style map (even empty there should be one)
+        Map<String, IResourceValue> styleMap = new HashMap<String, IResourceValue>();
+        projectResources.put("style", styleMap);
+
+        return projectResources;
+    }
+
+
+    private void display(ILayoutViewInfo result, String offset) {
+
+        String msg = String.format("%s%s L:%d T:%d R:%d B:%d",
+                offset,
+                result.getName(),
+                result.getLeft(), result.getTop(), result.getRight(), result.getBottom());
+
+        System.out.println(msg);
+        ILayoutViewInfo[] children = result.getChildren();
+        if (children != null) {
+            offset += "+-";
+            for (ILayoutViewInfo child : children) {
+                display(child, offset);
+            }
+        }
+    }
+    
+    /**
+     * Creates a {@link IStyleResourceValue} based on the given values.
+     * @param styleName the name of the style.
+     * @param items An array of Strings. Even indices contain a style item name, and odd indices
+     * a style item value. If the number of string in the array is not even, an exception is thrown.
+     */
+    private IStyleResourceValue createStyle(String styleName, String... items) {
+        StyleResourceValue value = new StyleResourceValue(styleName);
+        
+        if (items.length % 3 == 0) {
+            for (int i = 0 ; i < items.length;) {
+                value.addItem(new ResourceValue(items[i++], items[i++], items[i++]));
+            }
+        } else {
+            throw new IllegalArgumentException("Need a multiple of 3 for the number of strings");
+        }
+        
+        return value;
+    }
+
+    // ---------------
+
+    public void testTextLayout() throws Exception {
+        
+        TestParser parser = new TestParser();
+        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+        parser.setInput(new FileReader(new File(mTextOnlyXmlPath)));
+
+        Map<String, Map<String, IResourceValue>> projectResources = getProjectResources();
+        Map<String, Map<String, IResourceValue>> frameworkResources = getFrameworkResources();
+        
+        int screenWidth = 320;
+        int screenHeight = 480;
+
+        // FIXME need a dummy font for the tests!
+        ILayoutResult result = mBridge.computeLayout(parser, new Integer(1) /* projectKey */,
+                screenWidth, screenHeight,
+                "Theme", projectResources, frameworkResources, null, null);
+                
+        display(result.getRootView(), "");
+    }
+
+}
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java
new file mode 100644
index 0000000..cac1f95
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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 org.kxml2.io.KXmlParser;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.URL;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import junit.framework.TestCase;
+
+public class BridgeXmlBlockParserTest extends TestCase {
+
+    private String mXmlPath;
+    private Document mDoc;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        URL url = this.getClass().getClassLoader().getResource("data/layout1.xml");
+        mXmlPath = url.getFile();
+        mDoc = getXmlDocument(mXmlPath);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testXmlBlockParser() throws Exception {
+        XmlPullParser parser = new KXmlParser();
+        parser = new BridgeXmlBlockParser(parser, null, false /* platformResourceFlag */);
+        parser.setInput(new FileReader(new File(mXmlPath)));
+
+        assertEquals(XmlPullParser.START_DOCUMENT, parser.next());
+
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("LinearLayout", parser.getName());
+
+        assertEquals(XmlPullParser.TEXT, parser.next());
+
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("Button", parser.getName());
+        assertEquals(XmlPullParser.TEXT, parser.next());
+        assertEquals(XmlPullParser.END_TAG, parser.next());
+
+        assertEquals(XmlPullParser.TEXT, parser.next());
+
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("View", parser.getName());
+        assertEquals(XmlPullParser.END_TAG, parser.next());
+
+        assertEquals(XmlPullParser.TEXT, parser.next());
+
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("TextView", parser.getName());
+        assertEquals(XmlPullParser.END_TAG, parser.next());
+
+        assertEquals(XmlPullParser.TEXT, parser.next());
+
+        assertEquals(XmlPullParser.END_TAG, parser.next());
+        assertEquals(XmlPullParser.END_DOCUMENT, parser.next());
+    }
+    
+    //------------
+    
+    private Document getXmlDocument(String xmlFilePath)
+            throws ParserConfigurationException, SAXException, IOException {
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        
+        // keep comments
+        factory.setIgnoringComments(false);
+        // don't validate our bogus DTD
+        factory.setValidating(false);
+        // we want namespaces
+        factory.setNamespaceAware(true);
+        
+        DocumentBuilder builder = factory.newDocumentBuilder();
+        return builder.parse(new File(xmlFilePath));
+    }
+
+
+    /**
+     * Quick'n'dirty debug helper that dumps an XML structure to stdout.
+     */
+    @SuppressWarnings("unused")
+    private void dump(Node node, String prefix) {
+        Node n;
+
+        String[] types = {
+                "unknown",
+                "ELEMENT_NODE",
+                "ATTRIBUTE_NODE",
+                "TEXT_NODE",
+                "CDATA_SECTION_NODE",
+                "ENTITY_REFERENCE_NODE",
+                "ENTITY_NODE",
+                "PROCESSING_INSTRUCTION_NODE",
+                "COMMENT_NODE",
+                "DOCUMENT_NODE",
+                "DOCUMENT_TYPE_NODE",
+                "DOCUMENT_FRAGMENT_NODE",
+                "NOTATION_NODE"
+        };
+        
+        String s = String.format("%s<%s> %s %s",
+                prefix,
+                types[node.getNodeType()],
+                node.getNodeName(),
+                node.getNodeValue() == null ? "" : node.getNodeValue().trim());
+
+        System.out.println(s);
+        
+        n = node.getFirstChild();
+        if (n != null) {
+            dump(n, prefix + "- ");
+        }
+
+        n = node.getNextSibling();
+        if (n != null) {
+            dump(n, prefix);
+        }
+
+    }
+
+}
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java
new file mode 100644
index 0000000..67ec5e1
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java
@@ -0,0 +1,35 @@
+package com.android.layoutlib.bridge;
+
+import com.android.ninepatch.NinePatch;
+
+import java.net.URL;
+
+import junit.framework.TestCase;
+
+public class NinePatchTest extends TestCase {
+    
+    private NinePatch mPatch;
+
+    @Override
+    protected void setUp() throws Exception {
+        URL url = this.getClass().getClassLoader().getResource("data/button.9.png");
+
+        mPatch = NinePatch.load(url, false /* convert */);
+    }
+    
+    public void test9PatchLoad() throws Exception {
+        assertNotNull(mPatch);
+    }
+    
+    public void test9PatchMinSize() {
+        int[] padding = new int[4];
+        mPatch.getPadding(padding);
+        assertEquals(13, padding[0]);
+        assertEquals(3, padding[1]);
+        assertEquals(13, padding[2]);
+        assertEquals(4, padding[3]);
+        assertEquals(38, mPatch.getWidth());
+        assertEquals(27, mPatch.getHeight());
+    }
+
+}
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/StyleResourceValue.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/StyleResourceValue.java
new file mode 100644
index 0000000..84bdc2f
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/StyleResourceValue.java
@@ -0,0 +1,60 @@
+/*
+ * 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;
+import com.android.layoutlib.api.IStyleResourceValue;
+
+import java.util.HashMap;
+
+class StyleResourceValue extends ResourceValue implements IStyleResourceValue {
+
+    private String mParentStyle = null;
+    private HashMap<String, IResourceValue> mItems = new HashMap<String, IResourceValue>();
+
+    StyleResourceValue(String name) {
+        super(name);
+    }
+
+    StyleResourceValue(String name, String parentStyle) {
+        super(name);
+        mParentStyle = parentStyle;
+    }
+
+    public String getParentStyle() {
+        return mParentStyle;
+    }
+    
+    public IResourceValue findItem(String name) {
+        return mItems.get(name);
+    }
+    
+    public void addItem(IResourceValue value) {
+        mItems.put(value.getName(), value);
+    }
+    
+    @Override
+    public void replaceWith(ResourceValue value) {
+        super.replaceWith(value);
+        
+        if (value instanceof StyleResourceValue) {
+            mItems.clear();
+            mItems.putAll(((StyleResourceValue)value).mItems);
+        }
+    }
+
+}
diff --git a/tools/layoutlib/bridge/tests/data/button.9.png b/tools/layoutlib/bridge/tests/data/button.9.png
new file mode 100644
index 0000000..9d52f40
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/data/button.9.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/data/layout1.xml b/tools/layoutlib/bridge/tests/data/layout1.xml
new file mode 100644
index 0000000..554f541
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/data/layout1.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+	android:orientation="vertical"
+>
+	<Button
+		android:id="@+id/bouton"
+	    android:layout_width="wrap_content"
+	    android:layout_height="wrap_content"
+	    android:layout_weight="1"
+	    android:text="My Button Text"
+	    >
+	    </Button>
+	<View
+		android:id="@+id/surface"
+	    android:layout_width="fill_parent"
+	    android:layout_height="fill_parent"
+	    android:layout_weight="2"
+	    />
+	<TextView
+	    android:id="@+id/status"
+	    android:paddingLeft="2dip"
+	    android:layout_weight="0"
+	    android:background="@drawable/black"
+	    android:layout_width="fill_parent"
+	    android:layout_height="wrap_content"
+	    android:lines="1"
+	    android:gravity="center_vertical|center_horizontal"
+	    android:text="My TextView Text"
+	    />
+</LinearLayout>
diff --git a/tools/layoutlib/create/.classpath b/tools/layoutlib/create/.classpath
new file mode 100644
index 0000000..0c60f6a
--- /dev/null
+++ b/tools/layoutlib/create/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry excluding="mock_android/" kind="src" path="tests"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
+	<classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/asm/asm-3.1.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tools/layoutlib/create/.project b/tools/layoutlib/create/.project
new file mode 100644
index 0000000..e100d17
--- /dev/null
+++ b/tools/layoutlib/create/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>layoutlib_create</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/tools/layoutlib/create/Android.mk b/tools/layoutlib/create/Android.mk
new file mode 100644
index 0000000..310fae5
--- /dev/null
+++ b/tools/layoutlib/create/Android.mk
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+
+LOCAL_JAR_MANIFEST := manifest.txt
+LOCAL_STATIC_JAVA_LIBRARIES := \
+	asm-3.1
+
+LOCAL_MODULE := layoutlib_create
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
diff --git a/tools/layoutlib/create/README.txt b/tools/layoutlib/create/README.txt
new file mode 100644
index 0000000..09b392b
--- /dev/null
+++ b/tools/layoutlib/create/README.txt
@@ -0,0 +1,71 @@
+# Copyright (C) 2008 The Android Open Source Project
+
+
+- Description -
+---------------
+
+makeLayoutLib generates a library used by the Eclipse graphical layout editor
+to perform layout.
+
+
+
+- Usage -
+---------
+
+ ./makeLayoutLib path/to/android.jar destination.jar
+
+
+
+- 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.
+
+
+
+----
+20080617 Replace Class
+
+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.
+
+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.
+
+This won't rename/replace the inner static methods of a given class.
+
+
+
diff --git a/tools/layoutlib/create/manifest.txt b/tools/layoutlib/create/manifest.txt
new file mode 100644
index 0000000..238e7f9
--- /dev/null
+++ b/tools/layoutlib/create/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.tools.layoutlib.create.Main
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
new file mode 100644
index 0000000..b197ea7
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
@@ -0,0 +1,751 @@
+/*
+ * 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 org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.signature.SignatureVisitor;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * Analyzes the input JAR using the ASM java bytecode manipulation library
+ * to list the desired classes and their dependencies.
+ */
+public class AsmAnalyzer {
+
+    // Note: a bunch of stuff has package-level access for unit tests. Consider it private.
+    
+    /** Output logger. */
+    private final Log mLog;
+    /** The input source JAR to parse. */
+    private final List<String> mOsSourceJar;
+    /** The generator to fill with the class list and dependency list. */
+    private final AsmGenerator mGen;
+    /** Keep all classes that derive from these one (these included). */
+    private final String[] mDeriveFrom;
+    /** Glob patterns of classes to keep, e.g. "com.foo.*" */
+    private final String[] mIncludeGlobs;
+
+    /**
+     * Creates a new analyzer.
+     * 
+     * @param log The log output.
+     * @param osJarPath The input source JARs to parse.
+     * @param gen The generator to fill with the class list and dependency list.
+     * @param deriveFrom Keep all classes that derive from these one (these included). 
+     * @param includeGlobs Glob patterns of classes to keep, e.g. "com.foo.*"
+     *        ("*" does not matches dots whilst "**" does, "." and "$" are interpreted as-is)
+     */
+    public AsmAnalyzer(Log log, List<String> osJarPath, AsmGenerator gen,
+            String[] deriveFrom, String[] includeGlobs) {
+        mLog = log;
+        mGen = gen;
+        mOsSourceJar = osJarPath != null ? osJarPath : new ArrayList<String>();
+        mDeriveFrom = deriveFrom != null ? deriveFrom : new String[0];
+        mIncludeGlobs = includeGlobs != null ? includeGlobs : new String[0];
+    }
+
+    /**
+     * Starts the analysis using parameters from the constructor.
+     * Fills the generator with classes & dependencies found.
+     */
+    public void analyze() throws IOException, LogAbortException {
+
+        AsmAnalyzer visitor = this;
+        
+        Map<String, ClassReader> zipClasses = parseZip(mOsSourceJar);
+        mLog.info("Found %d classes in input JAR%s.", zipClasses.size(),
+                mOsSourceJar.size() > 1 ? "s" : "");
+        
+        Map<String, ClassReader> found = findIncludes(zipClasses);
+        Map<String, ClassReader> deps = findDeps(zipClasses, found);
+        
+        if (mGen != null) {
+            mGen.setKeep(found);
+            mGen.setDeps(deps);
+        }
+    }
+
+    /**
+     * Parses a JAR file and returns a list of all classes founds using a map
+     * class name => ASM ClassReader. Class names are in the form "android.view.View".
+     */
+    Map<String,ClassReader> parseZip(List<String> jarPathList) throws IOException {
+        TreeMap<String, ClassReader> classes = new TreeMap<String, ClassReader>();
+
+        for (String jarPath : jarPathList) {
+            ZipFile zip = new ZipFile(jarPath);
+            Enumeration<? extends ZipEntry> entries = zip.entries();
+            ZipEntry entry;
+            while (entries.hasMoreElements()) {
+                entry = entries.nextElement();
+                if (entry.getName().endsWith(".class")) {
+                    ClassReader cr = new ClassReader(zip.getInputStream(entry));
+                    String className = classReaderToClassName(cr);
+                    classes.put(className, cr);
+                }
+            }
+        }
+        
+        return classes;
+    }
+    
+    /**
+     * Utility that returns the fully qualified binary class name for a ClassReader.
+     * E.g. it returns something like android.view.View.
+     */
+    static String classReaderToClassName(ClassReader classReader) {
+        if (classReader == null) {
+            return null;
+        } else {
+            return classReader.getClassName().replace('/', '.');
+        }
+    }
+    
+    /**
+     * Utility that returns the fully qualified binary class name from a path-like FQCN.
+     * E.g. it returns android.view.View from android/view/View.
+     */
+    static String internalToBinaryClassName(String className) {
+        if (className == null) {
+            return null;
+        } else {
+            return className.replace('/', '.');
+        }
+    }
+    
+    /**
+     * Process the "includes" arrays.
+     * <p/>
+     * This updates the in_out_found map.
+     */
+    Map<String, ClassReader> findIncludes(Map<String, ClassReader> zipClasses)
+            throws LogAbortException {
+        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
+
+        mLog.debug("Find classes to include.");
+
+        for (String s : mIncludeGlobs) {
+            findGlobs(s, zipClasses, found);
+        }
+        for (String s : mDeriveFrom) {
+            findClassesDerivingFrom(s, zipClasses, found);
+        }
+        
+        return found;
+    }
+
+    
+    /**
+     * Uses ASM to find the class reader for the given FQCN class name.
+     * If found, insert it in the in_out_found map.
+     * Returns the class reader object.
+     */
+    ClassReader findClass(String className, Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inOutFound) throws LogAbortException {
+        ClassReader classReader = zipClasses.get(className);
+        if (classReader == null) {
+            throw new LogAbortException("Class %s not found by ASM in %s",
+                    className, mOsSourceJar);
+        }
+
+        inOutFound.put(className, classReader);
+        return classReader;
+    }
+
+    /**
+     * Insert in the inOutFound map all classes found in zipClasses that match the
+     * given glob pattern.
+     * <p/>
+     * The glob pattern is not a regexp. It only accepts the "*" keyword to mean
+     * "anything but a period". The "." and "$" characters match themselves.
+     * The "**" keyword means everything including ".".
+     * <p/>
+     * Examples:
+     * <ul>
+     * <li>com.foo.* matches all classes in the package com.foo but NOT sub-packages.
+     * <li>com.foo*.*$Event matches all internal Event classes in a com.foo*.* class.
+     * </ul>
+     */
+    void findGlobs(String globPattern, Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inOutFound) throws LogAbortException {
+        // transforms the glob pattern in a regexp:
+        // - escape "." with "\."
+        // - replace "*" by "[^.]*"
+        // - escape "$" with "\$"
+        // - add end-of-line match $
+        globPattern = globPattern.replaceAll("\\$", "\\\\\\$");
+        globPattern = globPattern.replaceAll("\\.", "\\\\.");
+        // prevent ** from being altered by the next rule, then process the * rule and finally
+        // the real ** rule (which is now @)
+        globPattern = globPattern.replaceAll("\\*\\*", "@");
+        globPattern = globPattern.replaceAll("\\*", "[^.]*");
+        globPattern = globPattern.replaceAll("@", ".*");
+        globPattern += "$";
+
+        Pattern regexp = Pattern.compile(globPattern);
+        
+        for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
+            String class_name = entry.getKey();
+            if (regexp.matcher(class_name).matches()) {
+                findClass(class_name, zipClasses, inOutFound);
+            }
+        }
+    }
+
+    /**
+     * Checks all the classes defined in the JarClassName instance and uses BCEL to
+     * determine if they are derived from the given FQCN super class name.
+     * Inserts the super class and all the class objects found in the map.
+     */
+    void findClassesDerivingFrom(String super_name, Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inOutFound) throws LogAbortException {
+        ClassReader super_clazz = findClass(super_name, zipClasses, inOutFound);
+
+        for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
+            String className = entry.getKey();
+            if (super_name.equals(className)) {
+                continue;
+            }
+            ClassReader classReader = entry.getValue();
+            ClassReader parent_cr = classReader;
+            while (parent_cr != null) {
+                String parent_name = internalToBinaryClassName(parent_cr.getSuperName());
+                if (parent_name == null) {
+                    // not found
+                    break;
+                } else if (super_name.equals(parent_name)) {
+                    inOutFound.put(className, classReader);
+                    break;
+                }
+                parent_cr = zipClasses.get(parent_name);
+            }
+        }
+    }
+
+    /**
+     * Instantiates a new DependencyVisitor. Useful for unit tests.
+     */
+    DependencyVisitor getVisitor(Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inKeep,
+            Map<String, ClassReader> outKeep,
+            Map<String, ClassReader> inDeps,
+            Map<String, ClassReader> outDeps) {
+        return new DependencyVisitor(zipClasses, inKeep, outKeep, inDeps, outDeps);
+    }
+
+    /**
+     * Finds all dependencies for all classes in keepClasses which are also
+     * listed in zipClasses. Returns a map of all the dependencies found.
+     */
+    Map<String, ClassReader> findDeps(Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inOutKeepClasses) {
+
+        TreeMap<String, ClassReader> deps = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> new_deps = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> new_keep = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> temp = new TreeMap<String, ClassReader>();
+
+        DependencyVisitor visitor = getVisitor(zipClasses,
+                inOutKeepClasses, new_keep,
+                deps, new_deps);
+
+        for (ClassReader cr : inOutKeepClasses.values()) {
+            cr.accept(visitor, 0 /* flags */);
+        }
+        
+        while (new_deps.size() > 0 || new_keep.size() > 0) {
+            deps.putAll(new_deps);
+            inOutKeepClasses.putAll(new_keep);
+
+            temp.clear();
+            temp.putAll(new_deps);
+            temp.putAll(new_keep);
+            new_deps.clear();
+            new_keep.clear();
+            mLog.debug("Found %1$d to keep, %2$d dependencies.",
+                    inOutKeepClasses.size(), deps.size());
+
+            for (ClassReader cr : temp.values()) {
+                cr.accept(visitor, 0 /* flags */);
+            }
+        }
+
+        mLog.info("Found %1$d classes to keep, %2$d class dependencies.",
+                inOutKeepClasses.size(), deps.size());
+
+        return deps;
+    }
+
+    
+
+    // ----------------------------------
+    
+    /**
+     * Visitor to collect all the type dependencies from a class. 
+     */
+    public class DependencyVisitor
+        implements ClassVisitor, FieldVisitor, MethodVisitor, SignatureVisitor, AnnotationVisitor {
+
+        /** All classes found in the source JAR. */
+        private final Map<String, ClassReader> mZipClasses;
+        /** Classes from which dependencies are to be found. */
+        private final Map<String, ClassReader> mInKeep;
+        /** Dependencies already known. */
+        private final Map<String, ClassReader> mInDeps;
+        /** New dependencies found by this visitor. */
+        private final Map<String, ClassReader> mOutDeps;
+        /** New classes to keep as-is found by this visitor. */
+        private final Map<String, ClassReader> mOutKeep;
+
+        /**
+         * Creates a new visitor that will find all the dependencies for the visited class.
+         * Types which are already in the zipClasses, keepClasses or inDeps are not marked.
+         * New dependencies are marked in outDeps.
+         * 
+         * @param zipClasses All classes found in the source JAR.
+         * @param inKeep Classes from which dependencies are to be found.
+         * @param inDeps Dependencies already known.
+         * @param outDeps New dependencies found by this visitor.
+         */
+        public DependencyVisitor(Map<String, ClassReader> zipClasses,
+                Map<String, ClassReader> inKeep,
+                Map<String, ClassReader> outKeep,
+                Map<String,ClassReader> inDeps,
+                Map<String,ClassReader> outDeps) {
+            mZipClasses = zipClasses;
+            mInKeep = inKeep;
+            mOutKeep = outKeep;
+            mInDeps = inDeps;
+            mOutDeps = outDeps;
+        }
+        
+        /**
+         * Considers the given class name as a dependency.
+         * If it does, add to the mOutDeps map.
+         */
+        public void considerName(String className) {
+            if (className == null) {
+                return;
+            }
+
+            className = internalToBinaryClassName(className);
+            
+            // exclude classes that have already been found
+            if (mInKeep.containsKey(className) ||
+                    mOutKeep.containsKey(className) ||
+                    mInDeps.containsKey(className) ||
+                    mOutDeps.containsKey(className)) {
+                return;
+            }
+
+            // exclude classes that are not part of the JAR file being examined
+            ClassReader cr = mZipClasses.get(className);
+            if (cr == null) {
+                return;
+            }
+
+            try {
+                // exclude classes that are part of the default JRE (the one executing this program)
+                if (getClass().getClassLoader().loadClass(className) != null) {
+                    return;
+                }
+            } catch (ClassNotFoundException e) {
+                // ignore
+            }
+            
+            // accept this class:
+            // - android classes are added to dependencies
+            // - non-android classes are added to the list of classes to keep as-is (they don't need
+            //   to be stubbed).
+            if (className.indexOf("android") >= 0) {  // TODO make configurable
+                mOutDeps.put(className, cr);
+            } else {
+                mOutKeep.put(className, cr);
+            }
+        }
+        
+        /**
+         * Considers this array of names using considerName().
+         */
+        public void considerNames(String[] classNames) {
+            if (classNames != null) {
+                for (String className : classNames) {
+                    considerName(className);
+                }
+            }
+        }
+
+        /**
+         * Considers this signature or type signature by invoking the {@link SignatureVisitor}
+         * on it.
+         */
+        public void considerSignature(String signature) {
+            if (signature != null) {
+                SignatureReader sr = new SignatureReader(signature);
+                // SignatureReader.accept will call accessType so we don't really have
+                // to differentiate where the signature comes from.
+                sr.accept(this);
+            }
+        }
+
+        /**
+         * Considers this {@link Type}. For arrays, the element type is considered.
+         * If the type is an object, it's internal name is considered.
+         */
+        public void considerType(Type t) {
+            if (t != null) {
+                if (t.getSort() == Type.ARRAY) {
+                    t = t.getElementType();
+                }
+                if (t.getSort() == Type.OBJECT) {
+                    considerName(t.getInternalName());
+                }
+            }
+        }
+
+        /**
+         * Considers a descriptor string. The descriptor is converted to a {@link Type}
+         * and then considerType() is invoked.
+         */
+        public void considerDesc(String desc) {
+            if (desc != null) {
+                try {
+                    Type t = Type.getType(desc);
+                    considerType(t);
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    // ignore, not a valid type.
+                }
+            }
+        }
+
+        
+        // ---------------------------------------------------
+        // --- ClassVisitor, FieldVisitor
+        // ---------------------------------------------------
+
+        // Visits a class header
+        public void visit(int version, int access, String name,
+                String signature, String superName, String[] interfaces) {
+            // signature is the signature of this class. May be null if the class is not a generic
+            // one, and does not extend or implement generic classes or interfaces.
+            
+            if (signature != null) {
+                considerSignature(signature);
+            }
+
+            // superName is the internal of name of the super class (see getInternalName).
+            // For interfaces, the super class is Object. May be null but only for the Object class.
+            considerName(superName);
+            
+            // interfaces is the internal names of the class's interfaces (see getInternalName).
+            // May be null.
+            considerNames(interfaces);
+        }
+
+        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+            // desc is the class descriptor of the annotation class.
+            considerDesc(desc);
+            return this; // return this to visit annotion values
+        }
+
+        public void visitAttribute(Attribute attr) {
+            // pass
+        }
+
+        // Visits the end of a class
+        public void visitEnd() {
+            // pass
+        }
+
+        public FieldVisitor visitField(int access, String name, String desc,
+                String signature, Object value) {
+            // desc is the field's descriptor (see Type).
+            considerDesc(desc);
+
+            // signature is the field's signature. May be null if the field's type does not use
+            // generic types.
+            considerSignature(signature);
+
+            return this; // a visitor to visit field annotations and attributes
+        }
+
+        public void visitInnerClass(String name, String outerName, String innerName, int access) {
+            // name is the internal name of an inner class (see getInternalName).
+            considerName(name);
+        }
+
+        public MethodVisitor visitMethod(int access, String name, String desc,
+                String signature, String[] exceptions) {
+            // desc is the method's descriptor (see Type).
+            considerDesc(desc);
+            // signature is the method's signature. May be null if the method parameters, return
+            // type and exceptions do not use generic types.
+            considerSignature(signature);
+            
+            return this; // returns this to visit the method
+        }
+
+        public void visitOuterClass(String owner, String name, String desc) {
+            // pass
+        }
+
+        public void visitSource(String source, String debug) {
+            // pass
+        }
+
+        
+        // ---------------------------------------------------
+        // --- MethodVisitor
+        // ---------------------------------------------------
+
+        public AnnotationVisitor visitAnnotationDefault() {
+            return this; // returns this to visit the default value
+        }
+
+
+        public void visitCode() {
+            // pass
+        }
+
+        // field instruction
+        public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+            // name is the field's name.
+            considerName(name);
+            // desc is the field's descriptor (see Type).
+            considerDesc(desc);
+        }
+
+        public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
+            // pass
+        }
+
+        public void visitIincInsn(int var, int increment) {
+            // pass -- an IINC instruction
+        }
+
+        public void visitInsn(int opcode) {
+            // pass -- a zero operand instruction
+        }
+
+        public void visitIntInsn(int opcode, int operand) {
+            // pass -- a single int operand instruction
+        }
+
+        public void visitJumpInsn(int opcode, Label label) {
+            // pass -- a jump instruction
+        }
+
+        public void visitLabel(Label label) {
+            // pass -- a label target
+        }
+
+        // instruction to load a constant from the stack
+        public void visitLdcInsn(Object cst) {
+            if (cst instanceof Type) {
+                considerType((Type) cst);
+            }
+        }
+
+        public void visitLineNumber(int line, Label start) {
+            // pass
+        }
+
+        public void visitLocalVariable(String name, String desc,
+                String signature, Label start, Label end, int index) {
+            // desc is the type descriptor of this local variable.
+            considerDesc(desc);
+            // signature is the type signature of this local variable. May be null if the local
+            // variable type does not use generic types.
+            considerSignature(signature);
+        }
+
+        public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+            // pass -- a lookup switch instruction
+        }
+
+        public void visitMaxs(int maxStack, int maxLocals) {
+            // pass
+        }
+
+        // instruction that invokes a method
+        public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+            
+            // owner is the internal name of the method's owner class
+            considerName(owner);
+            // desc is the method's descriptor (see Type).
+            considerDesc(desc);
+        }
+
+        // instruction multianewarray, whatever that is
+        public void visitMultiANewArrayInsn(String desc, int dims) {
+            
+            // desc an array type descriptor.
+            considerDesc(desc);
+        }
+
+        public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
+                boolean visible) {
+            // desc is the class descriptor of the annotation class.
+            considerDesc(desc);
+            return this; // return this to visit annotation values
+        }
+
+        public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
+            // pass -- table switch instruction
+            
+        }
+
+        public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+            // type is the internal name of the type of exceptions handled by the handler,
+            // or null to catch any exceptions (for "finally" blocks).
+            considerName(type);
+        }
+
+        // type instruction
+        public void visitTypeInsn(int opcode, String type) {
+            // type is the operand of the instruction to be visited. This operand must be the
+            // internal name of an object or array class.
+            considerName(type);
+        }
+
+        public void visitVarInsn(int opcode, int var) {
+            // pass -- local variable instruction 
+        }
+
+        
+        // ---------------------------------------------------
+        // --- SignatureVisitor
+        // ---------------------------------------------------
+
+        private String mCurrentSignatureClass = null;
+
+        // Starts the visit of a signature corresponding to a class or interface type
+        public void visitClassType(String name) {
+            mCurrentSignatureClass = name;
+            considerName(name);
+        }
+
+        // Visits an inner class
+        public void visitInnerClassType(String name) {
+            if (mCurrentSignatureClass != null) {
+                mCurrentSignatureClass += "$" + name;
+                considerName(mCurrentSignatureClass);
+            }
+        }
+
+        public SignatureVisitor visitArrayType() {
+            return this; // returns this to visit the signature of the array element type
+        }
+
+        public void visitBaseType(char descriptor) {
+            // pass -- a primitive type, ignored
+        }
+
+        public SignatureVisitor visitClassBound() {
+            return this; // returns this to visit the signature of the class bound
+        }
+
+        public SignatureVisitor visitExceptionType() {
+            return this; // return this to visit the signature of the exception type.
+        }
+
+        public void visitFormalTypeParameter(String name) {
+            // pass
+        }
+
+        public SignatureVisitor visitInterface() {
+            return this; // returns this to visit the signature of the interface type
+        }
+
+        public SignatureVisitor visitInterfaceBound() {
+            return this; // returns this to visit the signature of the interface bound
+        }
+
+        public SignatureVisitor visitParameterType() {
+            return this; // returns this to visit the signature of the parameter type
+        }
+
+        public SignatureVisitor visitReturnType() {
+            return this; // returns this to visit the signature of the return type
+        }
+
+        public SignatureVisitor visitSuperclass() {
+            return this; // returns this to visit the signature of the super class type
+        }
+
+        public SignatureVisitor visitTypeArgument(char wildcard) {
+            return this; // returns this to visit the signature of the type argument
+        }
+
+        public void visitTypeVariable(String name) {
+            // pass
+        }
+
+        public void visitTypeArgument() {
+            // pass
+        }
+        
+        
+        // ---------------------------------------------------
+        // --- AnnotationVisitor
+        // ---------------------------------------------------
+
+
+        // Visits a primitive value of an annotation
+        public void visit(String name, Object value) {
+            // value is the actual value, whose type must be Byte, Boolean, Character, Short,
+            // Integer, Long, Float, Double, String or Type
+            if (value instanceof Type) {
+                considerType((Type) value);
+            }
+        }
+
+        public AnnotationVisitor visitAnnotation(String name, String desc) {
+            // desc is the class descriptor of the nested annotation class.
+            considerDesc(desc);
+            return this; // returns this to visit the actual nested annotation value
+        }
+
+        public AnnotationVisitor visitArray(String name) {
+            return this; // returns this to visit the actual array value elements
+        }
+
+        public void visitEnum(String name, String desc, String value) {
+            // desc is the class descriptor of the enumeration class.
+            considerDesc(desc);
+        }
+        
+    }
+}
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
new file mode 100644
index 0000000..1adcc17
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -0,0 +1,338 @@
+/*
+ * 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 org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+
+/**
+ * Class that generates a new JAR from a list of classes, some of which are to be kept as-is
+ * and some of which are to be stubbed partially or totally.
+ */
+public class AsmGenerator {
+
+    /** Output logger. */
+    private final Log mLog;
+    /** The path of the destination JAR to create. */
+    private final String mOsDestJar;
+    /** List of classes to inject in the final JAR from _this_ archive. */
+    private final Class<?>[] mInjectClasses;
+    /** The set of methods to stub out. */
+    private final Set<String> mStubMethods;
+    /** All classes to output as-is, except if they have native methods. */
+    private Map<String, ClassReader> mKeep;
+    /** All dependencies that must be completely stubbed. */
+    private Map<String, ClassReader> mDeps;
+    /** Counter of number of classes renamed during transform. */
+    private int mRenameCount;
+    /** FQCN Names of the classes to rename: map old-FQCN => new-FQCN */
+    private final HashMap<String, String> mRenameClasses;
+    /** FQCN Names of "old" classes that were NOT renamed. This starts with the full list of
+     *  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 } }. */
+    private HashMap<String, Set<String>> mDeleteReturns;
+
+    /**
+     * 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 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.
+     */
+    public AsmGenerator(Log log, String osDestJar,
+            Class<?>[] injectClasses,
+            String[] stubMethods,
+            String[] renameClasses, String[] deleteReturns) {
+        mLog = log;
+        mOsDestJar = osDestJar;
+        mInjectClasses = injectClasses != null ? injectClasses : new Class<?>[0];
+        mStubMethods = stubMethods != null ? new HashSet<String>(Arrays.asList(stubMethods)) :
+                                             new HashSet<String>();
+
+        // Create the map of classes to rename.
+        mRenameClasses = new HashMap<String, String>();
+        mClassesNotRenamed = new HashSet<String>();
+        int n = renameClasses == null ? 0 : renameClasses.length;
+        for (int i = 0; i < n; i += 2) {
+            assert i + 1 < n;
+            // The ASM class names uses "/" separators, whereas regular FQCN use "."
+            String oldFqcn = binaryToInternalClassName(renameClasses[i]);
+            String newFqcn = binaryToInternalClassName(renameClasses[i + 1]);
+            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;
+                }
+    
+                // 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/>
+     * The names are "internal class names" rather than FQCN, i.e. they use "/" instead "."
+     * as package separators.
+     */
+    public Set<String> getClassesNotRenamed() {
+        return mClassesNotRenamed;
+    }
+
+    /**
+     * Utility that returns the internal ASM class name from a fully qualified binary class
+     * name. E.g. it returns android/view/View from android.view.View.
+     */
+    String binaryToInternalClassName(String className) {
+        if (className == null) {
+            return null;
+        } else {
+            return className.replace('.', '/');
+        }
+    }
+
+    /** Sets the map of classes to output as-is, except if they have native methods */
+    public void setKeep(Map<String, ClassReader> keep) {
+        mKeep = keep;
+    }
+
+    /** Sets the map of dependencies that must be completely stubbed */
+    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;
+    }
+
+    /** 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);
+            ClassReader cr = new ClassReader(is);
+            byte[] b = transform(cr, true /* stubNativesOnly */);
+            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 */);
+            String name = classNameToEntryPath(transformName(cr.getClassName()));
+            all.put(name, b);
+        }
+
+        for (Entry<String, ClassReader> entry : mKeep.entrySet()) {
+            ClassReader cr = entry.getValue();
+            byte[] b = transform(cr, true /* stubNativesOnly */);
+            String name = classNameToEntryPath(transformName(cr.getClassName()));
+            all.put(name, b);
+        }
+
+        mLog.info("# deps classes: %d", mDeps.size());
+        mLog.info("# keep classes: %d", mKeep.size());
+        mLog.info("# renamed     : %d", mRenameCount);
+
+        createJar(new FileOutputStream(mOsDestJar), all);
+        mLog.info("Created JAR file %s", mOsDestJar);
+    }
+
+    /**
+     * Writes the JAR file.
+     * 
+     * @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
+     */
+    void createJar(FileOutputStream outStream, Map<String,byte[]> all) throws IOException {
+        JarOutputStream jar = new JarOutputStream(outStream);
+        for (Entry<String, byte[]> entry : all.entrySet()) {
+            String name = entry.getKey();
+            JarEntry jar_entry = new JarEntry(name);
+            jar.putNextEntry(jar_entry);
+            jar.write(entry.getValue());
+            jar.closeEntry();
+        }
+        jar.flush();
+        jar.close();
+    }
+
+    /**
+     * Utility method that converts a fully qualified java name into a JAR entry path
+     * e.g. for the input "android.view.View" it returns "android/view/View.class"
+     */
+    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"
+     */
+    private String classToEntryPath(Class<?> clazz) {
+        String name = "";
+        Class<?> parent;
+        while ((parent = clazz.getEnclosingClass()) != null) {
+            name = "$" + clazz.getSimpleName() + name;
+            clazz = parent;
+        }
+        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);
+        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) {
+            mRenameCount++;
+            // This class is being renamed, so remove it from the list of classes not renamed.
+            mClassesNotRenamed.remove(className);
+        }
+
+        mLog.debug("Transform %s%s%s%s", className,
+                newName == className ? "" : " (renamed to " + newName + ")",
+                hasNativeMethods ? " -- has natives" : "",
+                stubNativesOnly ? " -- stub natives only" : "");
+
+        // 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, 
+                mDeleteReturns.get(className),
+                newName, rv,
+                stubNativesOnly, stubNativesOnly || hasNativeMethods);
+        cr.accept(cv, 0 /* flags */);
+        return cw.toByteArray();
+    }
+
+    /**
+     * Should this class be renamed, this returns the new name. Otherwise it returns the
+     * original name.
+     *
+     * @param className The internal ASM name of the class that may have to be renamed
+     * @return A new transformed name or the original input argument.
+     */
+    String transformName(String className) {
+        String newName = mRenameClasses.get(className);
+        if (newName != null) {
+            return newName;
+        }
+        int pos = className.indexOf('$');
+        if (pos > 0) {
+            // Is this an inner class of a renamed class?
+            String base = className.substring(0, pos);
+            newName = mRenameClasses.get(base);
+            if (newName != null) {
+                return newName + className.substring(pos);
+            }
+        }
+        
+        return className;
+    }
+
+    /**
+     * Returns true if a class has any native methods.
+     */
+    boolean hasNativeMethods(ClassReader cr) {
+        ClassHasNativeVisitor cv = new ClassHasNativeVisitor();
+        cr.accept(cv, 0 /* flags */);
+        return cv.hasNativeMethods();
+    }
+
+}
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
new file mode 100644
index 0000000..5424efa
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java
@@ -0,0 +1,80 @@
+/*
+ * 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 org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Indicates if a class contains any native methods.
+ */
+public class ClassHasNativeVisitor implements ClassVisitor {
+    
+    private boolean mHasNativeMethods = false;
+    
+    public boolean hasNativeMethods() {
+        return mHasNativeMethods;
+    }
+
+    public void visit(int version, int access, String name, String signature,
+            String superName, String[] interfaces) {
+        // pass
+    }
+
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        // pass
+        return null;
+    }
+
+    public void visitAttribute(Attribute attr) {
+        // pass
+    }
+
+    public void visitEnd() {
+        // pass
+    }
+
+    public FieldVisitor visitField(int access, String name, String desc,
+            String signature, Object value) {
+        // pass
+        return null;
+    }
+
+    public void visitInnerClass(String name, String outerName,
+            String innerName, int access) {
+        // pass
+    }
+
+    public MethodVisitor visitMethod(int access, String name, String desc,
+            String signature, String[] exceptions) {
+        mHasNativeMethods |= ((access & Opcodes.ACC_NATIVE) != 0);
+        return null;
+    }
+
+    public void visitOuterClass(String owner, String name, String desc) {
+        // pass
+    }
+
+    public void visitSource(String source, String debug) {
+        // pass
+    }
+
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java
new file mode 100644
index 0000000..8efd871
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java
@@ -0,0 +1,64 @@
+/*
+ * 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 java.io.PrintWriter;
+import java.io.StringWriter;
+
+public class Log {
+
+    private boolean mVerbose = false;
+
+    public void setVerbose(boolean verbose) {
+        mVerbose = verbose;
+    }
+
+    public void debug(String format, Object... args) {
+        if (mVerbose) {
+            info(format, args);
+        }
+    }
+
+    public void info(String format, Object... args) {
+        String s = String.format(format, args);
+        outPrintln(s);
+    }
+    
+    public void error(String format, Object... args) {
+        String s = String.format(format, args);
+        errPrintln(s);
+    }
+
+    public void exception(Throwable t, String format, Object... args) {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        t.printStackTrace(pw);
+        pw.flush();
+        error(format + "\n" + sw.toString(), args);
+    }
+    
+    /** for unit testing */
+    protected void errPrintln(String msg) {
+        System.err.println(msg);
+    }
+    
+    /** for unit testing */
+    protected void outPrintln(String msg) {
+        System.out.println(msg);
+    }
+    
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/LogAbortException.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/LogAbortException.java
new file mode 100644
index 0000000..dc4b4a7
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/LogAbortException.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+public class LogAbortException extends Exception {
+
+    private final String mFormat;
+    private final Object[] mArgs;
+
+    public LogAbortException(String format, Object... args) {
+        mFormat = format;
+        mArgs = args;
+    }
+    
+    public void error(Log log) {
+        log.error(mFormat, mArgs);
+    }
+}
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
new file mode 100644
index 0000000..76bd8d4
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
@@ -0,0 +1,174 @@
+/*
+ * 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 java.io.IOException;
+import java.util.ArrayList;
+import java.util.Set;
+
+
+
+public class Main {
+
+    public static void main(String[] args) {
+
+        Log log = new Log();
+
+        ArrayList<String> osJarPath = new ArrayList<String>();
+        String[] osDestJar = { null };
+
+        if (!processArgs(log, args, osJarPath, osDestJar)) {
+            log.error("Usage: layoutlib_create [-v] output.jar input.jar ...");
+            System.exit(1);
+        }
+
+        log.info("Output: %1$s", osDestJar[0]);
+        for (String path : osJarPath) {
+            log.info("Input :      %1$s", path);
+        }
+        
+        try {
+            AsmGenerator agen = new AsmGenerator(log, osDestJar[0],
+                    new Class<?>[] {  // classes to inject in the final JAR
+                        OverrideMethod.class,
+                        MethodListener.class,
+                        MethodAdapter.class
+                    },
+                    new String[] {  // methods to force override
+                        "android.view.View#isInEditMode",
+                        "android.content.res.Resources$Theme#obtainStyledAttributes",
+                    },
+                    new String[] {  // classes to rename (so that we can replace them in layoutlib)
+                        // original-platform-class-name ======> renamed-class-name
+                        "android.graphics.Matrix",              "android.graphics._Original_Matrix",
+                        "android.graphics.Paint",               "android.graphics._Original_Paint",
+                        "android.graphics.Typeface",            "android.graphics._Original_Typeface",
+                        "android.graphics.Bitmap",              "android.graphics._Original_Bitmap",
+                        "android.graphics.Path",                "android.graphics._Original_Path",
+                        "android.graphics.PorterDuffXfermode",  "android.graphics._Original_PorterDuffXfermode",
+                        "android.graphics.Shader",              "android.graphics._Original_Shader",
+                        "android.graphics.LinearGradient",      "android.graphics._Original_LinearGradient",
+                        "android.graphics.BitmapShader",        "android.graphics._Original_BitmapShader",
+                        "android.graphics.ComposeShader",       "android.graphics._Original_ComposeShader",
+                        "android.graphics.RadialGradient",      "android.graphics._Original_RadialGradient",
+                        "android.graphics.SweepGradient",       "android.graphics._Original_SweepGradient",
+                        "android.util.FloatMath",               "android.util._Original_FloatMath",
+                        "android.view.SurfaceView",             "android.view._Original_SurfaceView",
+                    },
+                    new String[] { // methods deleted from their return type.
+                        "android.graphics.Paint", // class to delete method 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 }
+            );
+
+            AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath, agen,
+                    new String[] { "android.view.View" },   // derived from
+                    new String[] {                          // include classes
+                        "android.*", // for android.R
+                        "android.util.*",
+                        "com.android.internal.util.*",
+                        "android.view.*",
+                        "android.widget.*",
+                        "com.android.internal.widget.*",
+                        "android.text.**",
+                        "android.graphics.*",
+                        "android.graphics.drawable.*",
+                        "android.content.*",
+                        "android.content.res.*",
+                        "org.apache.harmony.xml.*",
+                        "com.android.internal.R**",
+                        "android.pim.*", // for datepicker
+                        "android.os.*",  // for android.os.Handler
+                        });
+            aa.analyze();
+            agen.generate();
+            
+            // Throw an error if any class failed to get renamed by the generator
+            //
+            // IMPORTANT: if you're building the platform and you get this error message,
+            // it means the renameClasses[] array in AsmGenerator needs to be updated: some
+            // class should have been renamed but it was not found in the input JAR files.
+            Set<String> notRenamed = agen.getClassesNotRenamed();
+            if (notRenamed.size() > 0) {
+                // (80-column guide below for error formatting)
+                // 01234567890123456789012345678901234567890123456789012345678901234567890123456789
+                log.error(
+                  "ERROR when running layoutlib_create: the following classes are referenced\n" +
+                  "by tools/layoutlib/create but were not actually found in the input JAR files.\n" +
+                  "This may be due to some platform classes having been renamed.");
+                for (String fqcn : notRenamed) {
+                    log.error("- Class not found: %s", fqcn.replace('/', '.'));
+                }
+                for (String path : osJarPath) {
+                    log.info("- Input JAR : %1$s", path);
+                }
+                System.exit(1);
+            }
+            
+            System.exit(0);
+        } catch (IOException e) {
+            log.exception(e, "Failed to load jar");
+        } catch (LogAbortException e) {
+            e.error(log);
+        }
+
+        System.exit(1);
+    }
+
+    /**
+     * Returns true if args where properly parsed.
+     * Returns false if program should exit with command-line usage.
+     * <p/>
+     * Note: the String[0] is an output parameter wrapped in an array, since there is no
+     * "out" parameter support.
+     */
+    private static boolean processArgs(Log log, String[] args,
+            ArrayList<String> osJarPath, String[] osDestJar) {
+        for (int i = 0; i < args.length; i++) {
+            String s = args[i];
+            if (s.equals("-v")) {
+                log.setVerbose(true);
+            } else if (!s.startsWith("-")) {
+                if (osDestJar[0] == null) {
+                    osDestJar[0] = s;
+                } else {
+                    osJarPath.add(s);
+                }
+            } else {
+                log.error("Unknow argument: %s", s);
+                return false;
+            }
+        }
+        
+        if (osJarPath.isEmpty()) {
+            log.error("Missing parameter: path to input jar");
+            return false;
+        }
+        if (osDestJar[0] == null) {
+            log.error("Missing parameter: path to output jar");
+            return false;
+        }
+
+        return true;
+    }
+
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java
new file mode 100644
index 0000000..627ea17
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java
@@ -0,0 +1,91 @@
+/*
+ * 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;
+
+
+/**
+ * An adapter to make it easier to use {@link MethodListener}.
+ * <p/>
+ * The adapter calls the void {@link #onInvokeV(String, boolean, Object)} listener
+ * for all types (I, L, F, D and A), returning 0 or null as appropriate.
+ */
+public class MethodAdapter implements MethodListener {
+    /**
+     * A stub method is being invoked.
+     * <p/>
+     * Known limitation: caller arguments are not available.
+     *  
+     * @param signature The signature of the method being invoked, composed of the
+     *                  binary class name followed by the method descriptor (aka argument
+     *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V".
+     * @param isNative True if the method was a native method.
+     * @param caller The calling object. Null for static methods, "this" for instance methods.
+     */
+    public void onInvokeV(String signature, boolean isNative, Object caller) {
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns an integer or similar.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return an integer, or a boolean, or a short or a byte.
+     */
+    public int onInvokeI(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return 0;
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a long.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a long.
+     */
+    public long onInvokeL(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return 0;
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a float.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a float.
+     */
+    public float onInvokeF(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return 0;
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a double.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a double.
+     */
+    public double onInvokeD(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return 0;
+    }
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns an object.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return an object.
+     */
+    public Object onInvokeA(String signature, boolean isNative, Object caller) {
+        onInvokeV(signature, isNative, caller);
+        return null;
+    }
+}
+
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodListener.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodListener.java
new file mode 100644
index 0000000..6fc2b24
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodListener.java
@@ -0,0 +1,76 @@
+/*
+ * 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;
+
+
+/**
+ * Interface to allow a method invocation to be listened upon.
+ * <p/>
+ * This is used by {@link OverrideMethod} to register a listener for methods that
+ * have been stubbed by the {@link AsmGenerator}. At runtime the stub will call either a
+ * default global listener or a specific listener based on the method signature.
+ */
+public interface MethodListener {
+    /**
+     * A stub method is being invoked.
+     * <p/>
+     * Known limitation: caller arguments are not available.
+     *  
+     * @param signature The signature of the method being invoked, composed of the
+     *                  binary class name followed by the method descriptor (aka argument
+     *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V".
+     * @param isNative True if the method was a native method.
+     * @param caller The calling object. Null for static methods, "this" for instance methods.
+     */
+    public void onInvokeV(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns an integer or similar.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return an integer, or a boolean, or a short or a byte.
+     */
+    public int onInvokeI(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a long.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a long.
+     */
+    public long onInvokeL(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a float.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a float.
+     */
+    public float onInvokeF(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns a double.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return a double.
+     */
+    public double onInvokeD(String signature, boolean isNative, Object caller);
+
+    /**
+     * Same as {@link #onInvokeV(String, boolean, Object)} but returns an object.
+     * @see #onInvokeV(String, boolean, Object)
+     * @return an object.
+     */
+    public Object onInvokeA(String signature, boolean isNative, Object caller);
+}
+    
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java
new file mode 100644
index 0000000..a6aff99
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java
@@ -0,0 +1,151 @@
+/*
+ * 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 java.util.HashMap;
+
+/**
+ * Allows stub methods from LayoutLib to be overriden at runtime.
+ * <p/>
+ * Implementation note: all types required by this class(inner/outer classes & interfaces)
+ * must be referenced by the injectClass argument to {@link AsmGenerator} in Main.java;
+ * Otherwise they won't be accessible in layoutlib.jar at runtime.
+ */
+public final class OverrideMethod {
+
+    /** Map of method overridden. */
+    private static HashMap<String, MethodListener> sMethods = new HashMap<String, MethodListener>();
+    /** Default listener for all method not listed in sMethods. Nothing if null. */
+    private static MethodListener sDefaultListener = null;
+    
+    /**
+     * Sets the default listener for all methods not specifically handled.
+     * Null means to do nothing.
+     */
+    public static void setDefaultListener(MethodListener listener) {
+        sDefaultListener = listener;
+    }
+
+    /**
+     * Defines or reset a listener for the given method signature.
+     * 
+     * @param signature The signature of the method being invoked, composed of the
+     *                  binary class name followed by the method descriptor (aka argument
+     *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V"
+     * @param listener The new listener. Removes it if null.
+     */
+    public static void setMethodListener(String signature, MethodListener listener) {
+        if (listener == null) {
+            sMethods.remove(signature);
+        } else {
+            sMethods.put(signature, listener);
+        }
+    }
+    
+    /**
+     * Invokes the specific listener for the given signature or the default one if defined.
+     * <p/>
+     * This version invokes the method listener for the void return type. 
+     * <p/>
+     * Note: this is not intended to be used by the LayoutLib Bridge. It is intended to be called
+     * by the stubbed methods generated by the LayoutLib_create tool.
+     * 
+     * @param signature The signature of the method being invoked, composed of the
+     *                  binary class name followed by the method descriptor (aka argument
+     *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V".
+     * @param isNative True if the method was a native method.
+     * @param caller The calling object. Null for static methods, "this" for instance methods.
+     */
+    public static void invokeV(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            i.onInvokeV(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            sDefaultListener.onInvokeV(signature, isNative, caller);
+        }
+    }
+    
+    /**
+     * Invokes the specific listener for the int return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static int invokeI(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeI(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeI(signature, isNative, caller);
+        }
+        return 0;
+    }
+    
+    /**
+     * Invokes the specific listener for the long return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static long invokeL(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeL(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeL(signature, isNative, caller);
+        }
+        return 0;
+    }
+    
+    /**
+     * Invokes the specific listener for the float return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static float invokeF(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeF(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeF(signature, isNative, caller);
+        }
+        return 0;
+    }
+    
+    /**
+     * Invokes the specific listener for the double return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static double invokeD(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeD(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeD(signature, isNative, caller);
+        }
+        return 0;
+    }
+    
+    /**
+     * Invokes the specific listener for the object return type.
+     * @see #invokeV(String, boolean, Object)
+     */
+    public static Object invokeA(String signature, boolean isNative, Object caller) {
+        MethodListener i = sMethods.get(signature);
+        if (i != null) {
+            return i.onInvokeA(signature, isNative, caller);
+        } else if (sDefaultListener != null) {
+            return sDefaultListener.onInvokeA(signature, isNative, caller);
+        }
+        return null;
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java
new file mode 100644
index 0000000..0956b92
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java
@@ -0,0 +1,446 @@
+/*
+ * 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 org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.signature.SignatureVisitor;
+import org.objectweb.asm.signature.SignatureWriter;
+
+/**
+ * This class visitor renames a class from a given old name to a given new name.
+ * The class visitor will also rename all inner classes and references in the methods.
+ * <p/>
+ * 
+ * For inner classes, this handles only the case where the outer class name changes.
+ * The inner class name should remain the same. 
+ */
+public class RenameClassAdapter extends ClassAdapter {
+
+    
+    private final String mOldName;
+    private final String mNewName;
+    private String mOldBase;
+    private String mNewBase;
+
+    /**
+     * Creates a class visitor that renames a class from a given old name to a given new name.
+     * The class visitor will also rename all inner classes and references in the methods.
+     * The names must be full qualified internal ASM names (e.g. com/blah/MyClass$InnerClass).
+     */
+    public RenameClassAdapter(ClassWriter cv, String oldName, String newName) {
+        super(cv);
+        mOldBase = mOldName = oldName;
+        mNewBase = mNewName = newName;
+        
+        int pos = mOldName.indexOf('$');
+        if (pos > 0) {
+            mOldBase = mOldName.substring(0, pos);
+        }
+        pos = mNewName.indexOf('$');
+        if (pos > 0) {
+            mNewBase = mNewName.substring(0, pos);
+        }
+        
+        assert (mOldBase == null && mNewBase == null) || (mOldBase != null && mNewBase != null);
+    }
+
+
+    /**
+     * Renames a type descriptor, e.g. "Lcom.package.MyClass;"
+     * If the type doesn't need to be renamed, returns the input string as-is.
+     */
+    String renameTypeDesc(String desc) {
+        if (desc == null) {
+            return null;
+        }
+
+        return renameType(Type.getType(desc));
+    }
+    
+    /**
+     * Renames an object type, e.g. "Lcom.package.MyClass;" or an array type that has an
+     * object element, e.g. "[Lcom.package.MyClass;"
+     * If the type doesn't need to be renamed, returns the internal name of the input type.
+     */
+    String renameType(Type type) {
+        if (type == null) {
+            return null;
+        }
+
+        if (type.getSort() == Type.OBJECT) {
+            String in = type.getInternalName();
+            return "L" + renameInternalType(in) + ";";
+        } else if (type.getSort() == Type.ARRAY) {
+            StringBuilder sb = new StringBuilder();
+            for (int n = type.getDimensions(); n > 0; n--) {
+                sb.append('[');
+            }
+            sb.append(renameType(type.getElementType()));
+            return sb.toString();
+        }
+        return type.getDescriptor();
+    }
+
+    /**
+     * Renames an object type, e.g. "Lcom.package.MyClass;" or an array type that has an
+     * object element, e.g. "[Lcom.package.MyClass;".
+     * This is like renameType() except that it returns a Type object.
+     * If the type doesn't need to be renamed, returns the input type object.
+     */
+    Type renameTypeAsType(Type type) {
+        if (type == null) {
+            return null;
+        }
+
+        if (type.getSort() == Type.OBJECT) {
+            String in = type.getInternalName();
+            String newIn = renameInternalType(in);
+            if (newIn != in) {
+                return Type.getType("L" + newIn + ";");
+            }
+        } else if (type.getSort() == Type.ARRAY) {
+            StringBuilder sb = new StringBuilder();
+            for (int n = type.getDimensions(); n > 0; n--) {
+                sb.append('[');
+            }
+            sb.append(renameType(type.getElementType()));
+            return Type.getType(sb.toString());
+        }
+        return type;
+    }
+
+    /**
+     * Renames an internal type name, e.g. "com.package.MyClass".
+     * If the type doesn't need to be renamed, returns the input string as-is.
+     * <p/>
+     * The internal type of some of the MethodVisitor turns out to be a type
+       descriptor sometimes so descriptors are renamed too.
+     */
+    String renameInternalType(String type) {
+        if (type == null) {
+            return null;
+        }
+
+        if (type.equals(mOldName)) {
+            return mNewName;
+        }
+
+        if (mOldBase != mOldName && type.equals(mOldBase)) {
+            return mNewBase;
+        }
+    
+        int pos = type.indexOf('$');
+        if (pos == mOldBase.length() && type.startsWith(mOldBase)) {
+            return mNewBase + type.substring(pos);
+        }
+
+        // The internal type of some of the MethodVisitor turns out to be a type
+        // descriptor sometimes. This is the case with visitTypeInsn(type) and
+        // visitMethodInsn(owner). We try to detect it and adjust it here.
+        if (type.indexOf(';') > 0) {
+            type = renameTypeDesc(type);
+        }
+
+        return type;
+    }
+
+    /**
+     * Renames a method descriptor, i.e. applies renameType to all arguments and to the
+     * return value.
+     */
+    String renameMethodDesc(String desc) {
+        if (desc == null) {
+            return null;
+        }
+
+        Type[] args = Type.getArgumentTypes(desc);
+
+        StringBuilder sb = new StringBuilder("(");
+        for (Type arg : args) {
+            String name = renameType(arg);
+            sb.append(name);
+        }
+        sb.append(')');
+        
+        Type ret = Type.getReturnType(desc);
+        String name = renameType(ret);
+        sb.append(name);
+
+        return sb.toString();
+    }
+
+    
+    /**
+     * Renames the ClassSignature handled by ClassVisitor.visit 
+     * or the MethodTypeSignature handled by ClassVisitor.visitMethod.
+     */
+    String renameTypeSignature(String sig) {
+        if (sig == null) {
+            return null;
+        }
+        SignatureReader reader = new SignatureReader(sig);
+        SignatureWriter writer = new SignatureWriter();
+        reader.accept(new RenameSignatureAdapter(writer));
+        sig = writer.toString();
+        return sig;
+    }
+
+    
+    /**
+     * Renames the FieldTypeSignature handled by ClassVisitor.visitField
+     * or MethodVisitor.visitLocalVariable.
+     */
+    String renameFieldSignature(String sig) {
+        if (sig == null) {
+            return null;
+        }
+        SignatureReader reader = new SignatureReader(sig);
+        SignatureWriter writer = new SignatureWriter();
+        reader.acceptType(new RenameSignatureAdapter(writer));
+        sig = writer.toString();
+        return sig;
+    }
+
+    
+    //----------------------------------
+    // Methods from the ClassAdapter
+    
+    @Override
+    public void visit(int version, int access, String name, String signature,
+            String superName, String[] interfaces) {
+        name = renameInternalType(name);
+        superName = renameInternalType(superName);
+        signature = renameTypeSignature(signature);
+        
+        super.visit(version, access, name, signature, superName, interfaces);
+    }
+
+    @Override
+    public void visitInnerClass(String name, String outerName, String innerName, int access) {
+        assert outerName.equals(mOldName);
+        outerName = renameInternalType(outerName);
+        name = outerName + "$" + innerName;
+        super.visitInnerClass(name, outerName, innerName, access);
+    }
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc,
+            String signature, String[] exceptions) {
+        desc = renameMethodDesc(desc);
+        signature = renameTypeSignature(signature);
+        MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
+        return new RenameMethodAdapter(mw);
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        desc = renameTypeDesc(desc);
+        return super.visitAnnotation(desc, visible);
+    }
+    
+    @Override
+    public FieldVisitor visitField(int access, String name, String desc,
+            String signature, Object value) {
+        desc = renameTypeDesc(desc);
+        signature = renameFieldSignature(signature);
+        return super.visitField(access, name, desc, signature, value);
+    }
+    
+    
+    //----------------------------------
+
+    /**
+     * A method visitor that renames all references from an old class name to a new class name.
+     */
+    public class RenameMethodAdapter extends MethodAdapter {
+
+        /**
+         * Creates a method visitor that renames all references from a given old name to a given new
+         * name. The method visitor will also rename all inner classes.
+         * The names must be full qualified internal ASM names (e.g. com/blah/MyClass$InnerClass).
+         */
+        public RenameMethodAdapter(MethodVisitor mv) {
+            super(mv);
+        }
+
+        @Override
+        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+            desc = renameTypeDesc(desc);
+            
+            return super.visitAnnotation(desc, visible);
+        }
+
+        @Override
+        public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
+            desc = renameTypeDesc(desc);
+
+            return super.visitParameterAnnotation(parameter, desc, visible);
+        }
+
+        @Override
+        public void visitTypeInsn(int opcode, String type) {
+            type = renameInternalType(type);
+            
+            super.visitTypeInsn(opcode, type);
+        }
+
+        @Override
+        public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+            owner = renameInternalType(owner);
+            desc = renameTypeDesc(desc);
+
+            super.visitFieldInsn(opcode, owner, name, desc);
+        }
+
+        @Override
+        public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+            owner = renameInternalType(owner);
+            desc = renameMethodDesc(desc);
+
+            super.visitMethodInsn(opcode, owner, name, desc);
+        }
+        
+        @Override
+        public void visitLdcInsn(Object cst) {
+            // If cst is a Type, this means the code is trying to pull the .class constant
+            // for this class, so it needs to be renamed too.
+            if (cst instanceof Type) {
+                cst = renameTypeAsType((Type) cst);
+            }
+            super.visitLdcInsn(cst);
+        }
+
+        @Override
+        public void visitMultiANewArrayInsn(String desc, int dims) {
+            desc = renameTypeDesc(desc);
+         
+            super.visitMultiANewArrayInsn(desc, dims);
+        }
+
+        @Override
+        public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+            type = renameInternalType(type);
+            
+            super.visitTryCatchBlock(start, end, handler, type);
+        }
+
+        @Override
+        public void visitLocalVariable(String name, String desc, String signature,
+                Label start, Label end, int index) {
+            desc = renameTypeDesc(desc);
+            signature = renameFieldSignature(signature);
+            
+            super.visitLocalVariable(name, desc, signature, start, end, index);
+        }
+
+    }
+
+    //----------------------------------
+    
+    public class RenameSignatureAdapter implements SignatureVisitor {
+
+        private final SignatureVisitor mSv;
+
+        public RenameSignatureAdapter(SignatureVisitor sv) {
+            mSv = sv;
+        }
+
+        public void visitClassType(String name) {
+            name = renameInternalType(name);
+            mSv.visitClassType(name);
+        }
+
+        public void visitInnerClassType(String name) {
+            name = renameInternalType(name);
+            mSv.visitInnerClassType(name);
+        }
+
+        public SignatureVisitor visitArrayType() {
+            SignatureVisitor sv = mSv.visitArrayType();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public void visitBaseType(char descriptor) {
+            mSv.visitBaseType(descriptor);
+        }
+
+        public SignatureVisitor visitClassBound() {
+            SignatureVisitor sv = mSv.visitClassBound();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public void visitEnd() {
+            mSv.visitEnd();
+        }
+
+        public SignatureVisitor visitExceptionType() {
+            SignatureVisitor sv = mSv.visitExceptionType();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public void visitFormalTypeParameter(String name) {
+            mSv.visitFormalTypeParameter(name);
+        }
+
+        public SignatureVisitor visitInterface() {
+            SignatureVisitor sv = mSv.visitInterface();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public SignatureVisitor visitInterfaceBound() {
+            SignatureVisitor sv = mSv.visitInterfaceBound();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public SignatureVisitor visitParameterType() {
+            SignatureVisitor sv = mSv.visitParameterType();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public SignatureVisitor visitReturnType() {
+            SignatureVisitor sv = mSv.visitReturnType();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public SignatureVisitor visitSuperclass() {
+            SignatureVisitor sv = mSv.visitSuperclass();
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public void visitTypeArgument() {
+            mSv.visitTypeArgument();
+        }
+
+        public SignatureVisitor visitTypeArgument(char wildcard) {
+            SignatureVisitor sv = mSv.visitTypeArgument(wildcard);
+            return new RenameSignatureAdapter(sv);
+        }
+
+        public void visitTypeVariable(String name) {
+            mSv.visitTypeVariable(name);
+        }
+        
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
new file mode 100644
index 0000000..9a57a4a
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
@@ -0,0 +1,350 @@
+/*
+ * 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 org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * This method adapter rewrites a method by discarding the original code and generating
+ * a stub depending on the return type. Original annotations are passed along unchanged.
+ */
+class StubMethodAdapter implements MethodVisitor {
+
+    private static String CONSTRUCTOR = "<init>";
+    private static String CLASS_INIT = "<clinit>";
+    
+    /** The parent method writer */
+    private MethodVisitor mParentVisitor;
+    /** The method return type. Can be null. */
+    private Type mReturnType;
+    /** Message to be printed by stub methods. */
+    private String mInvokeSignature;
+    /** Flag to output the first line number. */
+    private boolean mOutputFirstLineNumber = true;
+    /** Flag that is true when implementing a constructor, to accept all original 
+     *  code calling the original super constructor. */
+    private boolean mIsInitMethod = false;
+
+    private boolean mMessageGenerated;
+    private final boolean mIsStatic;
+    private final boolean mIsNative;
+
+    public StubMethodAdapter(MethodVisitor mv, String methodName, Type returnType,
+            String invokeSignature, boolean isStatic, boolean isNative) {
+        mParentVisitor = mv;
+        mReturnType = returnType;
+        mInvokeSignature = invokeSignature;
+        mIsStatic = isStatic;
+        mIsNative = isNative;
+        
+        if (CONSTRUCTOR.equals(methodName) || CLASS_INIT.equals(methodName)) {
+            mIsInitMethod = true;
+        }
+    }
+    
+    private void generateInvoke() {
+        /* Generates the code:
+         *  OverrideMethod.invoke("signature", mIsNative ? true : false, null or this);
+         */
+        mParentVisitor.visitLdcInsn(mInvokeSignature);
+        // push true or false
+        mParentVisitor.visitInsn(mIsNative ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
+        // push null or this
+        if (mIsStatic) {
+            mParentVisitor.visitInsn(Opcodes.ACONST_NULL);
+        } else {
+            mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+        }
+
+        int sort = mReturnType != null ? mReturnType.getSort() : Type.VOID;
+        switch(sort) {
+        case Type.VOID:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeV",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)V");
+            mParentVisitor.visitInsn(Opcodes.RETURN);
+            break;
+        case Type.BOOLEAN:
+        case Type.CHAR:
+        case Type.BYTE:
+        case Type.SHORT:
+        case Type.INT:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeI",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)I");
+            switch(sort) {
+            case Type.BOOLEAN:
+                Label l1 = new Label();
+                mParentVisitor.visitJumpInsn(Opcodes.IFEQ, l1);
+                mParentVisitor.visitInsn(Opcodes.ICONST_1);
+                mParentVisitor.visitInsn(Opcodes.IRETURN);
+                mParentVisitor.visitLabel(l1);
+                mParentVisitor.visitInsn(Opcodes.ICONST_0);
+                break;
+            case Type.CHAR:
+                mParentVisitor.visitInsn(Opcodes.I2C);
+                break;
+            case Type.BYTE:
+                mParentVisitor.visitInsn(Opcodes.I2B);
+                break;
+            case Type.SHORT:
+                mParentVisitor.visitInsn(Opcodes.I2S);
+                break;
+            }
+            mParentVisitor.visitInsn(Opcodes.IRETURN);
+            break;
+        case Type.LONG:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeL",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)J");
+            mParentVisitor.visitInsn(Opcodes.LRETURN);
+            break;
+        case Type.FLOAT:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeF",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)F");
+            mParentVisitor.visitInsn(Opcodes.FRETURN);
+            break;
+        case Type.DOUBLE:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeD",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)D");
+            mParentVisitor.visitInsn(Opcodes.DRETURN);
+            break;
+        case Type.ARRAY:
+        case Type.OBJECT:
+            mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                    "com/android/tools/layoutlib/create/OverrideMethod",
+                    "invokeA",
+                    "(Ljava/lang/String;ZLjava/lang/Object;)Ljava/lang/Object;");
+            mParentVisitor.visitTypeInsn(Opcodes.CHECKCAST, mReturnType.getInternalName());
+            mParentVisitor.visitInsn(Opcodes.ARETURN);
+            break;
+        }
+
+    }
+
+    private void generatePop() {
+        /* Pops the stack, depending on the return type.
+         */
+        switch(mReturnType != null ? mReturnType.getSort() : Type.VOID) {
+        case Type.VOID:
+            break;
+        case Type.BOOLEAN:
+        case Type.CHAR:
+        case Type.BYTE:
+        case Type.SHORT:
+        case Type.INT:
+        case Type.FLOAT:
+        case Type.ARRAY:
+        case Type.OBJECT:
+            mParentVisitor.visitInsn(Opcodes.POP);
+            break;
+        case Type.LONG:
+        case Type.DOUBLE:
+            mParentVisitor.visitInsn(Opcodes.POP2);
+            break;
+        }
+    }
+
+    /* Pass down to visitor writer. In this implementation, either do nothing. */
+    public void visitCode() {
+        mParentVisitor.visitCode();
+    }
+
+    /*
+     * visitMaxs is called just before visitEnd if there was any code to rewrite.
+     * For non-constructor, generate the messaging code and the return statement
+     * if it hasn't been done before.
+     */
+    public void visitMaxs(int maxStack, int maxLocals) {
+        if (!mIsInitMethod && !mMessageGenerated) {
+            generateInvoke();
+            mMessageGenerated = true;
+        }
+        mParentVisitor.visitMaxs(maxStack, maxLocals);
+    }
+    
+    /**
+     * End of visiting.
+     * For non-constructor, generate the messaging code and the return statement
+     * if it hasn't been done before.
+     */
+    public void visitEnd() {
+        if (!mIsInitMethod && !mMessageGenerated) {
+            generateInvoke();
+            mMessageGenerated = true;
+            mParentVisitor.visitMaxs(1, 1);
+        }
+        mParentVisitor.visitEnd();
+    }
+
+    /* 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 (mIsInitMethod || mOutputFirstLineNumber) {
+            mParentVisitor.visitLineNumber(line, start);
+            mOutputFirstLineNumber = false;
+        }
+    }
+
+    /**
+     * For non-constructor, rewrite existing "return" instructions to write the message.
+     */
+    public void visitInsn(int opcode) {
+        if (mIsInitMethod) {
+            switch (opcode) {
+            case Opcodes.RETURN:
+            case Opcodes.ARETURN:
+            case Opcodes.DRETURN:
+            case Opcodes.FRETURN:
+            case Opcodes.IRETURN:
+            case Opcodes.LRETURN:
+                // Pop the last word from the stack since invoke will generate its own return.
+                generatePop();
+                generateInvoke();
+                mMessageGenerated = true;
+            default:
+                mParentVisitor.visitInsn(opcode);
+            }
+        }
+    }
+
+    public void visitLabel(Label label) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitLabel(label);
+        }
+    }
+
+    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitTryCatchBlock(start, end, handler, type);
+        }
+    }
+
+    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitMethodInsn(opcode, owner, name, desc);
+        }
+    }
+
+    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitFieldInsn(opcode, owner, name, desc);
+        }
+    }
+
+    public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitFrame(type, nLocal, local, nStack, stack);
+        }
+    }
+
+    public void visitIincInsn(int var, int increment) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitIincInsn(var, increment);
+        }
+    }
+
+    public void visitIntInsn(int opcode, int operand) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitIntInsn(opcode, operand);
+        }
+    }
+
+    public void visitJumpInsn(int opcode, Label label) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitJumpInsn(opcode, label);
+        }
+    }
+
+    public void visitLdcInsn(Object cst) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitLdcInsn(cst);
+        }
+    }
+
+    public void visitLocalVariable(String name, String desc, String signature,
+            Label start, Label end, int index) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitLocalVariable(name, desc, signature, start, end, index);
+        }
+    }
+
+    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitLookupSwitchInsn(dflt, keys, labels);
+        }
+    }
+
+    public void visitMultiANewArrayInsn(String desc, int dims) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitMultiANewArrayInsn(desc, dims);
+        }
+    }
+
+    public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitTableSwitchInsn(min, max, dflt, labels);
+        }
+    }
+
+    public void visitTypeInsn(int opcode, String type) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitTypeInsn(opcode, type);
+        }
+    }
+
+    public void visitVarInsn(int opcode, int var) {
+        if (mIsInitMethod) {
+            mParentVisitor.visitVarInsn(opcode, var);
+        }
+    }
+    
+}
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
new file mode 100644
index 0000000..e294d56
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
@@ -0,0 +1,177 @@
+/*
+ * 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 org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.util.Set;
+
+/**
+ * Class adapter that can stub some or all of the methods of the class. 
+ */
+class TransformClassAdapter extends ClassAdapter {
+
+    /** True if all methods should be stubbed, false if only native ones must be stubbed. */
+    private final boolean mStubAll;
+    /** True if the class is an interface. */
+    private boolean mIsInterface;
+    private final String mClassName;
+    private final Log mLog;
+    private final Set<String> mStubMethods;
+    private Set<String> mDeleteReturns;
+
+    /**
+     * Creates a new class adapter that will stub some or all methods.
+     * @param logger 
+     * @param stubMethods 
+     * @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 
+     *                        methods should be stubbed.
+     * @param hasNative True if the method has natives, in which case its access should be
+     *                  changed.
+     */
+    public TransformClassAdapter(Log logger, Set<String> stubMethods,
+            Set<String> deleteReturns, String className, ClassVisitor cv,
+            boolean stubNativesOnly, boolean hasNative) {
+        super(cv);
+        mLog = logger;
+        mStubMethods = stubMethods;
+        mClassName = className;
+        mStubAll = !stubNativesOnly;
+        mIsInterface = false;
+        mDeleteReturns = deleteReturns;
+    }
+
+    /* Visits the class header. */
+    @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;
+        // remove final
+        access = access & ~Opcodes.ACC_FINAL;
+        // note: leave abstract classes as such
+        // don't try to implement stub for interfaces
+
+        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) {
+        // remove protected or private and set as public
+        access = access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED);
+        access |= Opcodes.ACC_PUBLIC;
+        // remove final
+        access = access & ~Opcodes.ACC_FINAL;
+        // note: leave abstract classes as such
+        // don't try to implement stub for interfaces
+
+        super.visitInnerClass(name, outerName, innerName, access);
+    }
+
+    /* Visits a method. */
+    @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) {
+                String returnType = t.getInternalName();
+                if (returnType != null) {
+                    if (mDeleteReturns.contains(returnType)) {
+                        return null;
+                    }
+                }
+            }
+        }
+
+        String methodSignature = mClassName.replace('/', '.') + "#" + name;
+
+        // change access to public
+        access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE);
+        access |= Opcodes.ACC_PUBLIC;
+
+        // remove final
+        access = access & ~Opcodes.ACC_FINAL;
+
+        // stub this method if they are all to be stubbed or if it is a native method
+        // and don't try to stub interfaces nor abstract non-native methods.
+        if (!mIsInterface &&
+            ((access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) != Opcodes.ACC_ABSTRACT) &&
+            (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);
+
+        } else {
+            mLog.debug("  Keep: %s %s", name, desc);
+            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,
+            Object value) {
+        // change access to public
+        access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE);
+        access |= Opcodes.ACC_PUBLIC;
+        
+        return super.visitField(access, name, desc, signature, value);
+    }
+
+    /**
+     * Extracts the return {@link Type} of this descriptor.
+     */
+    Type returnType(String desc) {
+        if (desc != null) {
+            try {
+                return Type.getReturnType(desc);
+            } catch (ArrayIndexOutOfBoundsException e) {
+                // ignore, not a valid type.
+            }
+        }
+        return null;
+    }
+}
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
new file mode 100644
index 0000000..603284e
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
@@ -0,0 +1,228 @@
+/*
+ * 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 static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+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;
+import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Unit tests for some methods of {@link AsmAnalyzer}.
+ */
+public class AsmAnalyzerTest {
+
+    private MockLog mLog;
+    private ArrayList<String> mOsJarPath;
+    private AsmAnalyzer mAa;
+
+    @Before
+    public void setUp() throws Exception {
+        mLog = new LogTest.MockLog();
+        URL url = this.getClass().getClassLoader().getResource("data/mock_android.jar");
+        
+        mOsJarPath = new ArrayList<String>();
+        mOsJarPath.add(url.getFile());
+
+        mAa = new AsmAnalyzer(mLog, mOsJarPath, null /* gen */,
+                null /* deriveFrom */, null /* includeGlobs */ );
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+
+    @Test
+    public void testParseZip() throws IOException {
+        Map<String, ClassReader> map = mAa.parseZip(mOsJarPath);
+
+        assertArrayEquals(new String[] {
+                "mock_android.dummy.InnerTest",
+                "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.view.View",
+                "mock_android.view.ViewGroup",
+                "mock_android.view.ViewGroup$LayoutParams",
+                "mock_android.view.ViewGroup$MarginLayoutParams",
+                "mock_android.widget.LinearLayout",
+                "mock_android.widget.LinearLayout$LayoutParams",
+                "mock_android.widget.TableLayout",
+                "mock_android.widget.TableLayout$LayoutParams"
+            },
+            map.keySet().toArray());
+    }
+    
+    @Test
+    public void testFindClass() throws IOException, LogAbortException {
+        Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
+        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
+
+        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" },
+                found.keySet().toArray());
+        assertArrayEquals(new ClassReader[] { cr }, found.values().toArray());
+    }
+
+    @Test
+    public void testFindGlobs() throws IOException, LogAbortException {
+        Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
+        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
+
+        // this matches classes, a package match returns nothing
+        found.clear();
+        mAa.findGlobs("mock_android.view", zipClasses, found);
+
+        assertArrayEquals(new String[] { },
+            found.keySet().toArray());
+
+        // a complex glob search. * is a search pattern that matches names, not dots
+        mAa.findGlobs("mock_android.*.*Group$*Layout*", zipClasses, found);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.ViewGroup$LayoutParams",
+                "mock_android.view.ViewGroup$MarginLayoutParams"
+            },
+            found.keySet().toArray());
+
+        // a complex glob search. ** is a search pattern that matches names including dots
+        mAa.findGlobs("mock_android.**Group*", zipClasses, found);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.ViewGroup",
+                "mock_android.view.ViewGroup$LayoutParams",
+                "mock_android.view.ViewGroup$MarginLayoutParams"
+            },
+            found.keySet().toArray());
+
+        // matches a single class
+        found.clear();
+        mAa.findGlobs("mock_android.view.View", zipClasses, found);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.View"
+            },
+            found.keySet().toArray());
+
+        // matches everyting inside the given package but not sub-packages
+        found.clear();
+        mAa.findGlobs("mock_android.view.*", zipClasses, found);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.View",
+                "mock_android.view.ViewGroup",
+                "mock_android.view.ViewGroup$LayoutParams",
+                "mock_android.view.ViewGroup$MarginLayoutParams"
+            },
+            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 testFindClassesDerivingFrom() throws LogAbortException, IOException {
+        Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
+        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
+
+        mAa.findClassesDerivingFrom("mock_android.view.View", zipClasses, found);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.View",
+                "mock_android.view.ViewGroup",
+                "mock_android.widget.LinearLayout",
+                "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);
+        TreeMap<String, ClassReader> keep = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> new_keep = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> in_deps = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> out_deps = new TreeMap<String, ClassReader>();
+
+        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 */);
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.ViewGroup",
+                "mock_android.widget.TableLayout$LayoutParams",
+            },
+            out_deps.keySet().toArray());
+
+        in_deps.putAll(out_deps);
+        out_deps.clear();
+
+        // get second level dependencies
+        for (ClassReader cr2 : in_deps.values()) {
+            cr2.accept(visitor, 0 /* flags */);
+        }
+
+        assertArrayEquals(new String[] {
+                "mock_android.view.View",
+                "mock_android.view.ViewGroup$LayoutParams",
+                "mock_android.view.ViewGroup$MarginLayoutParams",
+            },
+            out_deps.keySet().toArray());
+
+        in_deps.putAll(out_deps);
+        out_deps.clear();
+
+        // get third level dependencies (there are none)
+        for (ClassReader cr2 : in_deps.values()) {
+            cr2.accept(visitor, 0 /* flags */);
+        }
+
+        assertArrayEquals(new String[] { }, out_deps.keySet().toArray());
+    }
+}
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
new file mode 100644
index 0000000..7cdf79a
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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 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;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * Unit tests for some methods of {@link AsmGenerator}.
+ */
+public class AsmGeneratorTest {
+
+    private MockLog mLog;
+    private ArrayList<String> mOsJarPath;
+    private String mOsDestJar;
+    private File mTempFile;
+
+    @Before
+    public void setUp() throws Exception {
+        mLog = new LogTest.MockLog();
+        URL url = this.getClass().getClassLoader().getResource("data/mock_android.jar");
+        
+        mOsJarPath = new ArrayList<String>();
+        mOsJarPath.add(url.getFile());
+
+        mTempFile = File.createTempFile("mock", "jar");
+        mOsDestJar = mTempFile.getAbsolutePath();
+        mTempFile.deleteOnExit();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mTempFile != null) {
+            mTempFile.delete();
+            mTempFile = null;
+        }
+    }
+
+    @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.
+            );
+
+        AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath, agen,
+                null,                 // derived from
+                new String[] {        // include classes
+                    "**"
+                });
+        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/LogTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/LogTest.java
new file mode 100644
index 0000000..3f13158
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/LogTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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 static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+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();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // pass
+    }
+
+    @Test
+    public void testDebug() {
+        assertEquals("", mLog.getOut());
+        assertEquals("", mLog.getErr());
+
+        mLog.setVerbose(false);
+        mLog.debug("Test %d", 42);
+        assertEquals("", mLog.getOut());
+
+        mLog.setVerbose(true);
+        mLog.debug("Test %d", 42);
+
+        assertEquals("Test 42\n", mLog.getOut());
+        assertEquals("", mLog.getErr());
+    }
+
+    @Test
+    public void testInfo() {
+        assertEquals("", mLog.getOut());
+        assertEquals("", mLog.getErr());
+
+        mLog.info("Test %d", 43);
+
+        assertEquals("Test 43\n", mLog.getOut());
+        assertEquals("", mLog.getErr());
+    }
+
+    @Test
+    public void testError() {
+        assertEquals("", mLog.getOut());
+        assertEquals("", mLog.getErr());
+
+        mLog.error("Test %d", 44);
+
+        assertEquals("", mLog.getOut());
+        assertEquals("Test 44\n", mLog.getErr());
+    }
+
+    @Test
+    public void testException() {
+        assertEquals("", mLog.getOut());
+        assertEquals("", mLog.getErr());
+
+        Exception e = new Exception("My Exception");
+        mLog.exception(e, "Test %d", 44);
+
+        assertEquals("", mLog.getOut());
+        assertTrue(mLog.getErr().startsWith("Test 44\njava.lang.Exception: My Exception"));
+    }
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/RenameClassAdapterTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/RenameClassAdapterTest.java
new file mode 100644
index 0000000..90c6a9c
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/RenameClassAdapterTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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 static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class RenameClassAdapterTest {
+
+    private RenameClassAdapter mOuter;
+    private RenameClassAdapter mInner;
+
+    @Before
+    public void setUp() throws Exception {
+        mOuter = new RenameClassAdapter(null, // cv
+                                         "com.pack.Old",
+                                         "org.blah.New");
+        
+        mInner = new RenameClassAdapter(null, // cv
+                                         "com.pack.Old$Inner",
+                                         "org.blah.New$Inner"); 
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+
+    /**
+     * Renames a type, e.g. "Lcom.package.My;"
+     * If the type doesn't need to be renamed, returns the input string as-is.
+     */
+    @Test
+    public void testRenameTypeDesc() {
+
+        // primitive types are left untouched
+        assertEquals("I", mOuter.renameTypeDesc("I"));
+        assertEquals("D", mOuter.renameTypeDesc("D"));
+        assertEquals("V", mOuter.renameTypeDesc("V"));
+
+        // object types that need no renaming are left untouched
+        assertEquals("Lcom.package.MyClass;", mOuter.renameTypeDesc("Lcom.package.MyClass;"));
+        assertEquals("Lcom.package.MyClass;", mInner.renameTypeDesc("Lcom.package.MyClass;"));
+
+        // object types that match the requirements
+        assertEquals("Lorg.blah.New;", mOuter.renameTypeDesc("Lcom.pack.Old;"));
+        assertEquals("Lorg.blah.New$Inner;", mInner.renameTypeDesc("Lcom.pack.Old$Inner;"));
+        // inner classes match the base type which is being renamed
+        assertEquals("Lorg.blah.New$Other;", mOuter.renameTypeDesc("Lcom.pack.Old$Other;"));
+        assertEquals("Lorg.blah.New$Other;", mInner.renameTypeDesc("Lcom.pack.Old$Other;"));
+
+        // arrays
+        assertEquals("[Lorg.blah.New;",  mOuter.renameTypeDesc("[Lcom.pack.Old;"));
+        assertEquals("[[Lorg.blah.New;", mOuter.renameTypeDesc("[[Lcom.pack.Old;"));
+        
+        assertEquals("[Lorg.blah.New;",  mInner.renameTypeDesc("[Lcom.pack.Old;"));
+        assertEquals("[[Lorg.blah.New;", mInner.renameTypeDesc("[[Lcom.pack.Old;"));
+    }
+
+    /**
+     * Renames an object type, e.g. "Lcom.package.MyClass;" or an array type that has an
+     * object element, e.g. "[Lcom.package.MyClass;"
+     * If the type doesn't need to be renamed, returns the internal name of the input type.
+     */
+    @Test
+    public void testRenameType() {
+        // Skip. This is actually tested by testRenameTypeDesc above.
+    }
+
+    /**
+     * Renames an internal type name, e.g. "com.package.MyClass".
+     * If the type doesn't need to be renamed, returns the input string as-is.
+     */
+    @Test
+    public void testRenameInternalType() {
+        // a descriptor is not left untouched
+        assertEquals("Lorg.blah.New;", mOuter.renameInternalType("Lcom.pack.Old;"));
+        assertEquals("Lorg.blah.New$Inner;", mOuter.renameInternalType("Lcom.pack.Old$Inner;"));
+
+        // an actual FQCN
+        assertEquals("org.blah.New", mOuter.renameInternalType("com.pack.Old"));
+        assertEquals("org.blah.New$Inner", mOuter.renameInternalType("com.pack.Old$Inner"));
+
+        assertEquals("org.blah.New$Other", mInner.renameInternalType("com.pack.Old$Other"));
+        assertEquals("org.blah.New$Other", mInner.renameInternalType("com.pack.Old$Other"));
+    }
+
+    /**
+     * Renames a method descriptor, i.e. applies renameType to all arguments and to the
+     * return value.
+     */
+    @Test
+    public void testRenameMethodDesc() {
+        assertEquals("(IDLorg.blah.New;[Lorg.blah.New$Inner;)Lorg.blah.New$Other;",
+               mOuter.renameMethodDesc("(IDLcom.pack.Old;[Lcom.pack.Old$Inner;)Lcom.pack.Old$Other;"));
+    }
+
+    
+
+}
diff --git a/tools/layoutlib/create/tests/data/mock_android.jar b/tools/layoutlib/create/tests/data/mock_android.jar
new file mode 100644
index 0000000..a7ea74f
--- /dev/null
+++ b/tools/layoutlib/create/tests/data/mock_android.jar
Binary files differ
diff --git a/tools/layoutlib/create/tests/data/mock_android.jardesc b/tools/layoutlib/create/tests/data/mock_android.jardesc
new file mode 100644
index 0000000..95f7591
--- /dev/null
+++ b/tools/layoutlib/create/tests/data/mock_android.jardesc
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="WINDOWS-1252" standalone="no"?>
+<jardesc>
+    <jar path="C:/ralf/google/src/raphael-lapdroid/device/tools/layoutlib/create/tests/data/mock_android.jar"/>
+    <options buildIfNeeded="true" compress="true" descriptionLocation="/layoutlib_create/tests/data/mock_android.jardesc" exportErrors="true" exportWarnings="true" includeDirectoryEntries="false" overwrite="false" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/>
+    <storedRefactorings deprecationInfo="true" structuralOnly="false"/>
+    <selectedProjects/>
+    <manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true">
+        <sealing sealJar="false">
+            <packagesToSeal/>
+            <packagesToUnSeal/>
+        </sealing>
+    </manifest>
+    <selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false">
+        <javaElement handleIdentifier="=layoutlib_create/tests&lt;mock_android.widget"/>
+        <javaElement handleIdentifier="=layoutlib_create/tests&lt;mock_android.view"/>
+        <javaElement handleIdentifier="=layoutlib_create/tests&lt;mock_android.dummy"/>
+    </selectedElements>
+</jardesc>
diff --git a/tools/layoutlib/create/tests/mock_android/dummy/InnerTest.java b/tools/layoutlib/create/tests/mock_android/dummy/InnerTest.java
new file mode 100644
index 0000000..e355ead
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_android/dummy/InnerTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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 mock_android.dummy;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+public class InnerTest {
+
+    private int mSomeField;
+    private MyStaticInnerClass mInnerInstance;
+    private MyIntEnum mTheIntEnum;
+    private MyGenerics1<int[][], InnerTest, MyIntEnum, float[]> mGeneric1;
+
+    public class NotStaticInner2 extends NotStaticInner1 {
+
+    }
+
+    public class NotStaticInner1 {
+
+        public void someThing() {
+            mSomeField = 2;
+            mInnerInstance = null;
+        }
+
+    }
+
+    private static class MyStaticInnerClass {
+
+    }
+    
+    private static class DerivingClass extends InnerTest {
+        
+    }
+    
+    // enums are a kind of inner static class
+    public enum MyIntEnum {
+        VALUE0(0),
+        VALUE1(1),
+        VALUE2(2);
+
+        MyIntEnum(int myInt) {
+            this.myInt = myInt;
+        }
+        final int myInt;
+    }
+    
+    public static class MyGenerics1<T, U, V, W> {
+        public MyGenerics1() {
+            int a = 1;
+        }
+    }
+    
+    public <X> void genericMethod1(X a, X[] a) {
+    }
+
+    public <X, Y> void genericMethod2(X a, List<Y> b) {
+    }
+
+    public <X, Y> void genericMethod3(X a, List<Y extends InnerTest> b) {
+    }
+
+    public <T extends InnerTest> void genericMethod4(T[] a, Collection<T> b, Collection<?> c) {
+        Iterator<T> i = b.iterator();
+    }
+
+    public void someMethod(InnerTest self) {
+        mSomeField = self.mSomeField;
+        MyStaticInnerClass m = new MyStaticInnerClass();
+        mInnerInstance = m;
+        mTheIntEnum = null;
+        mGeneric1 = new MyGenerics1();
+        genericMethod(new DerivingClass[0], new ArrayList<DerivingClass>(), new ArrayList<InnerTest>());
+    }
+}
diff --git a/tools/layoutlib/create/tests/mock_android/view/View.java b/tools/layoutlib/create/tests/mock_android/view/View.java
new file mode 100644
index 0000000..a80a98d
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_android/view/View.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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 mock_android.view;
+
+public class View {
+
+}
diff --git a/tools/layoutlib/create/tests/mock_android/view/ViewGroup.java b/tools/layoutlib/create/tests/mock_android/view/ViewGroup.java
new file mode 100644
index 0000000..466470f
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_android/view/ViewGroup.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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 mock_android.view;
+
+public class ViewGroup extends View {
+
+    public class MarginLayoutParams extends LayoutParams {
+
+    }
+
+    public class LayoutParams {
+
+    }
+
+}
diff --git a/tools/layoutlib/create/tests/mock_android/widget/LinearLayout.java b/tools/layoutlib/create/tests/mock_android/widget/LinearLayout.java
new file mode 100644
index 0000000..3870a63
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_android/widget/LinearLayout.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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 mock_android.widget;
+
+import mock_android.view.ViewGroup;
+
+public class LinearLayout extends ViewGroup {
+
+    public class LayoutParams extends mock_android.view.ViewGroup.LayoutParams {
+
+    }
+
+}
diff --git a/tools/layoutlib/create/tests/mock_android/widget/TableLayout.java b/tools/layoutlib/create/tests/mock_android/widget/TableLayout.java
new file mode 100644
index 0000000..e455e7d
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_android/widget/TableLayout.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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 mock_android.widget;
+
+import mock_android.view.ViewGroup;
+
+public class TableLayout extends ViewGroup {
+
+    public class LayoutParams extends MarginLayoutParams {
+
+    }
+
+}