Merge "Update asm-7.0 to asm-9.2." am: b48d95cd69 am: ec0f336191 am: 3f7de11b7b

Original change: https://android-review.googlesource.com/c/platform/frameworks/layoutlib/+/2016076

Change-Id: Ib3c346805d29e190931a6e73409d9854b7cd2c5a
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
deleted file mode 100644
index 3b31283..0000000
--- a/.idea/inspectionProfiles/profiles_settings.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<component name="InspectionProjectProfileManager">
-  <settings>
-    <option name="PROJECT_PROFILE" value="Project Default" />
-    <option name="USE_PROJECT_PROFILE" value="true" />
-    <version value="1.0" />
-  </settings>
-</component>
\ No newline at end of file
diff --git a/.idea/libraries/framework_jar.xml b/.idea/libraries/framework_jar.xml
index 2ad7916..82af4c5 100644
--- a/.idea/libraries/framework_jar.xml
+++ b/.idea/libraries/framework_jar.xml
@@ -8,6 +8,7 @@
       <root url="file://$PROJECT_DIR$/../base/core/java" />
       <root url="file://$PROJECT_DIR$/../base/graphics/java" />
       <root url="file://$PROJECT_DIR$/../../libcore/luni/src/main/java" />
+      <root url="file://$PROJECT_DIR$/../../libcore/dalvik/src/main/java" />
     </SOURCES>
   </library>
 </component>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 22a2093..943a663 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -13,7 +13,7 @@
     <option name="myDefaultNotNull" value="android.annotation.NonNull" />
     <option name="myNullables">
       <value>
-        <list size="13">
+        <list size="15">
           <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
           <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
           <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
@@ -26,13 +26,15 @@
           <item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
           <item index="10" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
           <item index="11" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
-          <item index="12" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.Nullable" />
+          <item index="12" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
+          <item index="13" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
+          <item index="14" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
         </list>
       </value>
     </option>
     <option name="myNotNulls">
       <value>
-        <list size="12">
+        <list size="14">
           <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
           <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
           <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
@@ -44,12 +46,14 @@
           <item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
           <item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
           <item index="10" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
-          <item index="11" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.NonNull" />
+          <item index="11" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
+          <item index="12" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
+          <item index="13" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
         </list>
       </value>
     </option>
   </component>
-  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_9" default="false" project-jdk-name="11" project-jdk-type="JavaSDK">
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
     <output url="file://$PROJECT_DIR$/out" />
   </component>
 </project>
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 5210215..5b018bb 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -5,6 +5,7 @@
       <module fileurl="file://$PROJECT_DIR$/bridge/bridge.iml" filepath="$PROJECT_DIR$/bridge/bridge.iml" />
       <module fileurl="file://$PROJECT_DIR$/common/common.iml" filepath="$PROJECT_DIR$/common/common.iml" />
       <module fileurl="file://$PROJECT_DIR$/create/create.iml" filepath="$PROJECT_DIR$/create/create.iml" />
+      <module fileurl="file://$PROJECT_DIR$/delegates/delegates.iml" filepath="$PROJECT_DIR$/delegates/delegates.iml" />
       <module fileurl="file://$PROJECT_DIR$/remote/client/remote client.iml" filepath="$PROJECT_DIR$/remote/client/remote client.iml" group="remote" />
       <module fileurl="file://$PROJECT_DIR$/remote/common/remote common.iml" filepath="$PROJECT_DIR$/remote/common/remote common.iml" group="remote" />
       <module fileurl="file://$PROJECT_DIR$/remote/server/remote server.iml" filepath="$PROJECT_DIR$/remote/server/remote server.iml" group="remote" />
diff --git a/.idea/runConfigurations/All_in_bridge.xml b/.idea/runConfigurations/All_in_bridge.xml
index 1e82efc..e89deb3 100644
--- a/.idea/runConfigurations/All_in_bridge.xml
+++ b/.idea/runConfigurations/All_in_bridge.xml
@@ -1,7 +1,6 @@
 <component name="ProjectRunConfigurationManager">
   <configuration default="false" name="All in bridge" type="JUnit" factoryName="JUnit" singleton="true">
     <module name="bridge" />
-    <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
     <option name="ALTERNATIVE_JRE_PATH" value="11" />
     <option name="PACKAGE_NAME" value="" />
     <option name="MAIN_CLASS_NAME" value="" />
diff --git a/.idea/runConfigurations/All_in_create.xml b/.idea/runConfigurations/All_in_create.xml
index b9cd419..08ae5b5 100644
--- a/.idea/runConfigurations/All_in_create.xml
+++ b/.idea/runConfigurations/All_in_create.xml
@@ -1,23 +1,12 @@
 <component name="ProjectRunConfigurationManager">
   <configuration default="false" name="All in create" type="JUnit" factoryName="JUnit" singleton="false" nameIsGenerated="true">
-    <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
     <module name="create" />
-    <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
-    <option name="ALTERNATIVE_JRE_PATH" value="" />
     <option name="PACKAGE_NAME" value="" />
     <option name="MAIN_CLASS_NAME" value="" />
     <option name="METHOD_NAME" value="" />
     <option name="TEST_OBJECT" value="package" />
-    <option name="VM_PARAMETERS" value="-ea" />
     <option name="PARAMETERS" value="" />
-    <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
-    <option name="ENV_VARIABLES" />
-    <option name="PASS_PARENT_ENVS" value="true" />
-    <option name="TEST_SEARCH_SCOPE">
-      <value defaultName="singleModule" />
-    </option>
-    <envs />
-    <patterns />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
     <RunnerSettings RunnerId="Debug">
       <option name="DEBUG_PORT" value="" />
       <option name="TRANSPORT" value="0" />
@@ -26,6 +15,8 @@
     <RunnerSettings RunnerId="Run" />
     <ConfigurationWrapper RunnerId="Debug" />
     <ConfigurationWrapper RunnerId="Run" />
-    <method />
+    <method v="2">
+      <option name="Make" enabled="true" />
+    </method>
   </configuration>
 </component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/Bridge_quick.xml b/.idea/runConfigurations/Bridge_quick.xml
index e9797b0..9ed99d1 100644
--- a/.idea/runConfigurations/Bridge_quick.xml
+++ b/.idea/runConfigurations/Bridge_quick.xml
@@ -1,8 +1,6 @@
 <component name="ProjectRunConfigurationManager">
   <configuration default="false" name="Bridge quick" type="JUnit" factoryName="JUnit">
     <module name="bridge" />
-    <useClassPathOnly />
-    <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
     <option name="ALTERNATIVE_JRE_PATH" value="11" />
     <option name="MAIN_CLASS_NAME" value="" />
     <option name="METHOD_NAME" value="" />
@@ -11,13 +9,10 @@
     <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
     <patterns>
       <pattern testClass="com.android.layoutlib.bridge.TestDelegates" />
-      <pattern testClass="android.graphics.Matrix_DelegateTest" />
       <pattern testClass="com.android.layoutlib.bridge.android.BridgeXmlBlockParserTest" />
     </patterns>
     <RunnerSettings RunnerId="Run" />
     <ConfigurationWrapper RunnerId="Run" />
-    <method v="2">
-      <option name="Make" enabled="true" />
-    </method>
+    <method v="2" />
   </configuration>
 </component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/Create.xml b/.idea/runConfigurations/Create.xml
index a3fd3a1..11a2ae4 100644
--- a/.idea/runConfigurations/Create.xml
+++ b/.idea/runConfigurations/Create.xml
@@ -1,10 +1,9 @@
 <component name="ProjectRunConfigurationManager">
   <configuration default="false" name="Create" type="Application" factoryName="Application" singleton="true">
     <option name="ALTERNATIVE_JRE_PATH" value="$PROJECT_DIR$/../../prebuilts/jdk/jdk9/linux-x86" />
-    <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
     <option name="MAIN_CLASS_NAME" value="com.android.tools.layoutlib.create.Main" />
     <module name="create" />
-    <option name="PROGRAM_PARAMETERS" value="--create-stub out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar out/host/common/obj/JAVA_LIBRARIES/icu4j-icudata-jarjar_intermediates/classes.jar out/host/common/obj/JAVA_LIBRARIES/icu4j-icutzdata-jarjar_intermediates/classes.jar" />
+    <option name="PROGRAM_PARAMETERS" value="--create-stub out/soong/.temp/temp_layoutlib.jar out/soong/.intermediates/prebuilts/misc/common/atf/atf-prebuilt-jars-371374941/linux_glibc_common/combined/atf-prebuilt-jars-371374941.jar out/soong/.intermediates/external/icu/android_icu4j/core-icu4j-for-host/android_common/withres/core-icu4j-for-host.jar out/soong/.intermediates/libcore/core-libart/android_common/javac/core-libart.jar out/soong/.intermediates/frameworks/base/framework-all/android_common/combined/framework-all.jar out/soong/.intermediates/frameworks/base/ext/android_common/withres/ext.jar out/soong/.intermediates/external/icu/icu4j/icu4j-icudata-jarjar/linux_glibc_common/jarjar/icu4j-icudata-jarjar.jar out/soong/.intermediates/external/icu/icu4j/icu4j-icutzdata-jarjar/linux_glibc_common/jarjar/icu4j-icutzdata-jarjar.jar" />
     <option name="VM_PARAMETERS" value="-ea" />
     <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/../.." />
     <RunnerSettings RunnerId="Debug">
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index c80f219..df496fd 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,7 +1,40 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
+  <component name="IssueNavigationConfiguration">
+    <option name="links">
+      <list>
+        <IssueNavigationLink>
+          <option name="issueRegexp" value="\bb/(\d+)(#\w+)?\b" />
+          <option name="linkRegexp" value="https://buganizer.corp.google.com/issues/$1$2" />
+        </IssueNavigationLink>
+        <IssueNavigationLink>
+          <option name="issueRegexp" value="\b(?:BUG=|FIXED=)(\d+)\b" />
+          <option name="linkRegexp" value="https://buganizer.corp.google.com/issues/$1" />
+        </IssueNavigationLink>
+        <IssueNavigationLink>
+          <option name="issueRegexp" value="\b(?:cl/|cr/|OCL=|DIFFBASE=|ROLLBACK_OF=)(\d+)\b" />
+          <option name="linkRegexp" value="https://critique.corp.google.com/$1" />
+        </IssueNavigationLink>
+        <IssueNavigationLink>
+          <option name="issueRegexp" value="\bomg/(\d+)\b" />
+          <option name="linkRegexp" value="https://omg.corp.google.com/$1" />
+        </IssueNavigationLink>
+        <IssueNavigationLink>
+          <option name="issueRegexp" value="\b(?:go/|goto/)([^,.&lt;&gt;()&quot;\s]+(?:[.,][^,.&lt;&gt;()&quot;\s]+)*)" />
+          <option name="linkRegexp" value="https://goto.google.com/$1" />
+        </IssueNavigationLink>
+        <IssueNavigationLink>
+          <option name="issueRegexp" value="\bcs/([^\s]+[\w$])" />
+          <option name="linkRegexp" value="https://cs.corp.google.com/search/?q=$1" />
+        </IssueNavigationLink>
+        <IssueNavigationLink>
+          <option name="issueRegexp" value="(LINT\.IfChange)|(LINT\.ThenChange)" />
+          <option name="linkRegexp" value="https://goto.google.com/ifthisthenthatlint" />
+        </IssueNavigationLink>
+      </list>
+    </option>
+  </component>
   <component name="VcsDirectoryMappings">
     <mapping directory="$PROJECT_DIR$" vcs="Git" />
   </component>
-</project>
-
+</project>
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
index db7bb1d..0be4ccc 100644
--- a/Android.bp
+++ b/Android.bp
@@ -30,7 +30,7 @@
     tools: ["layoutlib_create"],
     out: ["temp_layoutlib.jar"],
     srcs: [
-        ":atf-prebuilt{.jar}",
+        ":atf-prebuilt-371374941{.jar}",
         ":core-icu4j-for-host{.jar}",
         ":core-libart-for-host{.jar}",
         ":framework-all{.jar}",
@@ -40,3 +40,26 @@
     ],
     cmd: "rm -f $(out) && $(location layoutlib_create) --create-stub $(out) $(in)",
 }
+
+java_genrule_host {
+    name: "layoutlib-native-delegates",
+    tools: ["layoutlib_create"],
+    out: ["layoutlib-native-delegates.jar"],
+    srcs: [
+        ":framework{.jar}",
+    ],
+    cmd: "rm -f $(out) && $(location layoutlib_create) --create-native-only-delegates $(out) $(in)",
+}
+
+java_device_for_host {
+    name: "layoutlib_create-classpath",
+    libs: [
+        "conscrypt-for-host",
+        "core-icu4j-for-host",
+        "core-libart-for-host",
+        "ext",
+        "framework-all",
+        "icu4j-icudata-jarjar",
+        "icu4j-icutzdata-jarjar",
+    ],
+}
diff --git a/bridge/Android.bp b/bridge/Android.bp
index 6e86073..5eefce2 100644
--- a/bridge/Android.bp
+++ b/bridge/Android.bp
@@ -27,16 +27,16 @@
     libs: [
         "kxml2-2.3.0",
         "layoutlib_api-prebuilt",
-        "tools-common-prebuilt",
         "guava",
+        "ninepatch-prebuilt",
+        "sdk-common",
     ],
 
     static_libs: [
         "temp_layoutlib",
-        "ninepatch-prebuilt",
         "layoutlib-common",
+        "layoutlib-common-delegates",
         "layoutlib-validator",
-        "sdk-common",
     ],
 
     jarjar_rules: "jarjar-rules.txt",
@@ -56,16 +56,17 @@
         "kxml2-2.3.0",
         "temp_layoutlib",
         "guava",
+        "ninepatch-prebuilt",
+        "sdk-common",
     ],
 
     static_libs: [
         "layoutlib_create",
         "layoutlib_api-prebuilt",
-        "tools-common-prebuilt",
-        "ninepatch-prebuilt",
         "layoutlib-common",
+        "layoutlib-common-delegates",
+        "layoutlib-native-delegates",
         "layoutlib-validator",
-        "sdk-common",
     ],
 
     jarjar_rules: "jarjar-rules.txt",
diff --git a/bridge/bridge.iml b/bridge/bridge.iml
index c9ce012..870615d 100644
--- a/bridge/bridge.iml
+++ b/bridge/bridge.iml
@@ -27,7 +27,7 @@
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
     <orderEntry type="library" name="layoutlib_api-prebuilt" level="project" />
-    <orderEntry type="module-library">
+    <orderEntry type="module-library" scope="TEST">
       <library name="tools-common-prebuilt">
         <ANNOTATIONS>
           <root url="file://$MODULE_DIR$/.." />
@@ -43,7 +43,7 @@
     </orderEntry>
     <orderEntry type="library" name="framework.jar" level="project" />
     <orderEntry type="library" name="guava" level="project" />
-    <orderEntry type="module-library" scope="TEST">
+    <orderEntry type="module-library">
       <library name="sdk-common">
         <CLASSES>
           <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/sdk-common/sdk-common.jar!/" />
@@ -70,6 +70,7 @@
       </library>
     </orderEntry>
     <orderEntry type="library" scope="TEST" name="hamcrest" level="project" />
+    <orderEntry type="module" module-name="delegates" />
     <orderEntry type="module-library">
       <library name="ninepatch-prebuilt">
         <CLASSES>
@@ -93,25 +94,5 @@
       </library>
     </orderEntry>
     <orderEntry type="module" module-name="validator" />
-    <orderEntry type="module-library" scope="TEST">
-      <library>
-        <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/atf/atf_classes.jar!/" />
-        </CLASSES>
-        <JAVADOC />
-        <SOURCES />
-      </library>
-    </orderEntry>
-    <orderEntry type="module-library">
-      <library>
-        <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/sdk-common/sdk-common.jar!/" />
-        </CLASSES>
-        <JAVADOC />
-        <SOURCES>
-          <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/sdk-common/sdk-common-sources.jar!/" />
-        </SOURCES>
-      </library>
-    </orderEntry>
   </component>
 </module>
\ No newline at end of file
diff --git a/bridge/resources/icons/shadow-b.png b/bridge/resources/icons/shadow-b.png
deleted file mode 100644
index 68f4f4b..0000000
--- a/bridge/resources/icons/shadow-b.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow-bl.png b/bridge/resources/icons/shadow-bl.png
deleted file mode 100644
index ee7dbe8..0000000
--- a/bridge/resources/icons/shadow-bl.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow-br.png b/bridge/resources/icons/shadow-br.png
deleted file mode 100644
index c45ad77..0000000
--- a/bridge/resources/icons/shadow-br.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow-l.png b/bridge/resources/icons/shadow-l.png
deleted file mode 100644
index 77d0bd0..0000000
--- a/bridge/resources/icons/shadow-l.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow-r.png b/bridge/resources/icons/shadow-r.png
deleted file mode 100644
index 4af7a33..0000000
--- a/bridge/resources/icons/shadow-r.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow-tl.png b/bridge/resources/icons/shadow-tl.png
deleted file mode 100644
index 424fb36..0000000
--- a/bridge/resources/icons/shadow-tl.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow-tr.png b/bridge/resources/icons/shadow-tr.png
deleted file mode 100644
index 1fd0c77..0000000
--- a/bridge/resources/icons/shadow-tr.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow2-b.png b/bridge/resources/icons/shadow2-b.png
deleted file mode 100644
index 963973e..0000000
--- a/bridge/resources/icons/shadow2-b.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow2-bl.png b/bridge/resources/icons/shadow2-bl.png
deleted file mode 100644
index 7612487..0000000
--- a/bridge/resources/icons/shadow2-bl.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow2-br.png b/bridge/resources/icons/shadow2-br.png
deleted file mode 100644
index 8e20252..0000000
--- a/bridge/resources/icons/shadow2-br.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow2-l.png b/bridge/resources/icons/shadow2-l.png
deleted file mode 100644
index 2db18a0..0000000
--- a/bridge/resources/icons/shadow2-l.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow2-r.png b/bridge/resources/icons/shadow2-r.png
deleted file mode 100644
index 8e026f1..0000000
--- a/bridge/resources/icons/shadow2-r.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow2-tl.png b/bridge/resources/icons/shadow2-tl.png
deleted file mode 100644
index a8045ed..0000000
--- a/bridge/resources/icons/shadow2-tl.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow2-tr.png b/bridge/resources/icons/shadow2-tr.png
deleted file mode 100644
index 590373c..0000000
--- a/bridge/resources/icons/shadow2-tr.png
+++ /dev/null
Binary files differ
diff --git a/bridge/src/android/animation/AnimationThread.java b/bridge/src/android/animation/AnimationThread.java
deleted file mode 100644
index ce2aec7..0000000
--- a/bridge/src/android/animation/AnimationThread.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.animation;
-
-import com.android.ide.common.rendering.api.IAnimationListener;
-import com.android.ide.common.rendering.api.RenderSession;
-import com.android.ide.common.rendering.api.Result;
-import com.android.ide.common.rendering.api.Result.Status;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.RenderSessionImpl;
-
-import android.os.Handler;
-import android.os.Handler_Delegate;
-import android.os.Message;
-
-import java.util.PriorityQueue;
-import java.util.Queue;
-
-/**
- * Abstract animation thread.
- * <p/>
- * This does not actually start an animation, instead it fakes a looper that will play whatever
- * animation is sending messages to its own {@link Handler}.
- * <p/>
- * Classes should implement {@link #preAnimation()} and {@link #postAnimation()}.
- * <p/>
- * If {@link #preAnimation()} does not start an animation somehow then the thread doesn't do
- * anything.
- *
- */
-public abstract class AnimationThread extends Thread {
-
-    private static class MessageBundle implements Comparable<MessageBundle> {
-        final Handler mTarget;
-        final Message mMessage;
-        final long mUptimeMillis;
-
-        MessageBundle(Handler target, Message message, long uptimeMillis) {
-            mTarget = target;
-            mMessage = message;
-            mUptimeMillis = uptimeMillis;
-        }
-
-        @Override
-        public int compareTo(MessageBundle bundle) {
-            if (mUptimeMillis < bundle.mUptimeMillis) {
-                return -1;
-            }
-            return 1;
-        }
-    }
-
-    private final RenderSessionImpl mSession;
-
-    private Queue<MessageBundle> mQueue = new PriorityQueue<MessageBundle>();
-    private final IAnimationListener mListener;
-
-    public AnimationThread(RenderSessionImpl scene, String threadName,
-            IAnimationListener listener) {
-        super(threadName);
-        mSession = scene;
-        mListener = listener;
-    }
-
-    public abstract Result preAnimation();
-    public abstract void postAnimation();
-
-    @Override
-    public void run() {
-        Bridge.prepareThread();
-        try {
-            /* FIXME: The ANIMATION_FRAME message no longer exists.  Instead, the
-             * animation timing loop is completely based on a Choreographer objects
-             * that schedules animation and drawing frames.  The animation handler is
-             * no longer even a handler; it is just a Runnable enqueued on the Choreographer.
-            Handler_Delegate.setCallback(new IHandlerCallback() {
-                @Override
-                public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
-                    if (msg.what == ValueAnimator.ANIMATION_START ||
-                            msg.what == ValueAnimator.ANIMATION_FRAME) {
-                        mQueue.add(new MessageBundle(handler, msg, uptimeMillis));
-                    } else {
-                        // just ignore.
-                    }
-                }
-            });
-            */
-
-            // call out to the pre-animation work, which should start an animation or more.
-            Result result = preAnimation();
-            if (result.isSuccess() == false) {
-                mListener.done(result);
-            }
-
-            // loop the animation
-            RenderSession session = mSession.getSession();
-            do {
-                // check early.
-                if (mListener.isCanceled()) {
-                    break;
-                }
-
-                // get the next message.
-                MessageBundle bundle = mQueue.poll();
-                if (bundle == null) {
-                    break;
-                }
-
-                // sleep enough for this bundle to be on time
-                long currentTime = System.currentTimeMillis();
-                if (currentTime < bundle.mUptimeMillis) {
-                    try {
-                        sleep(bundle.mUptimeMillis - currentTime);
-                    } catch (InterruptedException e) {
-                        // FIXME log/do something/sleep again?
-                        e.printStackTrace();
-                    }
-                }
-
-                // check after sleeping.
-                if (mListener.isCanceled()) {
-                    break;
-                }
-
-                // ready to do the work, acquire the scene.
-                result = mSession.acquire(250);
-                if (result.isSuccess() == false) {
-                    mListener.done(result);
-                    return;
-                }
-
-                // process the bundle. If the animation is not finished, this will enqueue
-                // the next message, so mQueue will have another one.
-                try {
-                    // check after acquiring in case it took a while.
-                    if (mListener.isCanceled()) {
-                        break;
-                    }
-
-                    bundle.mTarget.handleMessage(bundle.mMessage);
-                    if (mSession.render(false /*freshRender*/).isSuccess()) {
-                        mListener.onNewFrame(session);
-                    }
-                } finally {
-                    mSession.release();
-                }
-            } while (mListener.isCanceled() == false && mQueue.size() > 0);
-
-            mListener.done(Status.SUCCESS.createResult());
-
-        } catch (Throwable throwable) {
-            // can't use Bridge.getLog() as the exception might be thrown outside
-            // of an acquire/release block.
-            mListener.done(Status.ERROR_UNKNOWN.createResult("Error playing animation", throwable));
-
-        } finally {
-            postAnimation();
-            Handler_Delegate.setCallback(null);
-            Bridge.cleanupThread();
-        }
-    }
-}
diff --git a/bridge/src/android/animation/PropertyValuesHolder_Delegate.java b/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
deleted file mode 100644
index 556a063..0000000
--- a/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.animation;
-
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Delegate implementing the native methods of android.animation.PropertyValuesHolder
- *
- * Through the layoutlib_create tool, the original native methods of PropertyValuesHolder have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
- * around to map int to instance of the delegate.
- *
- * The main goal of this class' methods are to provide a native way to access setters and getters
- * on some object. We override these methods to use reflection since the original reflection
- * implementation of the PropertyValuesHolder won't be able to access protected methods.
- *
- */
-public class PropertyValuesHolder_Delegate {
-    // This code is copied from android.animation.PropertyValuesHolder and must be kept in sync
-    // We try several different types when searching for appropriate setter/getter functions.
-    // The caller may have supplied values in a type that does not match the setter/getter
-    // functions (such as the integers 0 and 1 to represent floating point values for alpha).
-    // Also, the use of generics in constructors means that we end up with the Object versions
-    // of primitive types (Float vs. float). But most likely, the setter/getter functions
-    // will take primitive types instead.
-    // So we supply an ordered array of other types to try before giving up.
-    private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class,
-            Double.class, Integer.class};
-    private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class,
-            Float.class, Double.class};
-
-    private static final Object sMethodIndexLock = new Object();
-    private static final Map<Long, Method> ID_TO_METHOD = new HashMap<Long, Method>();
-    private static final Map<String, Long> METHOD_NAME_TO_ID = new HashMap<String, Long>();
-    private static long sNextId = 1;
-
-    private static long registerMethod(Class<?> targetClass, String methodName, Class[] types,
-            int nArgs) {
-        // Encode the number of arguments in the method name
-        String methodIndexName = String.format("%1$s.%2$s#%3$d", targetClass.getSimpleName(),
-                methodName, nArgs);
-        synchronized (sMethodIndexLock) {
-            Long methodId = METHOD_NAME_TO_ID.get(methodIndexName);
-
-            if (methodId != null) {
-                // The method was already registered, check whether the class loader has changed
-                Method method = ID_TO_METHOD.get(methodId);
-                if (targetClass.equals(method.getDeclaringClass())) {
-                    return methodId;
-                }
-            }
-
-            Class[] args = new Class[nArgs];
-            Method method = null;
-            for (Class typeVariant : types) {
-                for (int i = 0; i < nArgs; i++) {
-                    args[i] = typeVariant;
-                }
-                try {
-                    method = targetClass.getDeclaredMethod(methodName, args);
-                } catch (NoSuchMethodException ignore) {
-                }
-            }
-
-            if (method != null) {
-                if (methodId == null) {
-                    methodId = sNextId++;
-                }
-                ID_TO_METHOD.put(methodId, method);
-                METHOD_NAME_TO_ID.put(methodIndexName, methodId);
-
-                return methodId;
-            }
-        }
-
-        // Method not found
-        return 0;
-    }
-
-    private static void callMethod(Object target, long methodID, Object... args) {
-        Method method = ID_TO_METHOD.get(methodID);
-        assert method != null;
-
-        try {
-            method.setAccessible(true);
-            method.invoke(target, args);
-        } catch (IllegalAccessException | InvocationTargetException e) {
-            Bridge.getLog().error(null, "Unable to update property during animation", e, null,
-                    null);
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nGetIntMethod(Class<?> targetClass, String methodName) {
-        return nGetMultipleIntMethod(targetClass, methodName, 1);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nGetFloatMethod(Class<?> targetClass, String methodName) {
-        return nGetMultipleFloatMethod(targetClass, methodName, 1);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nGetMultipleIntMethod(Class<?> targetClass, String methodName,
-            int numParams) {
-        return registerMethod(targetClass, methodName, INTEGER_VARIANTS, numParams);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nGetMultipleFloatMethod(Class<?> targetClass, String methodName,
-            int numParams) {
-        return registerMethod(targetClass, methodName, FLOAT_VARIANTS, numParams);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nCallIntMethod(Object target, long methodID, int arg) {
-        callMethod(target, methodID, arg);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nCallFloatMethod(Object target, long methodID, float arg) {
-        callMethod(target, methodID, arg);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nCallTwoIntMethod(Object target, long methodID, int arg1,
-            int arg2) {
-        callMethod(target, methodID, arg1, arg2);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nCallFourIntMethod(Object target, long methodID, int arg1,
-            int arg2, int arg3, int arg4) {
-        callMethod(target, methodID, arg1, arg2, arg3, arg4);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nCallMultipleIntMethod(Object target, long methodID,
-            int[] args) {
-        assert args != null;
-
-        // Box parameters
-        Object[] params = new Object[args.length];
-        for (int i = 0; i < args.length; i++) {
-            params[i] = args;
-        }
-        callMethod(target, methodID, params);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nCallTwoFloatMethod(Object target, long methodID, float arg1,
-            float arg2) {
-        callMethod(target, methodID, arg1, arg2);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nCallFourFloatMethod(Object target, long methodID, float arg1,
-            float arg2, float arg3, float arg4) {
-        callMethod(target, methodID, arg1, arg2, arg3, arg4);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nCallMultipleFloatMethod(Object target, long methodID,
-            float[] args) {
-        assert args != null;
-
-        // Box parameters
-        Object[] params = new Object[args.length];
-        for (int i = 0; i < args.length; i++) {
-            params[i] = args;
-        }
-        callMethod(target, methodID, params);
-    }
-
-    public static void clearCaches() {
-        ID_TO_METHOD.clear();
-        METHOD_NAME_TO_ID.clear();
-    }
-}
diff --git a/bridge/src/android/app/ActivityThread_Delegate.java b/bridge/src/android/app/ActivityThread_Delegate.java
new file mode 100644
index 0000000..da81134
--- /dev/null
+++ b/bridge/src/android/app/ActivityThread_Delegate.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import com.android.layoutlib.bridge.impl.RenderAction;
+
+import android.content.Context;
+
+public class ActivityThread_Delegate {
+    public static Context getSystemUiContext() {
+        return RenderAction.getCurrentContext();
+    }
+}
diff --git a/bridge/src/android/content/res/AssetManager_Delegate.java b/bridge/src/android/content/res/AssetManager_Delegate.java
index c27df09..1b26834 100644
--- a/bridge/src/android/content/res/AssetManager_Delegate.java
+++ b/bridge/src/android/content/res/AssetManager_Delegate.java
@@ -21,6 +21,8 @@
 
 import android.util.SparseArray;
 
+import libcore.util.NativeAllocationRegistry_Delegate;
+
 /**
  * Delegate used to provide implementation of a select few native methods of {@link AssetManager}
  * <p/>
@@ -34,6 +36,7 @@
 
     private static final DelegateManager<AssetManager_Delegate> sManager =
             new DelegateManager<>(AssetManager_Delegate.class);
+    private static long sFinalizer = -1;
 
     // ---- delegate methods. ----
 
@@ -55,8 +58,14 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nativeThemeDestroy(long theme) {
-        Resources_Theme_Delegate.getDelegateManager().removeJavaReferenceFor(theme);
+    /*package*/ static long nativeGetThemeFreeFunction() {
+        synchronized (AssetManager_Delegate.class) {
+            if (sFinalizer == -1) {
+                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
+                        Resources_Theme_Delegate.getDelegateManager()::removeJavaReferenceFor);
+            }
+        }
+        return sFinalizer;
     }
 
     @LayoutlibDelegate
@@ -71,8 +80,6 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid() {
-        // AssetManager requires this not to be null
-        return new String[0];
-    }
+    /*package*/ static void createSystemAssetsInZygoteLocked(boolean reinitialize,
+            String frameworkPath) { }
 }
diff --git a/bridge/src/android/content/res/BridgeTypedArray.java b/bridge/src/android/content/res/BridgeTypedArray.java
index eb603f0..2935c15 100644
--- a/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/bridge/src/android/content/res/BridgeTypedArray.java
@@ -40,7 +40,6 @@
 import android.graphics.Typeface;
 import android.graphics.Typeface_Accessor;
 import android.graphics.drawable.Drawable;
-import android.text.Html;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.LayoutInflater_Delegate;
@@ -50,7 +49,6 @@
 import java.util.Arrays;
 import java.util.Map;
 
-import static android.text.Html.FROM_HTML_MODE_COMPACT;
 import static android.util.TypedValue.TYPE_ATTRIBUTE;
 import static android.util.TypedValue.TYPE_DIMENSION;
 import static android.util.TypedValue.TYPE_FLOAT;
@@ -64,8 +62,8 @@
 import static android.util.TypedValue.TYPE_NULL;
 import static android.util.TypedValue.TYPE_REFERENCE;
 import static android.util.TypedValue.TYPE_STRING;
-import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
-import static com.android.SdkConstants.PREFIX_THEME_REF;
+import static com.android.ide.common.rendering.api.AndroidConstants.PREFIX_RESOURCE_REF;
+import static com.android.ide.common.rendering.api.AndroidConstants.PREFIX_THEME_REF;
 import static com.android.ide.common.rendering.api.RenderResources.REFERENCE_EMPTY;
 import static com.android.ide.common.rendering.api.RenderResources.REFERENCE_NULL;
 import static com.android.ide.common.rendering.api.RenderResources.REFERENCE_UNDEFINED;
@@ -215,7 +213,7 @@
                     ValueXmlHelper.unescapeResourceString(resourceValue.getRawXmlValue(),
                             true, false);
             if (rawValue != null && !rawValue.equals(value)) {
-                return Html.fromHtml(rawValue, FROM_HTML_MODE_COMPACT);
+                return ResourceHelper.parseHtml(rawValue);
             }
         }
         return value;
@@ -270,10 +268,15 @@
         try {
             return convertValueToInt(s, defValue);
         } catch (NumberFormatException e) {
-            Bridge.getLog().warning(ILayoutLog.TAG_RESOURCES_FORMAT,
-                    String.format("\"%1$s\" in attribute \"%2$s\" is not a valid integer",
-                            s, mNames[index]),
-                    null, null);
+            // If s starts with ?, it means that it is a theme attribute that wasn't defined.
+            // That is an allowed behaviour, and the expected result is to return the default
+            // value.
+            // If we are in this case, we do not want to log a warning.
+            if (s == null || !s.startsWith(PREFIX_THEME_REF)) {
+                Bridge.getLog().warning(ILayoutLog.TAG_RESOURCES_FORMAT,
+                        String.format("\"%1$s\" in attribute \"%2$s\" is not a valid integer", s,
+                                mNames[index]), null, null);
+            }
         }
         return defValue;
     }
@@ -985,6 +988,9 @@
         if (value == null) {
             return TYPE_NULL;
         }
+        if (value.isEmpty()) {
+            return TYPE_STRING;
+        }
         if (value.startsWith(PREFIX_RESOURCE_REF)) {
             return TYPE_REFERENCE;
         }
diff --git a/bridge/src/android/content/res/Resources_Delegate.java b/bridge/src/android/content/res/Resources_Delegate.java
index 2689acd..f453aec 100644
--- a/bridge/src/android/content/res/Resources_Delegate.java
+++ b/bridge/src/android/content/res/Resources_Delegate.java
@@ -16,7 +16,6 @@
 
 package android.content.res;
 
-import com.android.SdkConstants;
 import com.android.ide.common.rendering.api.ArrayResourceValue;
 import com.android.ide.common.rendering.api.AssetRepository;
 import com.android.ide.common.rendering.api.DensityBasedResourceValue;
@@ -25,7 +24,6 @@
 import com.android.ide.common.rendering.api.PluralsResourceValue;
 import com.android.ide.common.rendering.api.RenderResources;
 import com.android.ide.common.rendering.api.ResourceNamespace;
-import com.android.ide.common.rendering.api.ResourceNamespace.Resolver;
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.ResourceValueImpl;
@@ -37,12 +35,10 @@
 import com.android.layoutlib.bridge.impl.ParserFactory;
 import com.android.layoutlib.bridge.impl.ResourceHelper;
 import com.android.layoutlib.bridge.util.NinePatchInputStream;
-import com.android.ninepatch.NinePatch;
 import com.android.resources.ResourceType;
 import com.android.resources.ResourceUrl;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 import com.android.tools.layoutlib.annotations.VisibleForTesting;
-import com.android.utils.Pair;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -58,6 +54,7 @@
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.LruCache;
+import android.util.Pair;
 import android.util.TypedValue;
 import android.view.DisplayAdjustments;
 import android.view.ViewGroup.LayoutParams;
@@ -68,8 +65,9 @@
 import java.util.WeakHashMap;
 
 import static android.content.res.AssetManager.ACCESS_STREAMING;
-import static com.android.SdkConstants.ANDROID_PKG;
-import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
+import static com.android.ide.common.rendering.api.AndroidConstants.ANDROID_PKG;
+import static com.android.ide.common.rendering.api.AndroidConstants.APP_PREFIX;
+import static com.android.ide.common.rendering.api.AndroidConstants.PREFIX_RESOURCE_REF;
 
 public class Resources_Delegate {
     private static WeakHashMap<Resources, LayoutlibCallback> sLayoutlibCallbacks =
@@ -160,7 +158,7 @@
                 value = new ResourceValueImpl(resourceInfo.getNamespace(),
                         resourceInfo.getResourceType(), attributeName, attributeName);
             }
-            return Pair.of(attributeName, value);
+            return Pair.create(attributeName, value);
         }
 
         return null;
@@ -175,7 +173,7 @@
     static Drawable getDrawable(Resources resources, int id, Theme theme) {
         Pair<String, ResourceValue> value = getResourceValue(resources, id);
         if (value != null) {
-            String key = value.getSecond().getValue();
+            String key = value.second.getValue();
 
             Drawable.ConstantState constantState = key != null ? sDrawableCache.get(key) : null;
             Drawable drawable;
@@ -183,10 +181,18 @@
                 drawable = constantState.newDrawable(resources, theme);
             } else {
                 drawable =
-                        ResourceHelper.getDrawable(value.getSecond(), getContext(resources), theme);
+                        ResourceHelper.getDrawable(value.second, getContext(resources), theme);
+
+                if (drawable == null) {
+                    throwException(resources, id);
+                    return null;
+                }
 
                 if (key != null) {
-                    sDrawableCache.put(key, drawable.getConstantState());
+                    Drawable.ConstantState state = drawable.getConstantState();
+                    if (state != null) {
+                        sDrawableCache.put(key, state);
+                    }
                 }
             }
 
@@ -210,7 +216,7 @@
         Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
-            ResourceValue resourceValue = value.getSecond();
+            ResourceValue resourceValue = value.second;
             try {
                 return ResourceHelper.getColor(resourceValue.getValue());
             } catch (NumberFormatException e) {
@@ -247,7 +253,7 @@
         Pair<String, ResourceValue> resValue = getResourceValue(resources, id);
 
         if (resValue != null) {
-            ColorStateList stateList = ResourceHelper.getColorStateList(resValue.getSecond(),
+            ColorStateList stateList = ResourceHelper.getColorStateList(resValue.second,
                     getContext(resources), theme);
             if (stateList != null) {
                 return stateList;
@@ -266,7 +272,7 @@
         Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
-            ResourceValue resValue = value.getSecond();
+            ResourceValue resValue = value.second;
 
             assert resValue != null;
             if (resValue != null) {
@@ -285,7 +291,7 @@
         Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
-            ResourceValue resValue = value.getSecond();
+            ResourceValue resValue = value.second;
 
             assert resValue != null;
             if (resValue != null) {
@@ -425,7 +431,7 @@
         Pair<String, ResourceValue> v = getResourceValue(resources, id);
 
         if (v != null) {
-            ResourceValue resValue = v.getSecond();
+            ResourceValue resValue = v.second;
 
             assert resValue != null;
             if (resValue != null) {
@@ -478,7 +484,7 @@
         Pair<String, ResourceValue> v = getResourceValue(resources, id);
 
         if (v != null) {
-            ResourceValue value = v.getSecond();
+            ResourceValue value = v.second;
 
             try {
                 BridgeXmlBlockParser parser =
@@ -505,7 +511,7 @@
         Pair<String, ResourceValue> v = getResourceValue(resources, id);
 
         if (v != null) {
-            ResourceValue value = v.getSecond();
+            ResourceValue value = v.second;
 
             try {
                 return ResourceHelper.getXmlBlockParser(getContext(resources), value);
@@ -525,7 +531,14 @@
 
     @LayoutlibDelegate
     static TypedArray obtainAttributes(Resources resources, AttributeSet set, int[] attrs) {
-        return getContext(resources).obtainStyledAttributes(set, attrs);
+        BridgeContext context = getContext(resources);
+        RenderResources renderResources = context.getRenderResources();
+        // Remove all themes, including default, to ensure theme attributes are not resolved
+        renderResources.getAllThemes().clear();
+        BridgeTypedArray ta = context.internalObtainStyledAttributes(set, attrs, 0, 0);
+        // Reset styles to only the default if present
+        renderResources.clearStyles();
+        return ta;
     }
 
     @LayoutlibDelegate
@@ -573,7 +586,7 @@
         Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
-            ResourceValue resValue = value.getSecond();
+            ResourceValue resValue = value.second;
 
             assert resValue != null;
             if (resValue != null) {
@@ -587,7 +600,7 @@
                     }
                     TypedValue tmpValue = new TypedValue();
                     if (ResourceHelper.parseFloatAttribute(
-                            value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+                            value.first, v, tmpValue, true /*requireUnit*/) &&
                             tmpValue.type == TypedValue.TYPE_DIMENSION) {
                         return tmpValue.getDimension(resources.getDisplayMetrics());
                     }
@@ -607,7 +620,7 @@
         Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
-            ResourceValue resValue = value.getSecond();
+            ResourceValue resValue = value.second;
 
             assert resValue != null;
             if (resValue != null) {
@@ -615,7 +628,7 @@
                 if (v != null) {
                     TypedValue tmpValue = new TypedValue();
                     if (ResourceHelper.parseFloatAttribute(
-                            value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+                            value.first, v, tmpValue, true /*requireUnit*/) &&
                             tmpValue.type == TypedValue.TYPE_DIMENSION) {
                         return TypedValue.complexToDimensionPixelOffset(tmpValue.data,
                                 resources.getDisplayMetrics());
@@ -636,7 +649,7 @@
         Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
-            ResourceValue resValue = value.getSecond();
+            ResourceValue resValue = value.second;
 
             assert resValue != null;
             if (resValue != null) {
@@ -644,7 +657,7 @@
                 if (v != null) {
                     TypedValue tmpValue = new TypedValue();
                     if (ResourceHelper.parseFloatAttribute(
-                            value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+                            value.first, v, tmpValue, true /*requireUnit*/) &&
                             tmpValue.type == TypedValue.TYPE_DIMENSION) {
                         return TypedValue.complexToDimensionPixelSize(tmpValue.data,
                                 resources.getDisplayMetrics());
@@ -665,7 +678,7 @@
         Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
-            ResourceValue resValue = value.getSecond();
+            ResourceValue resValue = value.second;
 
             assert resValue != null;
             if (resValue != null) {
@@ -692,7 +705,7 @@
         Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
-            ResourceValue resValue = value.getSecond();
+            ResourceValue resValue = value.second;
 
             if (resValue != null) {
                 String v = resValue.getValue();
@@ -712,7 +725,7 @@
         Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
-            ResourceValue resValue = value.getSecond();
+            ResourceValue resValue = value.second;
 
             if (resValue != null) {
                 String v = resValue.getValue();
@@ -774,9 +787,9 @@
     private static String getPackageName(ResourceReference resourceInfo, Resources resources) {
         String packageName = resourceInfo.getNamespace().getPackageName();
         if (packageName == null) {
-            packageName = getContext(resources).getPackageName();
+            packageName = getLayoutlibCallback(resources).getResourcePackage();
             if (packageName == null) {
-                packageName = SdkConstants.APP_PREFIX;
+                packageName = APP_PREFIX;
             }
         }
         return packageName;
@@ -802,8 +815,8 @@
     static String getString(Resources resources, int id) throws NotFoundException {
         Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
-        if (value != null && value.getSecond().getValue() != null) {
-            return value.getSecond().getValue();
+        if (value != null && value.second.getValue() != null) {
+            return value.second.getValue();
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
@@ -819,8 +832,8 @@
         Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
-            if (value.getSecond() instanceof PluralsResourceValue) {
-                PluralsResourceValue pluralsResourceValue = (PluralsResourceValue) value.getSecond();
+            if (value.second instanceof PluralsResourceValue) {
+                PluralsResourceValue pluralsResourceValue = (PluralsResourceValue) value.second;
                 PluralRules pluralRules = PluralRules.forLocale(resources.getConfiguration().getLocales()
                         .get(0));
                 String strValue = pluralsResourceValue.getValue(pluralRules.select(quantity));
@@ -831,7 +844,7 @@
                 return strValue;
             }
             else {
-                return value.getSecond().getValue();
+                return value.second.getValue();
             }
         }
 
@@ -860,7 +873,7 @@
             NotFoundException {
         Pair<String, ResourceValue> value = getResourceValue(resources, id);
         if (value != null) {
-            return ResourceHelper.getFont(value.getSecond(), getContext(resources), null);
+            return ResourceHelper.getFont(value.second, getContext(resources), null);
         }
 
         throwException(resources, id);
@@ -892,11 +905,11 @@
         Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
-            ResourceValue resVal = value.getSecond();
+            ResourceValue resVal = value.second;
             String v = resVal != null ? resVal.getValue() : null;
 
             if (v != null) {
-                if (ResourceHelper.parseFloatAttribute(value.getFirst(), v, outValue,
+                if (ResourceHelper.parseFloatAttribute(value.first, v, outValue,
                         false /*requireUnit*/)) {
                     return resVal;
                 }
@@ -940,7 +953,7 @@
         Pair<String, ResourceValue> v = getResourceValue(resources, id);
 
         if (v != null) {
-            ResourceValue value = v.getSecond();
+            ResourceValue value = v.second;
 
             try {
                 return ResourceHelper.getXmlBlockParser(getContext(resources), value);
@@ -973,8 +986,8 @@
         Pair<String, ResourceValue> result = getResourceValue(resources, id);
 
         ResourceNamespace layoutNamespace;
-        if (result != null && result.getSecond() != null) {
-            layoutNamespace = result.getSecond().getNamespace();
+        if (result != null && result.second != null) {
+            layoutNamespace = result.second.getNamespace();
         } else {
             // We need to pick something, even though the resource system never heard about a layout
             // with this numeric id.
@@ -996,7 +1009,7 @@
         Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
-            String path = value.getSecond().getValue();
+            String path = value.second.getValue();
             if (path != null) {
                 return openRawResource(resources, path);
             }
@@ -1026,11 +1039,8 @@
             if (stream == null) {
                 throw new NotFoundException(path);
             }
-            // If it's a nine-patch return a custom input stream so that
-            // other methods (mainly bitmap factory) can detect it's a 9-patch
-            // and actually load it as a 9-patch instead of a normal bitmap.
-            if (path.toLowerCase().endsWith(NinePatch.EXTENSION_9PATCH)) {
-                return new NinePatchInputStream(stream);
+            if (path.endsWith(".9.png")) {
+                stream = new NinePatchInputStream(stream, path);
             }
             return stream;
         } catch (IOException e) {
@@ -1104,7 +1114,7 @@
                 return Bridge.getResourceId(url.type, url.name);
             }
 
-            if (getContext(resources).getPackageName().equals(url.namespace)) {
+            if (getLayoutlibCallback(resources).getResourcePackage().equals(url.namespace)) {
                 return getLayoutlibCallback(resources).getOrGenerateResourceId(
                         new ResourceReference(ResourceNamespace.RES_AUTO, url.type, url.name));
             }
diff --git a/bridge/src/android/graphics/BaseCanvas_Delegate.java b/bridge/src/android/graphics/BaseCanvas_Delegate.java
deleted file mode 100644
index 5a9236c..0000000
--- a/bridge/src/android/graphics/BaseCanvas_Delegate.java
+++ /dev/null
@@ -1,815 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.GcSnapshot;
-import com.android.layoutlib.bridge.impl.PorterDuffUtility;
-import com.android.ninepatch.NinePatchChunk;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.Nullable;
-import android.text.TextUtils;
-import android.util.imagepool.ImagePool;
-import android.util.imagepool.ImagePoolProvider;
-
-import java.awt.Composite;
-import java.awt.Graphics2D;
-import java.awt.PaintContext;
-import java.awt.RenderingHints;
-import java.awt.Shape;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Arc2D;
-import java.awt.geom.Area;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
-import java.awt.image.ColorModel;
-import java.awt.image.DataBuffer;
-
-public class BaseCanvas_Delegate {
-    // ---- delegate manager ----
-    protected static DelegateManager<BaseCanvas_Delegate> sManager =
-            new DelegateManager<>(BaseCanvas_Delegate.class);
-
-    // ---- delegate helper data ----
-    private final static boolean[] sBoolOut = new boolean[1];
-
-
-    // ---- delegate data ----
-    protected Bitmap_Delegate mBitmap;
-    protected GcSnapshot mSnapshot;
-
-    // ---- Public Helper methods ----
-
-    protected BaseCanvas_Delegate(Bitmap_Delegate bitmap) {
-        mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
-    }
-
-    protected BaseCanvas_Delegate() {
-        mSnapshot = GcSnapshot.createDefaultSnapshot(null /*image*/);
-    }
-
-    /**
-     * Disposes of the {@link Graphics2D} stack.
-     */
-    protected void dispose() {
-        mSnapshot.dispose();
-    }
-
-    /**
-     * Returns the current {@link Graphics2D} used to draw.
-     */
-    public GcSnapshot getSnapshot() {
-        return mSnapshot;
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawBitmap(long nativeCanvas, long bitmapHandle, float left, float top,
-            long nativePaintOrZero, int canvasDensity, int screenDensity, int bitmapDensity) {
-        // get the delegate from the native int.
-        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle);
-        if (bitmapDelegate == null) {
-            return;
-        }
-
-        BufferedImage image = bitmapDelegate.getImage();
-        float right = left + image.getWidth();
-        float bottom = top + image.getHeight();
-
-        drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
-                0, 0, image.getWidth(), image.getHeight(),
-                (int)left, (int)top, (int)right, (int)bottom);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawBitmap(long nativeCanvas, long bitmapHandle, float srcLeft,
-            float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop,
-            float dstRight, float dstBottom, long nativePaintOrZero, int screenDensity,
-            int bitmapDensity) {
-        // get the delegate from the native int.
-        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle);
-        if (bitmapDelegate == null) {
-            return;
-        }
-
-        drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, (int) srcLeft, (int) srcTop,
-                (int) srcRight, (int) srcBottom, (int) dstLeft, (int) dstTop, (int) dstRight,
-                (int) dstBottom);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawBitmap(long nativeCanvas, int[] colors, int offset, int stride,
-            final float x, final float y, int width, int height, boolean hasAlpha,
-            long nativePaintOrZero) {
-        // create a temp BufferedImage containing the content.
-        final ImagePool.Image image = ImagePoolProvider.get().acquire(width, height,
-                hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
-        image.setRGB(0, 0, width, height, colors, offset, stride);
-
-        draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/,
-                (graphics, paint) -> {
-                    if (paint != null && paint.isFilterBitmap()) {
-                        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
-                                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-                    }
-
-                    image.drawImage(graphics, (int) x, (int) y, null);
-                });
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawColor(long nativeCanvas, final int color, final int mode) {
-        // get the delegate from the native int.
-        BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        final int w = canvasDelegate.mBitmap.getImage().getWidth();
-        final int h = canvasDelegate.mBitmap.getImage().getHeight();
-        draw(nativeCanvas, (graphics, paint) -> {
-            // reset its transform just in case
-            graphics.setTransform(new AffineTransform());
-
-            // set the color
-            graphics.setColor(new java.awt.Color(color, true /*alpha*/));
-
-            Composite composite = PorterDuffUtility.getComposite(
-                    PorterDuffUtility.getPorterDuffMode(mode), 0xFF);
-            if (composite != null) {
-                graphics.setComposite(composite);
-            }
-
-            graphics.fillRect(0, 0, w, h);
-        });
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawColor(long nativeCanvas, long nativeColorSpace, long color,
-            int mode) {
-        nDrawColor(nativeCanvas, Color.toArgb(color), mode);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawPaint(long nativeCanvas, long paint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawPaint is not supported.", null,null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawPoint(long nativeCanvas, float x, float y,
-            long nativePaint) {
-        // TODO: need to support the attribute (e.g. stroke width) of paint
-        draw(nativeCanvas, nativePaint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                (graphics, paintDelegate) -> graphics.fillRect((int)x, (int)y, 1, 1));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawPoints(long nativeCanvas, float[] pts, int offset, int count,
-            long nativePaint) {
-        if (offset < 0 || count < 0 || offset + count > pts.length) {
-            throw new IllegalArgumentException("Invalid argument set");
-        }
-        // ignore the last point if the count is odd (It means it is not paired).
-        count = (count >> 1) << 1;
-        for (int i = offset; i < offset + count; i += 2) {
-            nDrawPoint(nativeCanvas, pts[i], pts[i + 1], nativePaint);
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawLine(long nativeCanvas,
-            final float startX, final float startY, final float stopX, final float stopY,
-            long paint) {
-        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                (graphics, paintDelegate) -> graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawLines(long nativeCanvas,
-            final float[] pts, final int offset, final int count,
-            long nativePaint) {
-        draw(nativeCanvas, nativePaint, false /*compositeOnly*/,
-                false /*forceSrcMode*/, (graphics, paintDelegate) -> {
-                    for (int i = 0; i < count; i += 4) {
-                        graphics.drawLine((int) pts[i + offset], (int) pts[i + offset + 1],
-                                (int) pts[i + offset + 2], (int) pts[i + offset + 3]);
-                    }
-                });
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawRect(long nativeCanvas,
-            final float left, final float top, final float right, final float bottom, long paint) {
-
-        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                (graphics, paintDelegate) -> {
-                    int style = paintDelegate.getStyle();
-
-                    // draw
-                    if (style == Paint.Style.FILL.nativeInt ||
-                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                        graphics.fillRect((int)left, (int)top,
-                                (int)(right-left), (int)(bottom-top));
-                    }
-
-                    if (style == Paint.Style.STROKE.nativeInt ||
-                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                        graphics.drawRect((int)left, (int)top,
-                                (int)(right-left), (int)(bottom-top));
-                    }
-                });
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawOval(long nativeCanvas, final float left,
-            final float top, final float right, final float bottom, long paint) {
-        if (right > left && bottom > top) {
-            draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                    (graphics, paintDelegate) -> {
-                        int style = paintDelegate.getStyle();
-
-                        // draw
-                        if (style == Paint.Style.FILL.nativeInt ||
-                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                            graphics.fillOval((int)left, (int)top,
-                                    (int)(right - left), (int)(bottom - top));
-                        }
-
-                        if (style == Paint.Style.STROKE.nativeInt ||
-                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                            graphics.drawOval((int)left, (int)top,
-                                    (int)(right - left), (int)(bottom - top));
-                        }
-                    });
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawCircle(long nativeCanvas,
-            float cx, float cy, float radius, long paint) {
-        nDrawOval(nativeCanvas,
-                cx - radius, cy - radius, cx + radius, cy + radius,
-                paint);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawArc(long nativeCanvas,
-            final float left, final float top, final float right, final float bottom,
-            final float startAngle, final float sweep,
-            final boolean useCenter, long paint) {
-        if (right > left && bottom > top) {
-            draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                    (graphics, paintDelegate) -> {
-                        int style = paintDelegate.getStyle();
-
-                        Arc2D.Float arc = new Arc2D.Float(
-                                left, top, right - left, bottom - top,
-                                -startAngle, -sweep,
-                                useCenter ? Arc2D.PIE : Arc2D.OPEN);
-
-                        // draw
-                        if (style == Paint.Style.FILL.nativeInt ||
-                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                            graphics.fill(arc);
-                        }
-
-                        if (style == Paint.Style.STROKE.nativeInt ||
-                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                            graphics.draw(arc);
-                        }
-                    });
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawRoundRect(long nativeCanvas,
-            final float left, final float top, final float right, final float bottom,
-            final float rx, final float ry, long paint) {
-        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                (graphics, paintDelegate) -> {
-                    int style = paintDelegate.getStyle();
-
-                    // draw
-                    if (style == Paint.Style.FILL.nativeInt ||
-                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                        graphics.fillRoundRect(
-                                (int)left, (int)top,
-                                (int)(right - left), (int)(bottom - top),
-                                2 * (int)rx, 2 * (int)ry);
-                    }
-
-                    if (style == Paint.Style.STROKE.nativeInt ||
-                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                        graphics.drawRoundRect(
-                                (int)left, (int)top,
-                                (int)(right - left), (int)(bottom - top),
-                                2 * (int)rx, 2 * (int)ry);
-                    }
-                });
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
-            float outerTop, float outerRight, float outerBottom, float outerRx, float outerRy,
-            float innerLeft, float innerTop, float innerRight, float innerBottom, float innerRx,
-            float innerRy, long nativePaint) {
-        nDrawDoubleRoundRect(nativeCanvas, outerLeft, outerTop, outerRight, outerBottom,
-                new float[]{outerRx, outerRy, outerRx, outerRy, outerRx, outerRy, outerRx, outerRy},
-                innerLeft, innerTop, innerRight, innerBottom,
-                new float[]{innerRx, innerRy, innerRx, innerRy, innerRx, innerRy, innerRx, innerRy},
-                nativePaint);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
-            float outerTop, float outerRight, float outerBottom, float[] outerRadii,
-            float innerLeft, float innerTop, float innerRight, float innerBottom,
-            float[] innerRadii, long nativePaint) {
-        draw(nativeCanvas, nativePaint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                (graphics, paintDelegate) -> {
-                    RoundRectangle innerRect = new RoundRectangle(innerLeft, innerTop,
-                            innerRight - innerLeft, innerBottom - innerTop, innerRadii);
-                    RoundRectangle outerRect = new RoundRectangle(outerLeft, outerTop,
-                            outerRight - outerLeft, outerBottom - outerTop, outerRadii);
-
-                    int style = paintDelegate.getStyle();
-
-                    // draw
-                    if (style == Paint.Style.STROKE.nativeInt ||
-                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                        graphics.draw(innerRect);
-                        graphics.draw(outerRect);
-                    }
-
-                    if (style == Paint.Style.FILL.nativeInt ||
-                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                        Area outerArea = new Area(outerRect);
-                        Area innerArea = new Area(innerRect);
-                        outerArea.subtract(innerArea);
-                        graphics.fill(outerArea);
-                    }
-                });
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawPath(long nativeCanvas, long path, long paint) {
-        final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                (graphics, paintDelegate) -> {
-                    Shape shape = pathDelegate.getJavaShape();
-                    Rectangle2D bounds = shape.getBounds2D();
-                    if (bounds.isEmpty()) {
-                        // Apple JRE 1.6 doesn't like drawing empty shapes.
-                        // http://b.android.com/178278
-
-                        if (pathDelegate.isEmpty()) {
-                            // This means that the path doesn't have any lines or curves so
-                            // nothing to draw.
-                            return;
-                        }
-
-                        // The stroke width is not consider for the size of the bounds so,
-                        // for example, a horizontal line, would be considered as an empty
-                        // rectangle.
-                        // If the strokeWidth is not 0, we use it to consider the size of the
-                        // path as well.
-                        float strokeWidth = paintDelegate.getStrokeWidth();
-                        if (strokeWidth <= 0.0f) {
-                            return;
-                        }
-                        bounds.setRect(bounds.getX(), bounds.getY(),
-                                Math.max(strokeWidth, bounds.getWidth()),
-                                Math.max(strokeWidth, bounds.getHeight()));
-                    }
-
-                    int style = paintDelegate.getStyle();
-
-                    if (style == Paint.Style.FILL.nativeInt ||
-                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                        graphics.fill(shape);
-                    }
-
-                    if (style == Paint.Style.STROKE.nativeInt ||
-                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                        graphics.draw(shape);
-                    }
-                });
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawRegion(long nativeCanvas, long nativeRegion,
-            long nativePaint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Some canvas paths may not be drawn", null, null, null);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawNinePatch(long nativeCanvas, long nativeBitmap, long ninePatch,
-            final float dstLeft, final float dstTop, final float dstRight, final float dstBottom,
-            long nativePaintOrZero, final int screenDensity, final int bitmapDensity) {
-
-        // get the delegate from the native int.
-        final Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmap);
-        if (bitmapDelegate == null) {
-            return;
-        }
-
-        byte[] c = NinePatch_Delegate.getChunk(ninePatch);
-        if (c == null) {
-            // not a 9-patch?
-            BufferedImage image = bitmapDelegate.getImage();
-            drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, 0, 0, image.getWidth(),
-                    image.getHeight(), (int) dstLeft, (int) dstTop, (int) dstRight,
-                    (int) dstBottom);
-            return;
-        }
-
-        final NinePatchChunk chunkObject = NinePatch_Delegate.getChunk(c);
-        if (chunkObject == null) {
-            return;
-        }
-
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        // this one can be null
-        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
-
-        canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
-            @Override
-            public void draw(Graphics2D graphics, Paint_Delegate paint) {
-                chunkObject.draw(bitmapDelegate.getImage(), graphics, (int) dstLeft, (int) dstTop,
-                        (int) (dstRight - dstLeft), (int) (dstBottom - dstTop), screenDensity,
-                        bitmapDensity);
-            }
-        }, paintDelegate, true, false);
-
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawBitmapMatrix(long nCanvas, long bitmapHandle,
-            long nMatrix, long nPaint) {
-        // get the delegate from the native int.
-        BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        // get the delegate from the native int, which can be null
-        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
-
-        // get the delegate from the native int.
-        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle);
-        if (bitmapDelegate == null) {
-            return;
-        }
-
-        final BufferedImage image = getImageToDraw(bitmapDelegate, paintDelegate, sBoolOut);
-
-        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
-        if (matrixDelegate == null) {
-            return;
-        }
-
-        final AffineTransform mtx = matrixDelegate.getAffineTransform();
-
-        canvasDelegate.getSnapshot().draw((graphics, paint) -> {
-            if (paint != null && paint.isFilterBitmap()) {
-                graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
-                        RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-            }
-
-            //FIXME add support for canvas, screen and bitmap densities.
-            graphics.drawImage(image, mtx, null);
-        }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawBitmapMesh(long nCanvas, long bitmapHandle,
-            int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors,
-            int colorOffset, long nPaint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawBitmapMesh is not supported.", null, null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawVertices(long nCanvas, int mode, int n,
-            float[] verts, int vertOffset,
-            float[] texs, int texOffset,
-            int[] colors, int colorOffset,
-            short[] indices, int indexOffset,
-            int indexCount, long nPaint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawVertices is not supported.", null, null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawText(long nativeCanvas, char[] text, int index, int count,
-            float startX, float startY, int flags, long paint) {
-        drawText(nativeCanvas, text, index, count, startX, startY, flags,
-                paint);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawText(long nativeCanvas, String text,
-            int start, int end, float x, float y, final int flags, long paint) {
-        int count = end - start;
-        char[] buffer = TemporaryBuffer.obtain(count);
-        TextUtils.getChars(text, start, end, buffer, 0);
-
-        nDrawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawTextRun(long nativeCanvas, String text,
-            int start, int end, int contextStart, int contextEnd,
-            float x, float y, boolean isRtl, long paint) {
-        int count = end - start;
-        char[] buffer = TemporaryBuffer.obtain(count);
-        TextUtils.getChars(text, start, end, buffer, 0);
-
-        drawText(nativeCanvas, buffer, 0, count, x, y, isRtl ? Paint.BIDI_RTL : Paint.BIDI_LTR,
-                paint);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawTextRun(long nativeCanvas, char[] text,
-            int start, int count, int contextStart, int contextCount,
-            float x, float y, boolean isRtl, long paint,
-            long nativeMeasuredText) {
-        drawText(nativeCanvas, text, start, count, x, y, isRtl ? Paint.BIDI_RTL : Paint.BIDI_LTR, paint);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawTextOnPath(long nativeCanvas,
-            char[] text, int index,
-            int count, long path,
-            float hOffset,
-            float vOffset, int bidiFlags,
-            long paint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawTextOnPath is not supported.", null, null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nDrawTextOnPath(long nativeCanvas,
-            String text, long path,
-            float hOffset,
-            float vOffset,
-            int bidiFlags, long paint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawTextOnPath is not supported.", null, null, null /*data*/);
-    }
-
-    // ---- Private delegate/helper methods ----
-
-    /**
-     * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
-     * <p>Note that the drawable may actually be executed several times if there are
-     * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
-     */
-    private static void draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode,
-            GcSnapshot.Drawable drawable) {
-        // get the delegate from the native int.
-        BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        // get the paint which can be null if nPaint is 0;
-        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
-
-        canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly, forceSrcMode);
-    }
-
-    /**
-     * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
-     * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
-     * <p>Note that the drawable may actually be executed several times if there are
-     * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
-     */
-    private static void draw(long nCanvas, GcSnapshot.Drawable drawable) {
-        // get the delegate from the native int.
-        BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        canvasDelegate.mSnapshot.draw(drawable);
-    }
-
-    private static void drawText(long nativeCanvas, final char[] text, final int index,
-            final int count, final float startX, final float startY, final int bidiFlags,
-            long paint) {
-
-        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                (graphics, paintDelegate) -> {
-                    // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
-                    // Any change to this method should be reflected in Paint.measureText
-
-                    // Paint.TextAlign indicates how the text is positioned relative to X.
-                    // LEFT is the default and there's nothing to do.
-                    float x = startX;
-                    int limit = index + count;
-                    if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
-                        RectF bounds =
-                                paintDelegate.measureText(text, index, count, null, 0, bidiFlags);
-                        float m = bounds.right - bounds.left;
-                        if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
-                            x -= m / 2;
-                        } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
-                            x -= m;
-                        }
-                    }
-
-                    new BidiRenderer(graphics, paintDelegate, text).setRenderLocation(x,
-                            startY).renderText(index, limit, bidiFlags, null, 0, true);
-                });
-    }
-
-    private static void drawBitmap(long nativeCanvas, Bitmap_Delegate bitmap,
-            long nativePaintOrZero, final int sleft, final int stop, final int sright,
-            final int sbottom, final int dleft, final int dtop, final int dright,
-            final int dbottom) {
-        // get the delegate from the native int.
-        BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        // get the paint, which could be null if the int is 0
-        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
-
-        final BufferedImage image = getImageToDraw(bitmap, paintDelegate, sBoolOut);
-
-        draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0],
-                (graphics, paint) -> {
-                    if (paint != null && paint.isFilterBitmap()) {
-                        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
-                                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-                    }
-
-                    //FIXME add support for canvas, screen and bitmap densities.
-                    graphics.drawImage(image, dleft, dtop, dright, dbottom, sleft, stop, sright,
-                            sbottom, null);
-                });
-    }
-
-    /**
-     * Returns a BufferedImage ready for drawing, based on the bitmap and paint delegate.
-     * The image returns, through a 1-size boolean array, whether the drawing code should
-     * use a SRC composite no matter what the paint says.
-     *
-     * @param bitmap the bitmap
-     * @param paint the paint that will be used to draw
-     * @param forceSrcMode whether the composite will have to be SRC
-     * @return the image to draw
-     */
-    private static BufferedImage getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint,
-            boolean[] forceSrcMode) {
-        BufferedImage image = bitmap.getImage();
-        forceSrcMode[0] = false;
-
-        // if the bitmap config is alpha_8, then we erase all color value from it
-        // before drawing it or apply the texture from the shader if present.
-        if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) {
-            Shader_Delegate shader = paint.getShader();
-            java.awt.Paint javaPaint = null;
-            if (shader instanceof BitmapShader_Delegate) {
-                javaPaint = shader.getJavaPaint();
-            }
-
-            fixAlpha8Bitmap(image, javaPaint);
-        } else if (!bitmap.hasAlpha()) {
-            // hasAlpha is merely a rendering hint. There can in fact be alpha values
-            // in the bitmap but it should be ignored at drawing time.
-            // There is two ways to do this:
-            // - override the composite to be SRC. This can only be used if the composite
-            //   was going to be SRC or SRC_OVER in the first place
-            // - Create a different bitmap to draw in which all the alpha channel values is set
-            //   to 0xFF.
-            if (paint != null) {
-                PorterDuff.Mode mode = PorterDuff.intToMode(paint.getPorterDuffMode());
-
-                forceSrcMode[0] = mode == PorterDuff.Mode.SRC_OVER || mode == PorterDuff.Mode.SRC;
-            }
-
-            // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB
-            if (!forceSrcMode[0]) {
-                image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF);
-            }
-        }
-
-        return image;
-    }
-
-    /**
-     * This method will apply the correct color to the passed "only alpha" image. Colors on the
-     * passed image will be destroyed.
-     * If the passed javaPaint is null, the color will be set to 0. If a paint is passed, it will
-     * be used to obtain the color that will be applied.
-     * <p/>
-     * This will destroy the passed image color channel.
-     */
-    private static void fixAlpha8Bitmap(final BufferedImage image,
-            @Nullable java.awt.Paint javaPaint) {
-        int w = image.getWidth();
-        int h = image.getHeight();
-
-        DataBuffer texture = null;
-        if (javaPaint != null) {
-            PaintContext context = javaPaint.createContext(ColorModel.getRGBdefault(), null, null,
-                    new AffineTransform(), null);
-            texture = context.getRaster(0, 0, w, h).getDataBuffer();
-        }
-
-        int[] argb = new int[w * h];
-        image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
-
-        final int length = argb.length;
-        for (int i = 0; i < length; i++) {
-            argb[i] &= 0xFF000000;
-            if (texture != null) {
-                argb[i] |= texture.getElem(i) & 0x00FFFFFF;
-            }
-        }
-
-        image.setRGB(0, 0, w, h, argb, 0, w);
-    }
-
-    protected int save(int saveFlags) {
-        // get the current save count
-        int count = mSnapshot.size();
-
-        mSnapshot = mSnapshot.save(saveFlags);
-
-        // return the old save count
-        return count;
-    }
-
-    protected int saveLayerAlpha(RectF rect, int alpha, int saveFlags) {
-        Paint_Delegate paint = new Paint_Delegate();
-        paint.setAlpha(alpha);
-        return saveLayer(rect, paint, saveFlags);
-    }
-
-    protected int saveLayer(RectF rect, Paint_Delegate paint, int saveFlags) {
-        // get the current save count
-        int count = mSnapshot.size();
-
-        mSnapshot = mSnapshot.saveLayer(rect, paint, saveFlags);
-
-        // return the old save count
-        return count;
-    }
-
-    /**
-     * Restores the {@link GcSnapshot} to <var>saveCount</var>
-     * @param saveCount the saveCount
-     */
-    protected void restoreTo(int saveCount) {
-        mSnapshot = mSnapshot.restoreTo(saveCount);
-    }
-
-    /**
-     * Restores the top {@link GcSnapshot}
-     */
-    protected void restore() {
-        mSnapshot = mSnapshot.restore();
-    }
-
-    protected boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
-        return mSnapshot.clipRect(left, top, right, bottom, regionOp);
-    }
-}
diff --git a/bridge/src/android/graphics/BidiRenderer.java b/bridge/src/android/graphics/BidiRenderer.java
deleted file mode 100644
index 40d350b..0000000
--- a/bridge/src/android/graphics/BidiRenderer.java
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.Paint_Delegate.FontInfo;
-import android.icu.lang.UScriptRun;
-import android.icu.text.Bidi;
-import android.icu.text.BidiRun;
-
-import java.awt.Font;
-import java.awt.Graphics2D;
-import java.awt.Toolkit;
-import java.awt.font.FontRenderContext;
-import java.awt.font.GlyphVector;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Rectangle2D;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * Render the text by breaking it into various scripts and using the right font for each script.
- * Can be used to measure the text without actually drawing it.
- */
-@SuppressWarnings("deprecation")
-public class BidiRenderer {
-    private static final String JETBRAINS_VENDOR_ID = "JetBrains s.r.o";
-    private static final String JAVA_VENDOR = System.getProperty("java.vendor");
-    /** When scaleX is bigger than this, we need to apply the workaround for http://b.android.com/211659 */
-    private static final double SCALEX_WORKAROUND_LIMIT = 9;
-
-    private static class ScriptRun {
-        private final int start;
-        private final int limit;
-        private final Font font;
-
-        private ScriptRun(int start, int limit, @NonNull Font font) {
-            this.start = start;
-            this.limit = limit;
-            this.font = font;
-        }
-    }
-
-    private final Graphics2D mGraphics;
-    private final Paint_Delegate mPaint;
-    private char[] mText;
-    // Bounds of the text drawn so far.
-    private RectF mBounds;
-    private float mBaseline;
-    private final Bidi mBidi = new Bidi();
-
-
-    /**
-     * @param graphics May be null.
-     * @param paint The Paint to use to get the fonts. Should not be null.
-     * @param text Unidirectional text. Should not be null.
-     */
-    public BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
-        assert (paint != null);
-        mGraphics = graphics;
-        mPaint = paint;
-        mText = text;
-        mBounds = new RectF();
-    }
-
-    /**
-     *
-     * @param x The x-coordinate of the left edge of where the text should be drawn on the given
-     *            graphics.
-     * @param y The y-coordinate at which to draw the text on the given mGraphics.
-     *
-     */
-    public BidiRenderer setRenderLocation(float x, float y) {
-        mBounds.set(x, y, x, y);
-        mBaseline = y;
-        return this;
-    }
-
-    /**
-     * Perform Bidi Analysis on the text and then render it.
-     * <p/>
-     * To skip the analysis and render unidirectional text, see {@link
-     * #renderText(int, int, boolean, float[], int, boolean)}
-     */
-    public RectF renderText(int start, int limit, int bidiFlags, float[] advances,
-            int advancesIndex, boolean draw) {
-        mBidi.setPara(Arrays.copyOfRange(mText, start, limit), (byte)getIcuFlags(bidiFlags), null);
-        mText = mBidi.getText();
-        for (int i = 0; i < mBidi.countRuns(); i++) {
-            BidiRun visualRun = mBidi.getVisualRun(i);
-            boolean isRtl = visualRun.getDirection() == Bidi.RTL;
-            renderText(visualRun.getStart(), visualRun.getLimit(), isRtl, advances,
-                    advancesIndex, draw);
-        }
-        return mBounds;
-    }
-
-    /**
-     * Render unidirectional text.
-     * <p/>
-     * This method can also be used to measure the width of the text without actually drawing it.
-     * <p/>
-     * @param start index of the first character
-     * @param limit index of the first character that should not be rendered.
-     * @param isRtl is the text right-to-left
-     * @param advances If not null, then advances for each character to be rendered are returned
-     *            here.
-     * @param advancesIndex index into advances from where the advances need to be filled.
-     * @param draw If true and {@code graphics} is not null, draw the rendered text on the graphics
-     *            at the given co-ordinates
-     * @return A rectangle specifying the bounds of the text drawn.
-     */
-    public RectF renderText(int start, int limit, boolean isRtl, float[] advances,
-            int advancesIndex, boolean draw) {
-        // We break the text into scripts and then select font based on it and then render each of
-        // the script runs.
-        for (ScriptRun run : getScriptRuns(mText, start, limit, mPaint.getFonts())) {
-            int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT;
-            flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT;
-            renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw);
-            advancesIndex += run.limit - run.start;
-        }
-        return mBounds;
-    }
-
-    /**
-     * Render a script run to the right of the bounds passed. Use the preferred font to render as
-     * much as possible. This also implements a fallback mechanism to render characters that cannot
-     * be drawn using the preferred font.
-     */
-    private void renderScript(int start, int limit, Font preferredFont, int flag,
-            float[] advances, int advancesIndex, boolean draw) {
-        if (mPaint.getFonts().size() == 0 || preferredFont == null) {
-            return;
-        }
-
-        while (start < limit) {
-            int canDisplayUpTo = preferredFont.canDisplayUpTo(mText, start, limit);
-            if (canDisplayUpTo == -1) {
-                // We can draw all characters in the text.
-                render(start, limit, preferredFont, flag, advances, advancesIndex, draw);
-                return;
-            }
-            if (canDisplayUpTo > start) {
-                // We can draw something.
-                render(start, canDisplayUpTo, preferredFont, flag, advances, advancesIndex, draw);
-                advancesIndex += canDisplayUpTo - start;
-                start = canDisplayUpTo;
-            } else {
-                // We can display everything with the preferred font. Search for the font that
-                // allows us to display the maximum number of chars
-                List<FontInfo> fontInfos = mPaint.getFonts();
-                Font bestFont = null;
-                int highestUpTo = canDisplayUpTo;
-                //noinspection ForLoopReplaceableByForEach
-                for (int i = 0; i < fontInfos.size(); i++) {
-                    Font font = fontInfos.get(i).mFont;
-
-                    if (preferredFont == font) {
-                        // We know this font won't work since we've already tested it at the
-                        // beginning of the loop
-                        continue;
-                    }
-
-                    if (font == null) {
-                        logFontWarning();
-                        continue;
-                    }
-
-                    canDisplayUpTo = font.canDisplayUpTo(mText, start, limit);
-                    if (canDisplayUpTo == -1) {
-                        // This font can dis
-                        highestUpTo = limit;
-                        bestFont = font;
-                        break;
-                    } else if (canDisplayUpTo > highestUpTo) {
-                        highestUpTo = canDisplayUpTo;
-                        bestFont = font;
-                        // Keep searching in case there is a font that allows to display even
-                        // more text
-                    }
-                }
-
-                if (bestFont != null) {
-                    render(start, highestUpTo, bestFont, flag, advances, advancesIndex, draw);
-                    advancesIndex += highestUpTo - start;
-                    start = highestUpTo;
-                } else {
-                    int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1;
-
-                    // No font can display this char. Use the preferred font and skip this char.
-                    // The char will most probably appear as a box or a blank space. We could,
-                    // probably, use some heuristics and break the character into the base
-                    // character and diacritics and then draw it, but it's probably not worth the
-                    // effort.
-                    render(start, start + charCount, preferredFont, flag, advances, advancesIndex,
-                            draw);
-                    start += charCount;
-                    advancesIndex += charCount;
-                }
-            }
-        }
-    }
-
-    private static void logFontWarning() {
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_BROKEN,
-                "Some fonts could not be loaded. The rendering may not be perfect.", null, null,
-                null);
-    }
-
-    /**
-     * Renders the text to the right of the bounds with the given font.
-     * @param font The font to render the text with.
-     */
-    private void render(int start, int limit, Font font, int flag, float[] advances,
-            int advancesIndex, boolean draw) {
-        FontRenderContext frc = mGraphics != null ? mGraphics.getFontRenderContext() :
-                    Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
-
-        boolean frcIsAntialiased = frc.isAntiAliased();
-        boolean useAntialiasing = mPaint.isAntiAliased();
-
-        if (frcIsAntialiased) {
-            if (!useAntialiasing) {
-                // The context has antialiasing enabled but the paint does not. We need to
-                // disable it
-                frc = new FontRenderContext(font.getTransform(), false,
-                        frc.usesFractionalMetrics());
-            } else {
-                // In this case both the paint and the context antialising match but we need
-                // to check for a bug in the JDK
-                // Workaround for http://b.android.com/211659 (disable antialiasing)
-                if (font.isTransformed()) {
-                    AffineTransform transform = font.getTransform();
-                    if (transform.getScaleX() >= SCALEX_WORKAROUND_LIMIT &&
-                            JETBRAINS_VENDOR_ID.equals(JAVA_VENDOR)) {
-                        frc = new FontRenderContext(transform, false, frc.usesFractionalMetrics());
-                    }
-                }
-            }
-        } else if (useAntialiasing) {
-            // The context does not have antialiasing enabled but the paint does. We need to
-            // enable it unless we need to avoid the JDK bug
-
-            AffineTransform transform = font.getTransform();
-            // Workaround for http://b.android.com/211659 (disable antialiasing)
-            if (transform.getScaleX() < SCALEX_WORKAROUND_LIMIT ||
-                    !JETBRAINS_VENDOR_ID.equals(JAVA_VENDOR)) {
-                frc = new FontRenderContext(font.getTransform(), true, frc.usesFractionalMetrics());
-            }
-        }
-
-        GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag);
-        int ng = gv.getNumGlyphs();
-        int[] ci = gv.getGlyphCharIndices(0, ng, null);
-        if (advances != null) {
-            for (int i = 0; i < ng; i++) {
-                if (mText[ci[i]] == '\uFEFF') {
-                    // Workaround for bug in JetBrains JDK
-                    // where the character \uFEFF is associated a glyph with non-zero width
-                    continue;
-                }
-                int adv_idx = advancesIndex + ci[i];
-                advances[adv_idx] += gv.getGlyphMetrics(i).getAdvanceX();
-            }
-        }
-        if (draw && mGraphics != null) {
-            mGraphics.drawGlyphVector(gv, mBounds.right, mBaseline);
-        }
-
-        // Update the bounds.
-        Rectangle2D awtBounds = gv.getLogicalBounds();
-        // If the width of the bounds is zero, no text had been drawn earlier. Hence, use the
-        // coordinates from the bounds as an offset.
-        if (Math.abs(mBounds.right - mBounds.left) == 0) {
-            mBounds = awtRectToAndroidRect(awtBounds, mBounds.right, mBaseline, mBounds);
-        } else {
-            mBounds.union(awtRectToAndroidRect(awtBounds, mBounds.right, mBaseline, null));
-        }
-    }
-
-    // --- Static helper methods ---
-
-    private static RectF awtRectToAndroidRect(Rectangle2D awtRec, float offsetX, float offsetY,
-            @Nullable RectF destination) {
-        float left = (float) awtRec.getX();
-        float top = (float) awtRec.getY();
-        float right = (float) (left + awtRec.getWidth());
-        float bottom = (float) (top + awtRec.getHeight());
-        if (destination != null) {
-            destination.set(left, top, right, bottom);
-        } else {
-            destination = new RectF(left, top, right, bottom);
-        }
-        destination.offset(offsetX, offsetY);
-        return destination;
-    }
-
-    private static List<ScriptRun> getScriptRuns(char[] text, int start, int limit, List<FontInfo> fonts) {
-        LinkedList<ScriptRun> scriptRuns = new LinkedList<>();
-
-        int count = limit - start;
-        UScriptRun uScriptRun = new UScriptRun(text, start, count);
-        while (uScriptRun.next()) {
-            int scriptStart = uScriptRun.getScriptStart();
-            int scriptLimit = uScriptRun.getScriptLimit();
-            ScriptRun run = new ScriptRun(
-                    scriptStart, scriptLimit,
-                    getScriptFont(text, scriptStart, scriptLimit, fonts));
-            scriptRuns.add(run);
-        }
-        return scriptRuns;
-    }
-
-    // TODO: Replace this method with one which returns the font based on the scriptCode.
-    @NonNull
-    private static Font getScriptFont(char[] text, int start, int limit, List<FontInfo> fonts) {
-        if (fonts.isEmpty()) {
-            logFontWarning();
-            // Fallback font in case no font can be loaded
-            return Font.getFont(Font.SERIF);
-        }
-
-        // From all the fonts, select the one that can display the highest number of characters
-        Font bestFont = fonts.get(0).mFont;
-        int bestFontCount = 0;
-        for (FontInfo fontInfo : fonts) {
-            int count = fontInfo.mFont.canDisplayUpTo(text, start, limit);
-            if (count == -1) {
-                // This font can display everything, return this one
-                return fontInfo.mFont;
-            }
-
-            if (count > bestFontCount) {
-                bestFontCount = count;
-                bestFont = fontInfo.mFont;
-            }
-        }
-
-        return bestFont;
-    }
-
-    private static int getIcuFlags(int bidiFlag) {
-        switch (bidiFlag) {
-            case Paint.BIDI_LTR:
-            case Paint.BIDI_FORCE_LTR:
-                return Bidi.DIRECTION_LEFT_TO_RIGHT;
-            case Paint.BIDI_RTL:
-            case Paint.BIDI_FORCE_RTL:
-                return Bidi.DIRECTION_RIGHT_TO_LEFT;
-            case Paint.BIDI_DEFAULT_LTR:
-                return Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
-            case Paint.BIDI_DEFAULT_RTL:
-                return Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT;
-            default:
-                assert false;
-                return Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
-        }
-    }
-}
diff --git a/bridge/src/android/graphics/BitmapFactory_Delegate.java b/bridge/src/android/graphics/BitmapFactory_Delegate.java
deleted file mode 100644
index 7a12d38..0000000
--- a/bridge/src/android/graphics/BitmapFactory_Delegate.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.ninepatch.NinePatchChunk;
-import com.android.resources.Density;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.Nullable;
-import com.android.layoutlib.bridge.util.NinePatchInputStream;
-import android.graphics.BitmapFactory.Options;
-import android.graphics.Bitmap_Delegate.BitmapCreateFlags;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.EnumSet;
-import java.util.Set;
-
-/**
- * Delegate implementing the native methods of android.graphics.BitmapFactory
- *
- * Through the layoutlib_create tool, the original native methods of BitmapFactory have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
- * around to map int to instance of the delegate.
- *
- */
-/*package*/ class BitmapFactory_Delegate {
-
-    // ------ Native Delegates ------
-
-    @LayoutlibDelegate
-    /*package*/ static Bitmap nativeDecodeStream(InputStream is, byte[] storage,
-            @Nullable Rect padding, @Nullable Options opts, long inBitmapHandle,
-            long colorSpaceHandle) {
-        Bitmap bm = null;
-
-        Density density = Density.MEDIUM;
-        Set<BitmapCreateFlags> bitmapCreateFlags = EnumSet.of(BitmapCreateFlags.MUTABLE);
-        if (opts != null) {
-            density = Density.getEnum(opts.inDensity);
-            if (opts.inPremultiplied) {
-                bitmapCreateFlags.add(BitmapCreateFlags.PREMULTIPLIED);
-            }
-            opts.inScaled = false;
-        }
-
-        try {
-            if (is instanceof NinePatchInputStream) {
-                NinePatchInputStream npis = (NinePatchInputStream) is;
-                npis.disableFakeMarkSupport();
-
-                // load the bitmap as a nine patch
-                com.android.ninepatch.NinePatch ninePatch = com.android.ninepatch.NinePatch.load(
-                        npis, true /*is9Patch*/, false /*convert*/);
-
-                // get the bitmap and chunk objects.
-                NinePatchChunk chunk = ninePatch.getChunk();
-                bm = Bitmap_Delegate.createBitmap(ninePatch.getImage(),
-                        NinePatch_Delegate.serialize(chunk), bitmapCreateFlags, density);
-
-                if (padding != null) {
-                    // read the padding
-                    int[] paddingArray = chunk.getPadding();
-                    padding.left = paddingArray[0];
-                    padding.top = paddingArray[1];
-                    padding.right = paddingArray[2];
-                    padding.bottom = paddingArray[3];
-                }
-            } else {
-                // load the bitmap directly.
-                bm = Bitmap_Delegate.createBitmap(is, bitmapCreateFlags, density);
-            }
-        } catch (IOException e) {
-            Bridge.getLog().error(null, "Failed to load image", e, null, null);
-        }
-
-        return bm;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
-            Rect padding, Options opts, long inBitmapHandle, long colorSpaceHandle) {
-        if (opts != null) {
-            opts.inBitmap = null;
-        }
-        return null;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static Bitmap nativeDecodeAsset(long asset, Rect padding, Options opts,
-            long inBitmapHandle, long colorSpaceHandle) {
-        if (opts != null) {
-            opts.inBitmap = null;
-        }
-        return null;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static Bitmap nativeDecodeByteArray(byte[] data, int offset,
-            int length, Options opts, long inBitmapHandle, long colorSpaceHandle) {
-        if (opts != null) {
-            opts.inBitmap = null;
-        }
-        return null;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeIsSeekable(FileDescriptor fd) {
-        return true;
-    }
-
-    /**
-     * Set the newly decoded bitmap's density based on the Options.
-     *
-     * Copied from {@link BitmapFactory#setDensityFromOptions(Bitmap, Options)}.
-     */
-    @LayoutlibDelegate
-    /*package*/ static void setDensityFromOptions(Bitmap outputBitmap, Options opts) {
-        if (outputBitmap == null || opts == null) return;
-
-        final int density = opts.inDensity;
-        if (density != 0) {
-            outputBitmap.setDensity(density);
-            final int targetDensity = opts.inTargetDensity;
-            if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
-                return;
-            }
-
-            // --- Change from original implementation begins ---
-            // LayoutLib doesn't scale the nine patch when decoding it. Hence, don't change the
-            // density of the source bitmap in case of ninepatch.
-
-            if (opts.inScaled) {
-            // --- Change from original implementation ends. ---
-                outputBitmap.setDensity(targetDensity);
-            }
-        } else if (opts.inBitmap != null) {
-            // bitmap was reused, ensure density is reset
-            outputBitmap.setDensity(Bitmap.getDefaultDensity());
-        }
-    }
-}
diff --git a/bridge/src/android/graphics/BitmapShader_Delegate.java b/bridge/src/android/graphics/BitmapShader_Delegate.java
deleted file mode 100644
index e763824..0000000
--- a/bridge/src/android/graphics/BitmapShader_Delegate.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.Shader.TileMode;
-
-import java.awt.PaintContext;
-import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.NoninvertibleTransformException;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
-import java.awt.image.ColorModel;
-import java.awt.image.DataBufferInt;
-import java.awt.image.Raster;
-import java.awt.image.SampleModel;
-
-/**
- * Delegate implementing the native methods of android.graphics.BitmapShader
- *
- * Through the layoutlib_create tool, the original native methods of BitmapShader have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original BitmapShader class.
- *
- * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
- *
- * @see Shader_Delegate
- *
- */
-public class BitmapShader_Delegate extends Shader_Delegate {
-
-    // ---- delegate data ----
-    private java.awt.Paint mJavaPaint;
-
-    // ---- Public Helper methods ----
-
-    @Override
-    public java.awt.Paint getJavaPaint() {
-        return mJavaPaint;
-    }
-
-    @Override
-    public boolean isSupported() {
-        return true;
-    }
-
-    @Override
-    public String getSupportMessage() {
-        // no message since isSupported returns true;
-        return null;
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreate(long nativeMatrix, long bitmapHandle,
-            int shaderTileModeX, int shaderTileModeY) {
-        Bitmap_Delegate bitmap = Bitmap_Delegate.getDelegate(bitmapHandle);
-        if (bitmap == null) {
-            return 0;
-        }
-
-        BitmapShader_Delegate newDelegate = new BitmapShader_Delegate(nativeMatrix,
-                bitmap.getImage(),
-                Shader_Delegate.getTileMode(shaderTileModeX),
-                Shader_Delegate.getTileMode(shaderTileModeY));
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    // ---- Private delegate/helper methods ----
-
-    private BitmapShader_Delegate(long matrix, BufferedImage image,
-            TileMode tileModeX, TileMode tileModeY) {
-        super(matrix);
-        mJavaPaint = new BitmapShaderPaint(image, tileModeX, tileModeY);
-    }
-
-    private class BitmapShaderPaint implements java.awt.Paint {
-        private final BufferedImage mImage;
-        private final TileMode mTileModeX;
-        private final TileMode mTileModeY;
-
-        BitmapShaderPaint(BufferedImage image,
-                TileMode tileModeX, TileMode tileModeY) {
-            mImage = image;
-            mTileModeX = tileModeX;
-            mTileModeY = tileModeY;
-        }
-
-        @Override
-        public PaintContext createContext(ColorModel colorModel, Rectangle deviceBounds,
-                Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) {
-            AffineTransform canvasMatrix;
-            try {
-                canvasMatrix = xform.createInverse();
-            } catch (NoninvertibleTransformException e) {
-                Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_INVERSE,
-                        "Unable to inverse matrix in BitmapShader", e, null, null /*data*/);
-                canvasMatrix = new AffineTransform();
-            }
-
-            AffineTransform localMatrix = getLocalMatrix();
-            try {
-                localMatrix = localMatrix.createInverse();
-            } catch (NoninvertibleTransformException e) {
-                Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_INVERSE,
-                        "Unable to inverse matrix in BitmapShader", e, null, null /*data*/);
-                localMatrix = new AffineTransform();
-            }
-
-            if (!colorModel.isCompatibleRaster(mImage.getRaster())) {
-                // Fallback to the default ARGB color model
-                colorModel = ColorModel.getRGBdefault();
-            }
-
-            return new BitmapShaderContext(canvasMatrix, localMatrix, colorModel);
-        }
-
-        private class BitmapShaderContext implements PaintContext {
-
-            private final AffineTransform mCanvasMatrix;
-            private final AffineTransform mLocalMatrix;
-            private final ColorModel mColorModel;
-
-            public BitmapShaderContext(
-                    AffineTransform canvasMatrix,
-                    AffineTransform localMatrix,
-                    ColorModel colorModel) {
-                mCanvasMatrix = canvasMatrix;
-                mLocalMatrix = localMatrix;
-                mColorModel = colorModel;
-            }
-
-            @Override
-            public void dispose() {
-            }
-
-            @Override
-            public ColorModel getColorModel() {
-                return mColorModel;
-            }
-
-            @Override
-            public Raster getRaster(int x, int y, int w, int h) {
-                BufferedImage image = new BufferedImage(
-                    mColorModel, mColorModel.createCompatibleWritableRaster(w, h),
-                    mColorModel.isAlphaPremultiplied(), null);
-
-                int[] data = new int[w*h];
-
-                int index = 0;
-                float[] pt1 = new float[2];
-                float[] pt2 = new float[2];
-                for (int iy = 0 ; iy < h ; iy++) {
-                    for (int ix = 0 ; ix < w ; ix++) {
-                        // handle the canvas transform
-                        pt1[0] = x + ix;
-                        pt1[1] = y + iy;
-                        mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
-
-                        // handle the local matrix.
-                        pt1[0] = pt2[0];
-                        pt1[1] = pt2[1];
-                        mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
-
-                        data[index++] = getColor(pt2[0], pt2[1]);
-                    }
-                }
-
-                DataBufferInt dataBuffer = new DataBufferInt(data, data.length);
-                SampleModel colorModel = mColorModel.createCompatibleSampleModel(w, h);
-                return Raster.createWritableRaster(colorModel, dataBuffer, null);
-            }
-        }
-
-        /**
-         * Returns a color for an arbitrary point.
-         */
-        private int getColor(float fx, float fy) {
-            int x = getCoordinate(Math.round(fx), mImage.getWidth(), mTileModeX);
-            int y = getCoordinate(Math.round(fy), mImage.getHeight(), mTileModeY);
-
-            return mImage.getRGB(x, y);
-        }
-
-        private int getCoordinate(int i, int size, TileMode mode) {
-            if (i < 0) {
-                switch (mode) {
-                    case CLAMP:
-                        i = 0;
-                        break;
-                    case REPEAT:
-                        i = size - 1 - (-i % size);
-                        break;
-                    case MIRROR:
-                        // this is the same as the positive side, just make the value positive
-                        // first.
-                        i = -i;
-                        int count = i / size;
-                        i = i % size;
-
-                        if ((count % 2) == 1) {
-                            i = size - 1 - i;
-                        }
-                        break;
-                }
-            } else if (i >= size) {
-                switch (mode) {
-                    case CLAMP:
-                        i = size - 1;
-                        break;
-                    case REPEAT:
-                        i = i % size;
-                        break;
-                    case MIRROR:
-                        int count = i / size;
-                        i = i % size;
-
-                        if ((count % 2) == 1) {
-                            i = size - 1 - i;
-                        }
-                        break;
-                }
-            }
-
-            return i;
-        }
-
-
-        @Override
-        public int getTransparency() {
-            return java.awt.Paint.TRANSLUCENT;
-        }
-    }
-}
diff --git a/bridge/src/android/graphics/Bitmap_Delegate.java b/bridge/src/android/graphics/Bitmap_Delegate.java
deleted file mode 100644
index bfd00c5..0000000
--- a/bridge/src/android/graphics/Bitmap_Delegate.java
+++ /dev/null
@@ -1,760 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.AssetRepository;
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.ide.common.rendering.api.RenderResources;
-import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.android.BridgeContext;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.RenderAction;
-import com.android.resources.Density;
-import com.android.resources.ResourceType;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.Nullable;
-import android.graphics.Bitmap.Config;
-import android.hardware.HardwareBuffer;
-import android.os.Parcel;
-
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.Buffer;
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.Set;
-
-import javax.imageio.ImageIO;
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-import static android.content.res.AssetManager.ACCESS_STREAMING;
-
-/**
- * Delegate implementing the native methods of android.graphics.Bitmap
- *
- * Through the layoutlib_create tool, the original native methods of Bitmap have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Bitmap class.
- *
- * @see DelegateManager
- *
- */
-public final class Bitmap_Delegate {
-
-    public enum BitmapCreateFlags {
-        NONE, PREMULTIPLIED, MUTABLE
-    }
-
-    // ---- delegate manager ----
-    private static final DelegateManager<Bitmap_Delegate> sManager =
-            new DelegateManager<>(Bitmap_Delegate.class);
-    private static long sFinalizer = -1;
-
-    // ---- delegate helper data ----
-
-    // ---- delegate data ----
-    private final Config mConfig;
-    private final BufferedImage mImage;
-    private boolean mHasAlpha = true;
-    private boolean mHasMipMap = false;      // TODO: check the default.
-    private boolean mIsPremultiplied = true;
-    private int mGenerationId = 0;
-    private boolean mIsMutable;
-
-
-    // ---- Public Helper methods ----
-
-    /**
-     * Returns the native delegate associated to a given an int referencing a {@link Bitmap} object.
-     */
-    public static Bitmap_Delegate getDelegate(long native_bitmap) {
-        return sManager.getDelegate(native_bitmap);
-    }
-
-    /**
-     * Creates and returns a {@link Bitmap} initialized with the given stream content.
-     *
-     * @param input the stream from which to read the bitmap content
-     * @param isMutable whether the bitmap is mutable
-     * @param density the density associated with the bitmap
-     *
-     * @see Bitmap#isMutable()
-     * @see Bitmap#getDensity()
-     */
-    public static Bitmap createBitmap(@Nullable InputStream input, boolean isMutable,
-            Density density) throws IOException {
-        return createBitmap(input, getPremultipliedBitmapCreateFlags(isMutable), density);
-    }
-
-    /**
-     * Creates and returns a {@link Bitmap} initialized with the given file content.
-     *
-     * @param input the file from which to read the bitmap content
-     * @param density the density associated with the bitmap
-     *
-     * @see Bitmap#isPremultiplied()
-     * @see Bitmap#isMutable()
-     * @see Bitmap#getDensity()
-     */
-    static Bitmap createBitmap(@Nullable InputStream input, Set<BitmapCreateFlags> createFlags,
-            Density density) throws IOException {
-        // create a delegate with the content of the file.
-        BufferedImage image = input == null ? null : ImageIO.read(input);
-        if (image == null) {
-            // There was a problem decoding the image, or the decoder isn't registered. Webp maybe.
-            // Replace with a broken image icon.
-            BridgeContext currentContext = RenderAction.getCurrentContext();
-            if (currentContext != null) {
-                RenderResources resources = currentContext.getRenderResources();
-                ResourceValue broken = resources.getResolvedResource(
-                        BridgeContext.createFrameworkResourceReference(
-                                ResourceType.DRAWABLE, "ic_menu_report_image"));
-                AssetRepository assetRepository = currentContext.getAssets().getAssetRepository();
-                try (InputStream stream =
-                        assetRepository.openNonAsset(0, broken.getValue(), ACCESS_STREAMING)) {
-                    if (stream != null) {
-                        image = ImageIO.read(stream);
-                    }
-                }
-            }
-        }
-        Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888);
-        delegate.mIsMutable = createFlags.contains(BitmapCreateFlags.MUTABLE);
-
-        return createBitmap(delegate, createFlags, density.getDpiValue(), null);
-    }
-
-    /**
-     * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
-     *
-     * @param image the bitmap content
-     * @param isMutable whether the bitmap is mutable
-     * @param density the density associated with the bitmap
-     *
-     * @see Bitmap#isMutable()
-     * @see Bitmap#getDensity()
-     */
-    public static Bitmap createBitmap(BufferedImage image, boolean isMutable, Density density) {
-        return createBitmap(image, getPremultipliedBitmapCreateFlags(isMutable), density);
-    }
-
-    /**
-     * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
-     *
-     * @param image the bitmap content
-     * @param density the density associated with the bitmap
-     *
-     * @see Bitmap#isPremultiplied()
-     * @see Bitmap#isMutable()
-     * @see Bitmap#getDensity()
-     */
-    public static Bitmap createBitmap(BufferedImage image, Set<BitmapCreateFlags> createFlags,
-            Density density) {
-        return createBitmap(image, null, createFlags, density);
-    }
-
-    /**
-     * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
-     *
-     * @param image the bitmap content
-     * @param ninePatchChunk serialized ninepatch data
-     * @param density the density associated with the bitmap
-     *
-     * @see Bitmap#isPremultiplied()
-     * @see Bitmap#isMutable()
-     * @see Bitmap#getDensity()
-     */
-    public static Bitmap createBitmap(BufferedImage image, byte[] ninePatchChunk,
-            Set<BitmapCreateFlags> createFlags, Density density) {
-        // create a delegate with the given image.
-        Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888);
-        delegate.mIsMutable = createFlags.contains(BitmapCreateFlags.MUTABLE);
-
-        return createBitmap(delegate, createFlags, density.getDpiValue(), ninePatchChunk);
-    }
-
-    private static int getBufferedImageType() {
-        return BufferedImage.TYPE_INT_ARGB;
-    }
-
-    /**
-     * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}.
-     */
-    public BufferedImage getImage() {
-        return mImage;
-    }
-
-    /**
-     * Returns the Android bitmap config. Note that this not the config of the underlying
-     * Java2D bitmap.
-     */
-    public Config getConfig() {
-        return mConfig;
-    }
-
-    /**
-     * Returns the hasAlpha rendering hint
-     * @return true if the bitmap alpha should be used at render time
-     */
-    public boolean hasAlpha() {
-        return mHasAlpha && mConfig != Config.RGB_565;
-    }
-
-    /**
-     * Update the generationId.
-     *
-     * @see Bitmap#getGenerationId()
-     */
-    public void change() {
-        mGenerationId++;
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width,
-            int height, int nativeConfig, boolean isMutable, long nativeColorSpace) {
-        int imageType = getBufferedImageType();
-
-        // create the image
-        BufferedImage image = new BufferedImage(width, height, imageType);
-
-        if (colors != null) {
-            image.setRGB(0, 0, width, height, colors, offset, stride);
-        }
-
-        // create a delegate with the content of the stream.
-        Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
-        delegate.mIsMutable = isMutable;
-
-        return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
-                            Bitmap.getDefaultDensity(), null);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static Bitmap nativeCopy(long srcBitmap, int nativeConfig, boolean isMutable) {
-        Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(srcBitmap);
-        if (srcBmpDelegate == null) {
-            return null;
-        }
-
-        BufferedImage srcImage = srcBmpDelegate.getImage();
-
-        int width = srcImage.getWidth();
-        int height = srcImage.getHeight();
-
-        int imageType = getBufferedImageType();
-
-        // create the image
-        BufferedImage image = new BufferedImage(width, height, imageType);
-
-        // copy the source image into the image.
-        int[] argb = new int[width * height];
-        srcImage.getRGB(0, 0, width, height, argb, 0, width);
-        image.setRGB(0, 0, width, height, argb, 0, width);
-
-        // create a delegate with the content of the stream.
-        Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
-        delegate.mIsMutable = isMutable;
-
-        return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
-                Bitmap.getDefaultDensity(), null);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static Bitmap nativeCopyAshmem(long nativeSrcBitmap) {
-        // Unused method; no implementation provided.
-        assert false;
-        return null;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static Bitmap nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig) {
-        // Unused method; no implementation provided.
-        assert false;
-        return null;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeGetNativeFinalizer() {
-        synchronized (Bitmap_Delegate.class) {
-            if (sFinalizer == -1) {
-                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
-            }
-            return sFinalizer;
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeRecycle(long nativeBitmap) {
-        // In our case recycle() is a no-op. We will let the finalizer to dispose the bitmap.
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeReconfigure(long nativeBitmap, int width, int height,
-            int config, boolean isPremultiplied) {
-        Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
-                "Bitmap.reconfigure() is not supported", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeCompress(long nativeBitmap, int format, int quality,
-            OutputStream stream, byte[] tempStorage) {
-        Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
-                "Bitmap.compress() is not supported", null, null /*data*/);
-        return true;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeErase(long nativeBitmap, int color) {
-        // get the delegate from the native int.
-        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
-        if (delegate == null) {
-            return;
-        }
-
-        BufferedImage image = delegate.mImage;
-
-        Graphics2D g = image.createGraphics();
-        try {
-            g.setColor(new java.awt.Color(color, true));
-
-            g.fillRect(0, 0, image.getWidth(), image.getHeight());
-        } finally {
-            g.dispose();
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeErase(long nativeBitmap, long colorSpacePtr, long color) {
-        nativeErase(nativeBitmap, Color.toArgb(color));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nativeRowBytes(long nativeBitmap) {
-        // get the delegate from the native int.
-        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
-        if (delegate == null) {
-            return 0;
-        }
-
-        return delegate.mImage.getWidth();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nativeConfig(long nativeBitmap) {
-        // get the delegate from the native int.
-        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
-        if (delegate == null) {
-            return 0;
-        }
-
-        return delegate.mConfig.nativeInt;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeHasAlpha(long nativeBitmap) {
-        // get the delegate from the native int.
-        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
-        return delegate == null || delegate.mHasAlpha;
-
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeHasMipMap(long nativeBitmap) {
-        // get the delegate from the native int.
-        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
-        return delegate == null || delegate.mHasMipMap;
-
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nativeGetPixel(long nativeBitmap, int x, int y) {
-        // get the delegate from the native int.
-        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
-        if (delegate == null) {
-            return 0;
-        }
-
-        return delegate.mImage.getRGB(x, y);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeGetColor(long nativeBitmap, int x, int y) {
-        return nativeGetPixel(nativeBitmap, x, y);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeGetPixels(long nativeBitmap, int[] pixels, int offset,
-            int stride, int x, int y, int width, int height) {
-        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.getImage().getRGB(x, y, width, height, pixels, offset, stride);
-    }
-
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeSetPixel(long nativeBitmap, int x, int y, int color) {
-        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.getImage().setRGB(x, y, color);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeSetPixels(long nativeBitmap, int[] colors, int offset,
-            int stride, int x, int y, int width, int height) {
-        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.getImage().setRGB(x, y, width, height, colors, offset, stride);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeCopyPixelsToBuffer(long nativeBitmap, Buffer dst) {
-        // FIXME implement native delegate
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Bitmap.copyPixelsToBuffer is not supported.", null, null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeCopyPixelsFromBuffer(long nb, Buffer src) {
-        // FIXME implement native delegate
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Bitmap.copyPixelsFromBuffer is not supported.", null, null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nativeGenerationId(long nativeBitmap) {
-        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
-        if (delegate == null) {
-            return 0;
-        }
-
-        return delegate.mGenerationId;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static Bitmap nativeCreateFromParcel(Parcel p) {
-        // This is only called by Bitmap.CREATOR (Parcelable.Creator<Bitmap>), which is only
-        // used during aidl call so really this should not be called.
-        Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
-                "AIDL is not suppored, and therefore Bitmaps cannot be created from parcels.",
-                null, null /*data*/);
-        return null;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeWriteToParcel(long nativeBitmap, int density, Parcel p) {
-        // This is only called when sending a bitmap through aidl, so really this should not
-        // be called.
-        Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
-                "AIDL is not suppored, and therefore Bitmaps cannot be written to parcels.",
-                null, null /*data*/);
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static Bitmap nativeExtractAlpha(long nativeBitmap, long nativePaint,
-            int[] offsetXY) {
-        Bitmap_Delegate bitmap = sManager.getDelegate(nativeBitmap);
-        if (bitmap == null) {
-            return null;
-        }
-
-        // get the paint which can be null if nativePaint is 0.
-        Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint);
-
-        if (paint != null && paint.getMaskFilter() != null) {
-            Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MASKFILTER,
-                    "MaskFilter not supported in Bitmap.extractAlpha",
-                    null, null, null /*data*/);
-        }
-
-        int alpha = paint != null ? paint.getAlpha() : 0xFF;
-        BufferedImage image = createCopy(bitmap.getImage(), BufferedImage.TYPE_INT_ARGB, alpha);
-
-        // create the delegate. The actual Bitmap config is only an alpha channel
-        Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ALPHA_8);
-        delegate.mIsMutable = true;
-
-        // the density doesn't matter, it's set by the Java method.
-        return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.MUTABLE),
-                Density.DEFAULT_DENSITY /*density*/, null);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeIsPremultiplied(long nativeBitmap) {
-        // get the delegate from the native int.
-        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
-        return delegate != null && delegate.mIsPremultiplied;
-
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeSetPremultiplied(long nativeBitmap, boolean isPremul) {
-        // get the delegate from the native int.
-        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.mIsPremultiplied = isPremul;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha,
-            boolean isPremul) {
-        // get the delegate from the native int.
-        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.mHasAlpha = hasAlpha;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap) {
-        // get the delegate from the native int.
-        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.mHasMipMap = hasMipMap;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeSameAs(long nb0, long nb1) {
-        Bitmap_Delegate delegate1 = sManager.getDelegate(nb0);
-        if (delegate1 == null) {
-            return false;
-        }
-
-        Bitmap_Delegate delegate2 = sManager.getDelegate(nb1);
-        if (delegate2 == null) {
-            return false;
-        }
-
-        BufferedImage image1 = delegate1.getImage();
-        BufferedImage image2 = delegate2.getImage();
-        if (delegate1.mConfig != delegate2.mConfig ||
-                image1.getWidth() != image2.getWidth() ||
-                image1.getHeight() != image2.getHeight()) {
-            return false;
-        }
-
-        // get the internal data
-        int w = image1.getWidth();
-        int h = image2.getHeight();
-        int[] argb1 = new int[w*h];
-        int[] argb2 = new int[w*h];
-
-        image1.getRGB(0, 0, w, h, argb1, 0, w);
-        image2.getRGB(0, 0, w, h, argb2, 0, w);
-
-        // compares
-        if (delegate1.mConfig == Config.ALPHA_8) {
-            // in this case we have to manually compare the alpha channel as the rest is garbage.
-            final int length = w*h;
-            for (int i = 0 ; i < length ; i++) {
-                if ((argb1[i] & 0xFF000000) != (argb2[i] & 0xFF000000)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        return Arrays.equals(argb1, argb2);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nativeGetAllocationByteCount(long nativeBitmap) {
-        // get the delegate from the native int.
-        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
-        if (delegate == null) {
-            return 0;
-        }
-        int size = nativeRowBytes(nativeBitmap) * delegate.mImage.getHeight();
-        return size < 0 ? Integer.MAX_VALUE : size;
-
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativePrepareToDraw(long nativeBitmap) {
-        // do nothing as Bitmap_Delegate does not have caches
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap) {
-        Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(nativeBitmap);
-        if (srcBmpDelegate == null) {
-            return null;
-        }
-
-        BufferedImage srcImage = srcBmpDelegate.getImage();
-
-        // create the image
-        BufferedImage image = new BufferedImage(srcImage.getColorModel(), srcImage.copyData(null),
-                srcImage.isAlphaPremultiplied(), null);
-
-        // create a delegate with the content of the stream.
-        Bitmap_Delegate delegate = new Bitmap_Delegate(image, srcBmpDelegate.getConfig());
-        delegate.mIsMutable = srcBmpDelegate.mIsMutable;
-
-        return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.NONE),
-                Bitmap.getDefaultDensity(), null);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer,
-            long nativeColorSpace) {
-        Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
-                "Bitmap.nativeWrapHardwareBufferBitmap() is not supported", null, null, null);
-        return null;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeIsSRGB(long nativeBitmap) {
-        Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
-                "Color spaces are not supported", null, null /*data*/);
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static ColorSpace nativeComputeColorSpace(long nativePtr) {
-        Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
-                "Color spaces are not supported", null, null /*data*/);
-        return null;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeSetColorSpace(long nativePtr, long nativeColorSpace) {
-        Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
-                "Color spaces are not supported", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeIsSRGBLinear(long nativePtr) {
-        Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
-                "Color spaces are not supported", null, null /*data*/);
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeSetImmutable(long nativePtr) {
-        Bitmap_Delegate bmpDelegate = sManager.getDelegate(nativePtr);
-        if (bmpDelegate == null) {
-            return;
-        }
-        bmpDelegate.mIsMutable = false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeIsImmutable(long nativePtr) {
-        Bitmap_Delegate bmpDelegate = sManager.getDelegate(nativePtr);
-        if (bmpDelegate == null) {
-            return false;
-        }
-        return !bmpDelegate.mIsMutable;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static HardwareBuffer nativeGetHardwareBuffer(long nativeBitmap) {
-        Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
-                "HardwareBuffer is not supported", null, null /*data*/);
-        return null;
-    }
-
-    // ---- Private delegate/helper methods ----
-
-    private Bitmap_Delegate(BufferedImage image, Config config) {
-        mImage = image;
-        mConfig = config;
-    }
-
-    private static Bitmap createBitmap(Bitmap_Delegate delegate,
-            Set<BitmapCreateFlags> createFlags, int density, byte[] ninePatchChunk) {
-        // get its native_int
-        long nativeInt = sManager.addNewDelegate(delegate);
-
-        int width = delegate.mImage.getWidth();
-        int height = delegate.mImage.getHeight();
-        boolean isPremultiplied = createFlags.contains(BitmapCreateFlags.PREMULTIPLIED);
-
-        // and create/return a new Bitmap with it
-        return new Bitmap(nativeInt, width, height, density, isPremultiplied,
-                ninePatchChunk, null /* layoutBounds */, true /* fromMalloc */);
-    }
-
-    private static Set<BitmapCreateFlags> getPremultipliedBitmapCreateFlags(boolean isMutable) {
-        Set<BitmapCreateFlags> createFlags = EnumSet.of(BitmapCreateFlags.PREMULTIPLIED);
-        if (isMutable) {
-            createFlags.add(BitmapCreateFlags.MUTABLE);
-        }
-        return createFlags;
-    }
-
-    /**
-     * Creates and returns a copy of a given BufferedImage.
-     * <p/>
-     * if alpha is different than 255, then it is applied to the alpha channel of each pixel.
-     *
-     * @param image the image to copy
-     * @param imageType the type of the new image
-     * @param alpha an optional alpha modifier
-     * @return a new BufferedImage
-     */
-    /*package*/ static BufferedImage createCopy(BufferedImage image, int imageType, int alpha) {
-        int w = image.getWidth();
-        int h = image.getHeight();
-
-        BufferedImage result = new BufferedImage(w, h, imageType);
-
-        int[] argb = new int[w * h];
-        image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
-
-        if (alpha != 255) {
-            final int length = argb.length;
-            for (int i = 0 ; i < length; i++) {
-                int a = (argb[i] >>> 24 * alpha) / 255;
-                argb[i] = (a << 24) | (argb[i] & 0x00FFFFFF);
-            }
-        }
-
-        result.setRGB(0, 0, w, h, argb, 0, w);
-
-        return result;
-    }
-
-}
diff --git a/bridge/src/android/graphics/BlendComposite.java b/bridge/src/android/graphics/BlendComposite.java
deleted file mode 100644
index 5cc964a..0000000
--- a/bridge/src/android/graphics/BlendComposite.java
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright (C) 2014 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.Composite;
-import java.awt.CompositeContext;
-import java.awt.RenderingHints;
-import java.awt.image.ColorModel;
-import java.awt.image.DataBuffer;
-import java.awt.image.Raster;
-import java.awt.image.WritableRaster;
-
-/*
- * (non-Javadoc)
- * The class is adapted from a demo tool for Blending Modes written by
- * Romain Guy (romainguy@android.com). The tool is available at
- * http://www.curious-creature.org/2006/09/20/new-blendings-modes-for-java2d/
- *
- * This class has been adapted for applying color filters. When applying color filters, the src
- * image should not extend beyond the dest image, but in our implementation of the filters, it does.
- * To compensate for the effect, we recompute the alpha value of the src image before applying
- * the color filter as it should have been applied.
- */
-public final class BlendComposite implements Composite {
-    public enum BlendingMode {
-        MULTIPLY(),
-        SCREEN(),
-        DARKEN(),
-        LIGHTEN(),
-        OVERLAY(),
-        ADD();
-
-        private final BlendComposite mComposite;
-
-        BlendingMode() {
-            mComposite = new BlendComposite(this);
-        }
-
-        BlendComposite getBlendComposite() {
-            return mComposite;
-        }
-    }
-
-    private float alpha;
-    private BlendingMode mode;
-
-    private BlendComposite(BlendingMode mode) {
-        this(mode, 1.0f);
-    }
-
-    private BlendComposite(BlendingMode mode, float alpha) {
-        this.mode = mode;
-        setAlpha(alpha);
-    }
-
-    public static BlendComposite getInstance(BlendingMode mode) {
-        return mode.getBlendComposite();
-    }
-
-    public static BlendComposite getInstance(BlendingMode mode, float alpha) {
-        if (alpha > 0.9999f) {
-            return getInstance(mode);
-        }
-        return new BlendComposite(mode, alpha);
-    }
-
-    public float getAlpha() {
-        return alpha;
-    }
-
-    public BlendingMode getMode() {
-        return mode;
-    }
-
-    private void setAlpha(float alpha) {
-        if (alpha < 0.0f || alpha > 1.0f) {
-            assert false : "alpha must be comprised between 0.0f and 1.0f";
-            alpha = Math.min(alpha, 1.0f);
-            alpha = Math.max(alpha, 0.0f);
-        }
-
-        this.alpha = alpha;
-    }
-
-    @Override
-    public int hashCode() {
-        return Float.floatToIntBits(alpha) * 31 + mode.ordinal();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (!(obj instanceof BlendComposite)) {
-            return false;
-        }
-
-        BlendComposite bc = (BlendComposite) obj;
-
-        return mode == bc.mode && alpha == bc.alpha;
-    }
-
-    public CompositeContext createContext(ColorModel srcColorModel,
-                                          ColorModel dstColorModel,
-                                          RenderingHints hints) {
-        return new BlendingContext(this);
-    }
-
-    private static final class BlendingContext implements CompositeContext {
-        private final Blender blender;
-        private final BlendComposite composite;
-
-        private BlendingContext(BlendComposite composite) {
-            this.composite = composite;
-            this.blender = Blender.getBlenderFor(composite);
-        }
-
-        public void dispose() {
-        }
-
-        public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
-            if (src.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
-                dstIn.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
-                dstOut.getSampleModel().getDataType() != DataBuffer.TYPE_INT) {
-                throw new IllegalStateException(
-                        "Source and destination must store pixels as INT.");
-            }
-
-            int width = Math.min(src.getWidth(), dstIn.getWidth());
-            int height = Math.min(src.getHeight(), dstIn.getHeight());
-
-            float alpha = composite.getAlpha();
-
-            int[] srcPixel = new int[4];
-            int[] dstPixel = new int[4];
-            int[] result = new int[4];
-            int[] srcPixels = new int[width];
-            int[] dstPixels = new int[width];
-
-            for (int y = 0; y < height; y++) {
-                dstIn.getDataElements(0, y, width, 1, dstPixels);
-                if (alpha != 0) {
-                    src.getDataElements(0, y, width, 1, srcPixels);
-                    for (int x = 0; x < width; x++) {
-                        // pixels are stored as INT_ARGB
-                        // our arrays are [R, G, B, A]
-                        int pixel = srcPixels[x];
-                        srcPixel[0] = (pixel >> 16) & 0xFF;
-                        srcPixel[1] = (pixel >>  8) & 0xFF;
-                        srcPixel[2] = (pixel      ) & 0xFF;
-                        srcPixel[3] = (pixel >> 24) & 0xFF;
-
-                        pixel = dstPixels[x];
-                        dstPixel[0] = (pixel >> 16) & 0xFF;
-                        dstPixel[1] = (pixel >>  8) & 0xFF;
-                        dstPixel[2] = (pixel      ) & 0xFF;
-                        dstPixel[3] = (pixel >> 24) & 0xFF;
-
-                        // ---- Modified from original ----
-                        // recompute src pixel for transparency.
-                        srcPixel[3] *= dstPixel[3] / 0xFF;
-                        // ---- Modification ends ----
-
-                        result = blender.blend(srcPixel, dstPixel, result);
-
-                        // mixes the result with the opacity
-                        if (alpha == 1) {
-                            dstPixels[x] = (result[3] & 0xFF) << 24 |
-                                           (result[0] & 0xFF) << 16 |
-                                           (result[1] & 0xFF) <<  8 |
-                                           result[2] & 0xFF;
-                        } else {
-                            dstPixels[x] =
-                                    ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 |
-                                    ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16 |
-                                    ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) <<  8 |
-                                    (int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF;
-                        }
-
-                    }
-            }
-                dstOut.setDataElements(0, y, width, 1, dstPixels);
-            }
-        }
-    }
-
-    private static abstract class Blender {
-        public abstract int[] blend(int[] src, int[] dst, int[] result);
-
-        public static Blender getBlenderFor(BlendComposite composite) {
-            switch (composite.getMode()) {
-                case ADD:
-                    return new Blender() {
-                        @Override
-                        public int[] blend(int[] src, int[] dst, int[] result) {
-                            for (int i = 0; i < 4; i++) {
-                                result[i] = Math.min(255, src[i] + dst[i]);
-                            }
-                            return result;
-                        }
-                    };
-                case DARKEN:
-                    return new Blender() {
-                        @Override
-                        public int[] blend(int[] src, int[] dst, int[] result) {
-                            for (int i = 0; i < 3; i++) {
-                                result[i] = Math.min(src[i], dst[i]);
-                            }
-                            result[3] = Math.min(255, src[3] + dst[3]);
-                            return result;
-                        }
-                    };
-                case LIGHTEN:
-                    return new Blender() {
-                        @Override
-                        public int[] blend(int[] src, int[] dst, int[] result) {
-                            for (int i = 0; i < 3; i++) {
-                                result[i] = Math.max(src[i], dst[i]);
-                            }
-                            result[3] = Math.min(255, src[3] + dst[3]);
-                            return result;
-                        }
-                    };
-                case MULTIPLY:
-                    return new Blender() {
-                        @Override
-                        public int[] blend(int[] src, int[] dst, int[] result) {
-                            for (int i = 0; i < 3; i++) {
-                                result[i] = (src[i] * dst[i]) >> 8;
-                            }
-                            result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
-                            return result;
-                        }
-                    };
-                case OVERLAY:
-                    return new Blender() {
-                        @Override
-                        public int[] blend(int[] src, int[] dst, int[] result) {
-                            for (int i = 0; i < 3; i++) {
-                                result[i] = dst[i] < 128 ? dst[i] * src[i] >> 7 :
-                                    255 - ((255 - dst[i]) * (255 - src[i]) >> 7);
-                            }
-                            result[3] = Math.min(255, src[3] + dst[3]);
-                            return result;
-                        }
-                    };
-                case SCREEN:
-                    return new Blender() {
-                        @Override
-                        public int[] blend(int[] src, int[] dst, int[] result) {
-                            result[0] = 255 - ((255 - src[0]) * (255 - dst[0]) >> 8);
-                            result[1] = 255 - ((255 - src[1]) * (255 - dst[1]) >> 8);
-                            result[2] = 255 - ((255 - src[2]) * (255 - dst[2]) >> 8);
-                            result[3] = Math.min(255, src[3] + dst[3]);
-                            return result;
-                        }
-                    };
-                default:
-                    assert false : "Blender not implement for " + composite.getMode().name();
-
-                    // Ignore the blend
-                    return new Blender() {
-                        @Override
-                        public int[] blend(int[] src, int[] dst, int[] result) {
-                            result[0] = dst[0];
-                            result[1] = dst[1];
-                            result[2] = dst[2];
-                            result[3] = dst[3];
-                            return result;
-                        }
-                    };
-            }
-        }
-    }
-}
diff --git a/bridge/src/android/graphics/BlendModeColorFilter_Delegate.java b/bridge/src/android/graphics/BlendModeColorFilter_Delegate.java
deleted file mode 100644
index fc25903..0000000
--- a/bridge/src/android/graphics/BlendModeColorFilter_Delegate.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.BlendModeColorFilter
- *
- * Through the layoutlib_create tool, the original native methods of BlendModeColorFilter have
- * been replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original PorterDuffColorFilter class.
- *
- * Because this extends {@link ColorFilter_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the Shader classes will be added to the manager
- * owned by {@link ColorFilter_Delegate}.
- *
- * @see ColorFilter_Delegate
- *
- */
-public class BlendModeColorFilter_Delegate extends ColorFilter_Delegate {
-
-    @Override
-    public String getSupportMessage() {
-        return "BlendMode Color Filters are not supported.";
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long native_CreateBlendModeFilter(int srcColor, int blendmode) {
-        return PorterDuffColorFilter_Delegate.native_CreateBlendModeFilter(srcColor, blendmode);
-    }
-}
diff --git a/bridge/src/android/graphics/BlurMaskFilter_Delegate.java b/bridge/src/android/graphics/BlurMaskFilter_Delegate.java
deleted file mode 100644
index d2569c7..0000000
--- a/bridge/src/android/graphics/BlurMaskFilter_Delegate.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.BlurMaskFilter
- *
- * Through the layoutlib_create tool, the original native methods of BlurMaskFilter have
- * been replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original BlurMaskFilter class.
- *
- * Because this extends {@link MaskFilter_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the Shader classes will be added to the manager
- * owned by {@link MaskFilter_Delegate}.
- *
- * @see MaskFilter_Delegate
- *
- */
-public class BlurMaskFilter_Delegate extends MaskFilter_Delegate {
-
-    // ---- delegate data ----
-
-    // ---- Public Helper methods ----
-
-    @Override
-    public boolean isSupported() {
-        return false;
-    }
-
-    @Override
-    public String getSupportMessage() {
-        return "Blur Mask Filters are not supported.";
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeConstructor(float radius, int style) {
-        BlurMaskFilter_Delegate newDelegate = new BlurMaskFilter_Delegate();
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/Canvas_Delegate.java b/bridge/src/android/graphics/Canvas_Delegate.java
index 19e88b6..8ce28c9 100644
--- a/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/bridge/src/android/graphics/Canvas_Delegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -16,474 +16,21 @@
 
 package android.graphics;
 
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.GcSnapshot;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
-import android.graphics.Bitmap.Config;
-
-import java.awt.Graphics2D;
-import java.awt.Rectangle;
-import java.awt.geom.AffineTransform;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-
-/**
- * Delegate implementing the native methods of android.graphics.Canvas
- *
- * Through the layoutlib_create tool, the original native methods of Canvas have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Canvas class.
- *
- * @see DelegateManager
- *
- */
-public final class Canvas_Delegate extends BaseCanvas_Delegate {
-
-    // ---- delegate manager ----
-    private static long sFinalizer = -1;
-
-    private DrawFilter_Delegate mDrawFilter = null;
-
-    // ---- Public Helper methods ----
-
-    /**
-     * Returns the native delegate associated to a given {@link Canvas} object.
-     */
-    public static Canvas_Delegate getDelegate(Canvas canvas) {
-        return (Canvas_Delegate) sManager.getDelegate(canvas.getNativeCanvasWrapper());
-    }
-
-    /**
-     * Returns the native delegate associated to a given an int referencing a {@link Canvas} object.
-     */
-    public static Canvas_Delegate getDelegate(long native_canvas) {
-        return (Canvas_Delegate) sManager.getDelegate(native_canvas);
-    }
-
-    /**
-     * Returns the current {@link Graphics2D} used to draw.
-     */
-    public GcSnapshot getSnapshot() {
-        return mSnapshot;
-    }
-
-    /**
-     * Returns the {@link DrawFilter} delegate or null if none have been set.
-     *
-     * @return the delegate or null.
-     */
-    public DrawFilter_Delegate getDrawFilter() {
-        return mDrawFilter;
-    }
-
-    // ---- native methods ----
-
+public class Canvas_Delegate {
     @LayoutlibDelegate
-    /*package*/ static void nFreeCaches() {
-        // nothing to be done here.
+    static boolean getClipBounds(Canvas thisCanvas, Rect bounds) {
+        // Despite allowing a null argument, Canvas.getClipBounds(Rect) causes a native crash
+        // in Android. In this case, throw an exception to avoid a layoutlib native crash.
+        if (bounds == null) {
+            throw new RuntimeException("Null bounds causes a crash in Android");
+        }
+        return thisCanvas.getClipBounds_Original(bounds);
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nFreeTextLayoutCaches() {
-        // nothing to be done here yet.
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nInitRaster(long bitmapHandle) {
-        if (bitmapHandle > 0) {
-            // get the Bitmap from the int
-            Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle);
-
-            // create a new Canvas_Delegate with the given bitmap and return its new native int.
-            Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate);
-
-            return sManager.addNewDelegate(newDelegate);
-        }
-
-        // create a new Canvas_Delegate and return its new native int.
-        Canvas_Delegate newDelegate = new Canvas_Delegate();
-
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    @LayoutlibDelegate
-    public static void nSetBitmap(long canvas, long bitmapHandle) {
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
-        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle);
-        if (canvasDelegate == null || bitmapDelegate == null) {
-            return;
-        }
-        canvasDelegate.mBitmap = bitmapDelegate;
-        canvasDelegate.mSnapshot = GcSnapshot.createDefaultSnapshot(bitmapDelegate);
-    }
-
-    @LayoutlibDelegate
-    public static boolean nIsOpaque(long nativeCanvas) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return false;
-        }
-
-        return canvasDelegate.mBitmap.getConfig() == Config.RGB_565;
-    }
-
-    @LayoutlibDelegate
-    public static int nGetWidth(long nativeCanvas) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return 0;
-        }
-
-        return canvasDelegate.mBitmap.getImage().getWidth();
-    }
-
-    @LayoutlibDelegate
-    public static int nGetHeight(long nativeCanvas) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return 0;
-        }
-
-        return canvasDelegate.mBitmap.getImage().getHeight();
-    }
-
-    @LayoutlibDelegate
-    public static int nSave(long nativeCanvas, int saveFlags) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return 0;
-        }
-
-        return canvasDelegate.save(saveFlags);
-    }
-
-    @LayoutlibDelegate
-    public static int nSaveLayer(long nativeCanvas, float l,
-                                               float t, float r, float b,
-                                               long paint, int layerFlags) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return 0;
-        }
-
-        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
-
-        return canvasDelegate.saveLayer(new RectF(l, t, r, b),
-                paintDelegate, layerFlags);
-    }
-
-    @LayoutlibDelegate
-    public static int nSaveUnclippedLayer(long nativeCanvas, int l, int t, int r, int b) {
-        return nSaveLayer(nativeCanvas, l, t, r, b, 0, 0);
-    }
-
-    @LayoutlibDelegate
-    public static int nSaveLayerAlpha(long nativeCanvas, float l,
-                                                    float t, float r, float b,
-                                                    int alpha, int layerFlags) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return 0;
-        }
-
-        return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags);
-    }
-
-    @LayoutlibDelegate
-    public static void nRestoreUnclippedLayer(long nativeCanvas, int saveCount,
-            long nativePaint) {
-        nRestoreToCount(nativeCanvas, saveCount);
-    }
-
-    @LayoutlibDelegate
-    public static boolean nRestore(long nativeCanvas) {
-        // FIXME: implement throwOnUnderflow.
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return false;
-        }
-
-        canvasDelegate.restore();
-        return true;
-    }
-
-    @LayoutlibDelegate
-    public static void nRestoreToCount(long nativeCanvas, int saveCount) {
-        // FIXME: implement throwOnUnderflow.
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        canvasDelegate.restoreTo(saveCount);
-    }
-
-    @LayoutlibDelegate
-    public static int nGetSaveCount(long nativeCanvas) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return 0;
-        }
-
-        return canvasDelegate.getSnapshot().size();
-    }
-
-    @LayoutlibDelegate
-   public static void nTranslate(long nativeCanvas, float dx, float dy) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        canvasDelegate.getSnapshot().translate(dx, dy);
-    }
-
-    @LayoutlibDelegate
-       public static void nScale(long nativeCanvas, float sx, float sy) {
-            // get the delegate from the native int.
-            Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-            if (canvasDelegate == null) {
-                return;
-            }
-
-            canvasDelegate.getSnapshot().scale(sx, sy);
-        }
-
-    @LayoutlibDelegate
-    public static void nRotate(long nativeCanvas, float degrees) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees));
-    }
-
-    @LayoutlibDelegate
-   public static void nSkew(long nativeCanvas, float kx, float ky) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        // get the current top graphics2D object.
-        GcSnapshot g = canvasDelegate.getSnapshot();
-
-        // get its current matrix
-        AffineTransform currentTx = g.getTransform();
-        // get the AffineTransform for the given skew.
-        float[] mtx = Matrix_Delegate.getSkew(kx, ky);
-        AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx);
-
-        // combine them so that the given matrix is applied after.
-        currentTx.preConcatenate(matrixTx);
-
-        // give it to the graphics2D as a new matrix replacing all previous transform
-        g.setTransform(currentTx);
-    }
-
-    @LayoutlibDelegate
-    public static void nConcat(long nCanvas, long nMatrix) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
-        if (matrixDelegate == null) {
-            return;
-        }
-
-        // get the current top graphics2D object.
-        GcSnapshot snapshot = canvasDelegate.getSnapshot();
-
-        // get its current matrix
-        AffineTransform currentTx = snapshot.getTransform();
-        // get the AffineTransform of the given matrix
-        AffineTransform matrixTx = matrixDelegate.getAffineTransform();
-
-        // combine them so that the given matrix is applied after.
-        currentTx.concatenate(matrixTx);
-
-        // give it to the graphics2D as a new matrix replacing all previous transform
-        snapshot.setTransform(currentTx);
-    }
-
-    @LayoutlibDelegate
-    public static void nSetMatrix(long nCanvas, long nMatrix) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
-        if (matrixDelegate == null) {
-            return;
-        }
-
-        // get the current top graphics2D object.
-        GcSnapshot snapshot = canvasDelegate.getSnapshot();
-
-        // get the AffineTransform of the given matrix
-        AffineTransform matrixTx = matrixDelegate.getAffineTransform();
-
-        // give it to the graphics2D as a new matrix replacing all previous transform
-        snapshot.setTransform(matrixTx);
-
-        if (matrixDelegate.hasPerspective()) {
-            assert false;
-            Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_AFFINE,
-                    "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " +
-                    "supports affine transformations.", null, null, null /*data*/);
-        }
-    }
-
-    @LayoutlibDelegate
-    public static boolean nClipRect(long nCanvas,
-                                                  float left, float top,
-                                                  float right, float bottom,
-                                                  int regionOp) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
-        if (canvasDelegate == null) {
-            return false;
-        }
-
-        return canvasDelegate.clipRect(left, top, right, bottom, regionOp);
-    }
-
-    @LayoutlibDelegate
-    public static boolean nClipPath(long nativeCanvas,
-                                                  long nativePath,
-                                                  int regionOp) {
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return true;
-        }
-
-        Path_Delegate pathDelegate = Path_Delegate.getDelegate(nativePath);
-        if (pathDelegate == null) {
-            return true;
-        }
-
-        return canvasDelegate.mSnapshot.clip(pathDelegate.getJavaShape(), regionOp);
-    }
-
-    @LayoutlibDelegate
-    public static void nSetDrawFilter(long nativeCanvas, long nativeFilter) {
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        canvasDelegate.mDrawFilter = DrawFilter_Delegate.getDelegate(nativeFilter);
-
-        if (canvasDelegate.mDrawFilter != null && !canvasDelegate.mDrawFilter.isSupported()) {
-            Bridge.getLog().fidelityWarning(ILayoutLog.TAG_DRAWFILTER,
-                    canvasDelegate.mDrawFilter.getSupportMessage(), null, null, null /*data*/);
-        }
-    }
-
-    @LayoutlibDelegate
-    public static boolean nGetClipBounds(long nativeCanvas,
-                                                       Rect bounds) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return false;
-        }
-
-        Rectangle rect = canvasDelegate.getSnapshot().getClip().getBounds();
-        if (rect != null && !rect.isEmpty()) {
-            bounds.left = rect.x;
-            bounds.top = rect.y;
-            bounds.right = rect.x + rect.width;
-            bounds.bottom = rect.y + rect.height;
-            return true;
-        }
-
-        return false;
-    }
-
-    @LayoutlibDelegate
-    public static void nGetMatrix(long canvas, long matrix) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix);
-        if (matrixDelegate == null) {
-            return;
-        }
-
-        AffineTransform transform = canvasDelegate.getSnapshot().getTransform();
-        matrixDelegate.set(Matrix_Delegate.makeValues(transform));
-    }
-
-    @LayoutlibDelegate
-    public static boolean nQuickReject(long nativeCanvas, long path) {
-        // FIXME properly implement quickReject
-        return false;
-    }
-
-    @LayoutlibDelegate
-    public static boolean nQuickReject(long nativeCanvas,
-                                                     float left, float top,
-                                                     float right, float bottom) {
-        // FIXME properly implement quickReject
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nGetNativeFinalizer() {
-        synchronized (Canvas_Delegate.class) {
-            if (sFinalizer == -1) {
-                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(nativePtr -> {
-                    Canvas_Delegate delegate = Canvas_Delegate.getDelegate(nativePtr);
-                    if (delegate != null) {
-                        delegate.dispose();
-                    }
-                    sManager.removeJavaReferenceFor(nativePtr);
-                });
-            }
-        }
-        return sFinalizer;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetCompatibilityVersion(int apiLevel) {
-        // Unsupported by layoutlib, do nothing
-    }
-
-    private Canvas_Delegate(Bitmap_Delegate bitmap) {
-        super(bitmap);
-    }
-
-    private Canvas_Delegate() {
-        super();
+    static Rect getClipBounds(Canvas thisCanvas) {
+        return thisCanvas.getClipBounds_Original();
     }
 }
-
diff --git a/bridge/src/android/graphics/ColorFilter_Delegate.java b/bridge/src/android/graphics/ColorFilter_Delegate.java
deleted file mode 100644
index 84424bc..0000000
--- a/bridge/src/android/graphics/ColorFilter_Delegate.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Graphics2D;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.ColorFilter
- *
- * Through the layoutlib_create tool, the original native methods of ColorFilter have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original ColorFilter class.
- *
- * This also serve as a base class for all ColorFilter delegate classes.
- *
- * @see DelegateManager
- *
- */
-public abstract class ColorFilter_Delegate {
-
-    // ---- delegate manager ----
-    protected static final DelegateManager<ColorFilter_Delegate> sManager =
-            new DelegateManager<ColorFilter_Delegate>(ColorFilter_Delegate.class);
-    private static long sFinalizer = -1;
-
-    // ---- delegate helper data ----
-
-    // ---- delegate data ----
-
-    // ---- Public Helper methods ----
-
-    public static ColorFilter_Delegate getDelegate(long nativeShader) {
-        return sManager.getDelegate(nativeShader);
-    }
-
-    public abstract String getSupportMessage();
-
-    public boolean isSupported() {
-        return false;
-    }
-
-    public void applyFilter(Graphics2D g, int width, int height) {
-        // This should never be called directly. If supported, the sub class should override this.
-        assert false;
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeGetFinalizer() {
-        synchronized (ColorFilter_Delegate.class) {
-            if (sFinalizer == -1) {
-                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
-                        sManager::removeJavaReferenceFor);
-            }
-        }
-        return sFinalizer;
-    }
-
-    // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java b/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
deleted file mode 100644
index 6739484..0000000
--- a/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.ColorMatrixColorFilter
- *
- * Through the layoutlib_create tool, the original native methods of ColorMatrixColorFilter have
- * been replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original ColorMatrixColorFilter class.
- *
- * Because this extends {@link ColorFilter_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the Shader classes will be added to the manager
- * owned by {@link ColorFilter_Delegate}.
- *
- * @see ColorFilter_Delegate
- *
- */
-public class ColorMatrixColorFilter_Delegate extends ColorFilter_Delegate {
-
-    // ---- delegate data ----
-
-    // ---- Public Helper methods ----
-
-    @Override
-    public String getSupportMessage() {
-        return "ColorMatrix Color Filters are not supported.";
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeColorMatrixFilter(float[] array) {
-        ColorMatrixColorFilter_Delegate newDelegate = new ColorMatrixColorFilter_Delegate();
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/ColorSpace_Rgb_Delegate.java b/bridge/src/android/graphics/ColorSpace_Rgb_Delegate.java
deleted file mode 100644
index 3637055..0000000
--- a/bridge/src/android/graphics/ColorSpace_Rgb_Delegate.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.ColorSpace
- *
- * Through the layoutlib_create tool, the original native methods of ColorSpace have been replaced
- * by calls to methods of the same name in this delegate class.
- */
-public class ColorSpace_Rgb_Delegate {
-
-    // ---- delegate manager ----
-    private static final DelegateManager<ColorSpace_Rgb_Delegate> sManager =
-            new DelegateManager<>(ColorSpace_Rgb_Delegate.class);
-    private static long sFinalizer = -1;
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeGetNativeFinalizer() {
-        synchronized (ColorSpace_Rgb_Delegate.class) {
-            if (sFinalizer == -1) {
-                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
-            }
-        }
-        return sFinalizer;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreate(float a, float b, float c, float d,
-            float e, float f, float g, float[] xyz) {
-        // Layoutlib does not support color spaces, but a native object is required
-        // for ColorSpace$Rgb. This creates an empty delegate for it.
-        return sManager.addNewDelegate(new ColorSpace_Rgb_Delegate());
-    }
-}
diff --git a/bridge/src/android/graphics/Color_Delegate.java b/bridge/src/android/graphics/Color_Delegate.java
deleted file mode 100644
index afd24f0..0000000
--- a/bridge/src/android/graphics/Color_Delegate.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2018 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.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.Color
- *
- * Through the layoutlib_create tool, the original native methods of Color have been replaced
- * by calls to methods of the same name in this delegate class.
- */
-public class Color_Delegate {
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeRGBToHSV(int red, int greed, int blue, float hsv[]) {
-        java.awt.Color.RGBtoHSB(red, greed, blue, hsv);
-        hsv[0] = hsv[0] * 360;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nativeHSVToColor(int alpha, float hsv[]) {
-        java.awt.Color rgb = new java.awt.Color(java.awt.Color.HSBtoRGB(hsv[0] / 360, pin(hsv[1]), pin(hsv[2])));
-        return Color.argb(alpha, rgb.getRed(), rgb.getGreen(), rgb.getBlue());
-    }
-
-    private static float pin(float value) {
-        return Math.max(0, Math.min(1, value));
-    }
-}
diff --git a/bridge/src/android/graphics/ComposePathEffect_Delegate.java b/bridge/src/android/graphics/ComposePathEffect_Delegate.java
deleted file mode 100644
index bc3df7d..0000000
--- a/bridge/src/android/graphics/ComposePathEffect_Delegate.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Stroke;
-
-/**
- * Delegate implementing the native methods of android.graphics.ComposePathEffect
- *
- * Through the layoutlib_create tool, the original native methods of ComposePathEffect have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original ComposePathEffect class.
- *
- * Because this extends {@link PathEffect_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link PathEffect_Delegate}.
- *
- * @see PathEffect_Delegate
- *
- */
-public class ComposePathEffect_Delegate extends PathEffect_Delegate {
-
-    // ---- delegate data ----
-
-    // ---- Public Helper methods ----
-
-    @Override
-    public Stroke getStroke(Paint_Delegate paint) {
-        // FIXME
-        return null;
-    }
-
-    @Override
-    public boolean isSupported() {
-        return false;
-    }
-
-    @Override
-    public String getSupportMessage() {
-        return "Compose Path Effects are not supported in Layout Preview mode.";
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreate(long outerpe, long innerpe) {
-        ComposePathEffect_Delegate newDelegate = new ComposePathEffect_Delegate();
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/ComposeShader_Delegate.java b/bridge/src/android/graphics/ComposeShader_Delegate.java
deleted file mode 100644
index ab37968..0000000
--- a/bridge/src/android/graphics/ComposeShader_Delegate.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Paint;
-
-/**
- * Delegate implementing the native methods of android.graphics.ComposeShader
- *
- * Through the layoutlib_create tool, the original native methods of ComposeShader have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original ComposeShader class.
- *
- * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
- *
- * @see Shader_Delegate
- *
- */
-public class ComposeShader_Delegate extends Shader_Delegate {
-
-    // ---- delegate data ----
-
-    // ---- Public Helper methods ----
-
-    @Override
-    public Paint getJavaPaint() {
-        // FIXME
-        return null;
-    }
-
-    @Override
-    public boolean isSupported() {
-        return false;
-    }
-
-    @Override
-    public String getSupportMessage() {
-        return "Compose Shaders are not supported in Layout Preview mode.";
-    }
-
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreate(long nativeMatrix, long native_shaderA,
-            long native_shaderB, int native_mode) {
-        // FIXME not supported yet.
-        ComposeShader_Delegate newDelegate = new ComposeShader_Delegate(nativeMatrix);
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-
-    // ---- Private delegate/helper methods ----
-
-    private ComposeShader_Delegate(long nativeMatrix) {
-        super(nativeMatrix);
-    }
-}
diff --git a/bridge/src/android/graphics/CornerPathEffect_Delegate.java b/bridge/src/android/graphics/CornerPathEffect_Delegate.java
deleted file mode 100644
index 73745c3..0000000
--- a/bridge/src/android/graphics/CornerPathEffect_Delegate.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Stroke;
-
-/**
- * Delegate implementing the native methods of android.graphics.CornerPathEffect
- *
- * Through the layoutlib_create tool, the original native methods of CornerPathEffect have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original CornerPathEffect class.
- *
- * Because this extends {@link PathEffect_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link PathEffect_Delegate}.
- *
- * @see PathEffect_Delegate
- *
- */
-public class CornerPathEffect_Delegate extends PathEffect_Delegate {
-
-    // ---- delegate data ----
-
-    // ---- Public Helper methods ----
-
-    @Override
-    public Stroke getStroke(Paint_Delegate paint) {
-        // FIXME
-        return null;
-    }
-
-    @Override
-    public boolean isSupported() {
-        return false;
-    }
-
-    @Override
-    public String getSupportMessage() {
-        return "Corner Path Effects are not supported in Layout Preview mode.";
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreate(float radius) {
-        CornerPathEffect_Delegate newDelegate = new CornerPathEffect_Delegate();
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/DashPathEffect_Delegate.java b/bridge/src/android/graphics/DashPathEffect_Delegate.java
deleted file mode 100644
index 881afde..0000000
--- a/bridge/src/android/graphics/DashPathEffect_Delegate.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.BasicStroke;
-import java.awt.Stroke;
-
-/**
- * Delegate implementing the native methods of android.graphics.DashPathEffect
- *
- * Through the layoutlib_create tool, the original native methods of DashPathEffect have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original DashPathEffect class.
- *
- * Because this extends {@link PathEffect_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the PathEffect classes will be added to the manager owned by
- * {@link PathEffect_Delegate}.
- *
- * @see PathEffect_Delegate
- *
- */
-public final class DashPathEffect_Delegate extends PathEffect_Delegate {
-
-    // ---- delegate data ----
-
-    private final float[] mIntervals;
-    private final float mPhase;
-
-    // ---- Public Helper methods ----
-
-    @Override
-    public Stroke getStroke(Paint_Delegate paint) {
-        return new BasicStroke(
-                paint.getStrokeWidth(),
-                paint.getJavaCap(),
-                paint.getJavaJoin(),
-                paint.getJavaStrokeMiter(),
-                mIntervals,
-                mPhase);
-    }
-
-    @Override
-    public boolean isSupported() {
-        return true;
-    }
-
-    @Override
-    public String getSupportMessage() {
-        // no message since isSupported returns true;
-        return null;
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreate(float intervals[], float phase) {
-        DashPathEffect_Delegate newDelegate = new DashPathEffect_Delegate(intervals, phase);
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    // ---- Private delegate/helper methods ----
-
-    private DashPathEffect_Delegate(float intervals[], float phase) {
-        mIntervals = new float[intervals.length];
-        System.arraycopy(intervals, 0, mIntervals, 0, intervals.length);
-        mPhase = phase;
-    }
-}
-
diff --git a/bridge/src/android/graphics/DiscretePathEffect_Delegate.java b/bridge/src/android/graphics/DiscretePathEffect_Delegate.java
deleted file mode 100644
index 46109f3..0000000
--- a/bridge/src/android/graphics/DiscretePathEffect_Delegate.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Stroke;
-
-/**
- * Delegate implementing the native methods of android.graphics.DiscretePathEffect
- *
- * Through the layoutlib_create tool, the original native methods of DiscretePathEffect have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original DiscretePathEffect class.
- *
- * Because this extends {@link PathEffect_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link PathEffect_Delegate}.
- *
- * @see PathEffect_Delegate
- *
- */
-public class DiscretePathEffect_Delegate extends PathEffect_Delegate {
-
-    // ---- delegate data ----
-
-    // ---- Public Helper methods ----
-
-    @Override
-    public Stroke getStroke(Paint_Delegate paint) {
-        // FIXME
-        return null;
-    }
-
-    @Override
-    public boolean isSupported() {
-        return false;
-    }
-
-    @Override
-    public String getSupportMessage() {
-        return "Discrete Path Effects are not supported in Layout Preview mode.";
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreate(float length, float deviation) {
-        DiscretePathEffect_Delegate newDelegate = new DiscretePathEffect_Delegate();
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/DrawFilter_Delegate.java b/bridge/src/android/graphics/DrawFilter_Delegate.java
deleted file mode 100644
index 2e10740..0000000
--- a/bridge/src/android/graphics/DrawFilter_Delegate.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.DrawFilter
- *
- * Through the layoutlib_create tool, the original native methods of DrawFilter have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original DrawFilter class.
- *
- * This also serve as a base class for all DrawFilter delegate classes.
- *
- * @see DelegateManager
- *
- */
-public abstract class DrawFilter_Delegate {
-
-    // ---- delegate manager ----
-    protected static final DelegateManager<DrawFilter_Delegate> sManager =
-            new DelegateManager<DrawFilter_Delegate>(DrawFilter_Delegate.class);
-
-    // ---- delegate helper data ----
-
-    // ---- delegate data ----
-
-    // ---- Public Helper methods ----
-
-    public static DrawFilter_Delegate getDelegate(long nativeDrawFilter) {
-        return sManager.getDelegate(nativeDrawFilter);
-    }
-
-    public abstract boolean isSupported();
-    public abstract String getSupportMessage();
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeDestructor(long nativeDrawFilter) {
-        sManager.removeJavaReferenceFor(nativeDrawFilter);
-    }
-
-    // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java b/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java
deleted file mode 100644
index e5040cc..0000000
--- a/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.EmbossMaskFilter
- *
- * Through the layoutlib_create tool, the original native methods of EmbossMaskFilter have
- * been replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original EmbossMaskFilter class.
- *
- * Because this extends {@link MaskFilter_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the Shader classes will be added to the manager
- * owned by {@link MaskFilter_Delegate}.
- *
- * @see MaskFilter_Delegate
- *
- */
-public class EmbossMaskFilter_Delegate extends MaskFilter_Delegate {
-
-    // ---- delegate data ----
-
-    // ---- Public Helper methods ----
-
-    @Override
-    public boolean isSupported() {
-        return false;
-    }
-
-    @Override
-    public String getSupportMessage() {
-        return "Emboss Mask Filters are not supported.";
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeConstructor(float[] direction, float ambient,
-            float specular, float blurRadius) {
-        EmbossMaskFilter_Delegate newDelegate = new EmbossMaskFilter_Delegate();
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/FontFamily_Delegate.java b/bridge/src/android/graphics/FontFamily_Delegate.java
deleted file mode 100644
index a452aae..0000000
--- a/bridge/src/android/graphics/FontFamily_Delegate.java
+++ /dev/null
@@ -1,467 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.fonts.FontVariationAxis;
-
-import java.awt.Font;
-import java.awt.FontFormatException;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.logging.Logger;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-import static android.graphics.Typeface.RESOLVE_BY_FONT_TABLE;
-import static android.graphics.Typeface_Delegate.SYSTEM_FONTS;
-
-/**
- * Delegate implementing the native methods of android.graphics.FontFamily
- *
- * Through the layoutlib_create tool, the original native methods of FontFamily have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original FontFamily class.
- *
- * @see DelegateManager
- */
-public class FontFamily_Delegate {
-
-    public static final int DEFAULT_FONT_WEIGHT = 400;
-    public static final int BOLD_FONT_WEIGHT_DELTA = 300;
-    public static final int BOLD_FONT_WEIGHT = 700;
-
-    private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
-    private static final String FN_ALL_FONTS_LIST = "fontsInSdk.txt";
-    private static final String EXTENSION_OTF = ".otf";
-
-    private static final int CACHE_SIZE = 10;
-    // The cache has a drawback that if the font file changed after the font object was created,
-    // we will not update it.
-    private static final Map<String, FontInfo> sCache =
-            new LinkedHashMap<String, FontInfo>(CACHE_SIZE) {
-        @Override
-        protected boolean removeEldestEntry(Map.Entry<String, FontInfo> eldest) {
-            return size() > CACHE_SIZE;
-        }
-
-        @Override
-        public FontInfo put(String key, FontInfo value) {
-            // renew this entry.
-            FontInfo removed = remove(key);
-            super.put(key, value);
-            return removed;
-        }
-    };
-
-    /**
-     * A class associating {@link Font} with its metadata.
-     */
-    public static final class FontInfo {
-        @Nullable
-        public Font mFont;
-        public int mWeight;
-        public boolean mIsItalic;
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-            FontInfo fontInfo = (FontInfo) o;
-            return mWeight == fontInfo.mWeight && mIsItalic == fontInfo.mIsItalic;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mWeight, mIsItalic);
-        }
-
-        @Override
-        public String toString() {
-            return "FontInfo{" + "mWeight=" + mWeight + ", mIsItalic=" + mIsItalic + '}';
-        }
-    }
-
-    // ---- delegate manager ----
-    private static final DelegateManager<FontFamily_Delegate> sManager =
-            new DelegateManager<FontFamily_Delegate>(FontFamily_Delegate.class);
-    private static long sFamilyFinalizer = -1;
-
-    // ---- delegate helper data ----
-    private static String sFontLocation;
-    private static final List<FontFamily_Delegate> sPostInitDelegate = new
-            ArrayList<FontFamily_Delegate>();
-    private static Set<String> SDK_FONTS;
-
-
-    // ---- delegate data ----
-
-    // Order does not really matter but we use a LinkedHashMap to get reproducible results across
-    // render calls
-    private Map<FontInfo, Font> mFonts = new LinkedHashMap<>();
-
-    /**
-     * The variant of the Font Family - compact or elegant.
-     * <p/>
-     * 0 is unspecified, 1 is compact and 2 is elegant. This needs to be kept in sync with values in
-     * android.graphics.FontFamily
-     *
-     * @see Paint#setElegantTextHeight(boolean)
-     */
-    private FontVariant mVariant;
-    // List of runnables to process fonts after sFontLoader is initialized.
-    private List<Runnable> mPostInitRunnables = new ArrayList<Runnable>();
-    /** @see #isValid() */
-    private boolean mValid = false;
-
-
-    // ---- Public helper class ----
-
-    public enum FontVariant {
-        // The order needs to be kept in sync with android.graphics.FontFamily.
-        NONE, COMPACT, ELEGANT
-    }
-
-    // ---- Public Helper methods ----
-
-    public static FontFamily_Delegate getDelegate(long nativeFontFamily) {
-        return sManager.getDelegate(nativeFontFamily);
-    }
-
-    public static synchronized void setFontLocation(String fontLocation) {
-        sFontLocation = fontLocation;
-        // init list of bundled fonts.
-        File allFonts = new File(fontLocation, FN_ALL_FONTS_LIST);
-        // Current number of fonts is 103. Use the next round number to leave scope for more fonts
-        // in the future.
-        Set<String> allFontsList = new HashSet<>(128);
-        Scanner scanner = null;
-        try {
-            scanner = new Scanner(allFonts);
-            while (scanner.hasNext()) {
-                String name = scanner.next();
-                // Skip font configuration files.
-                if (!name.endsWith(".xml")) {
-                    allFontsList.add(name);
-                }
-            }
-        } catch (FileNotFoundException e) {
-            Bridge.getLog().error(ILayoutLog.TAG_BROKEN,
-                    "Unable to load the list of fonts. Try re-installing the SDK Platform from the SDK Manager.",
-                    e, null, null);
-        } finally {
-            if (scanner != null) {
-                scanner.close();
-            }
-        }
-        SDK_FONTS = Collections.unmodifiableSet(allFontsList);
-        for (FontFamily_Delegate fontFamily : sPostInitDelegate) {
-            fontFamily.init();
-        }
-        sPostInitDelegate.clear();
-    }
-
-    @Nullable
-    public Font getFont(int desiredWeight, boolean isItalic) {
-        FontInfo desiredStyle = new FontInfo();
-        desiredStyle.mWeight = desiredWeight;
-        desiredStyle.mIsItalic = isItalic;
-
-        Font cachedFont = mFonts.get(desiredStyle);
-        if (cachedFont != null) {
-            return cachedFont;
-        }
-
-        FontInfo bestFont = null;
-
-        if (mFonts.size() == 1) {
-            // No need to compute the match since we only have one candidate
-            bestFont = mFonts.keySet().iterator().next();
-        } else {
-            int bestMatch = Integer.MAX_VALUE;
-
-            for (FontInfo font : mFonts.keySet()) {
-                int match = computeMatch(font, desiredStyle);
-                if (match < bestMatch) {
-                    bestMatch = match;
-                    bestFont = font;
-                    if (bestMatch == 0) {
-                        break;
-                    }
-                }
-            }
-        }
-
-        if (bestFont == null) {
-            return null;
-        }
-
-
-        // Derive the font as required and add it to the list of Fonts.
-        deriveFont(bestFont, desiredStyle);
-        addFont(desiredStyle);
-        return desiredStyle.mFont;
-    }
-
-    public FontVariant getVariant() {
-        return mVariant;
-    }
-
-    /**
-     * Returns if the FontFamily should contain any fonts. If this returns true and
-     * {@link #getFont(int, boolean)} returns an empty list, it means that an error occurred while
-     * loading the fonts. However, some fonts are deliberately skipped, for example they are not
-     * bundled with the SDK. In such a case, this method returns false.
-     */
-    public boolean isValid() {
-        return mValid;
-    }
-
-    private static Font loadFont(String path) {
-        if (path.startsWith(SYSTEM_FONTS) ) {
-            String relativePath = path.substring(SYSTEM_FONTS.length());
-            File f = new File(sFontLocation, relativePath);
-
-            try {
-                return Font.createFont(Font.TRUETYPE_FONT, f);
-            } catch (Exception e) {
-                if (path.endsWith(EXTENSION_OTF) && e instanceof FontFormatException) {
-                    // If we aren't able to load an Open Type font, don't log a warning just yet.
-                    // We wait for a case where font is being used. Only then we try to log the
-                    // warning.
-                    return null;
-                }
-                Bridge.getLog().fidelityWarning(ILayoutLog.TAG_BROKEN,
-                        String.format("Unable to load font %1$s", relativePath),
-                        e, null, null);
-            }
-        } else {
-            Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                    "Only platform fonts located in " + SYSTEM_FONTS + "can be loaded.",
-                    null, null, null);
-        }
-
-        return null;
-    }
-
-    @Nullable
-    public static String getFontLocation() {
-        return sFontLocation;
-    }
-
-    // ---- delegate methods ----
-    @LayoutlibDelegate
-    /*package*/ static boolean addFont(FontFamily thisFontFamily, String path, int ttcIndex,
-            FontVariationAxis[] axes, int weight, int italic) {
-        if (thisFontFamily.mBuilderPtr == 0) {
-            assert false : "Unable to call addFont after freezing.";
-            return false;
-        }
-        final FontFamily_Delegate delegate = getDelegate(thisFontFamily.mBuilderPtr);
-        return delegate != null && delegate.addFont(path, ttcIndex, weight, italic);
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nInitBuilder(String lang, int variant) {
-        // TODO: support lang. This is required for japanese locale.
-        FontFamily_Delegate delegate = new FontFamily_Delegate();
-        // variant can be 0, 1 or 2.
-        assert variant < 3;
-        delegate.mVariant = FontVariant.values()[variant];
-        if (sFontLocation != null) {
-            delegate.init();
-        } else {
-            sPostInitDelegate.add(delegate);
-        }
-        return sManager.addNewDelegate(delegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nCreateFamily(long builderPtr) {
-        return builderPtr;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nGetFamilyReleaseFunc() {
-        synchronized (FontFamily_Delegate.class) {
-            if (sFamilyFinalizer == -1) {
-                sFamilyFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
-                        sManager::removeJavaReferenceFor);
-            }
-        }
-        return sFamilyFinalizer;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex,
-            int weight, int isItalic) {
-        assert false : "The only client of this method has been overridden.";
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font,
-            int ttcIndex, int weight, int isItalic) {
-        assert false : "The only client of this method has been overridden.";
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nAddAxisValue(long builderPtr, int tag, float value) {
-        assert false : "The only client of this method has been overridden.";
-    }
-
-    static boolean addFont(long builderPtr, final String path, final int weight,
-            final boolean isItalic) {
-        final FontFamily_Delegate delegate = getDelegate(builderPtr);
-        int italic = isItalic ? 1 : 0;
-        if (delegate != null) {
-            if (sFontLocation == null) {
-                delegate.mPostInitRunnables.add(() -> delegate.addFont(path, weight, italic));
-                return true;
-            }
-            return delegate.addFont(path, weight, italic);
-        }
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nGetBuilderReleaseFunc() {
-        // Layoutlib uses the same reference for the builder and the font family,
-        // so it should not release that reference at the builder stage.
-        return -1;
-    }
-
-    // ---- private helper methods ----
-
-    private void init() {
-        for (Runnable postInitRunnable : mPostInitRunnables) {
-            postInitRunnable.run();
-        }
-        mPostInitRunnables = null;
-    }
-
-    private boolean addFont(final String path, int ttcIndex, int weight, int italic) {
-        // FIXME: support ttc fonts. Hack JRE??
-        if (sFontLocation == null) {
-            mPostInitRunnables.add(() -> addFont(path, weight, italic));
-            return true;
-        }
-        return addFont(path, weight, italic);
-    }
-
-     private boolean addFont(@NonNull String path) {
-         return addFont(path, DEFAULT_FONT_WEIGHT, path.endsWith(FONT_SUFFIX_ITALIC) ? 1 : RESOLVE_BY_FONT_TABLE);
-     }
-
-    private boolean addFont(@NonNull String path, int weight, int italic) {
-        if (path.startsWith(SYSTEM_FONTS) &&
-                !SDK_FONTS.contains(path.substring(SYSTEM_FONTS.length()))) {
-            Logger.getLogger(FontFamily_Delegate.class.getSimpleName()).warning("Unable to load font " + path);
-            return mValid = false;
-        }
-        // Set valid to true, even if the font fails to load.
-        mValid = true;
-        Font font = loadFont(path);
-        if (font == null) {
-            return false;
-        }
-        FontInfo fontInfo = new FontInfo();
-        fontInfo.mFont = font;
-        fontInfo.mWeight = weight;
-        fontInfo.mIsItalic = italic == RESOLVE_BY_FONT_TABLE ? font.isItalic() : italic == 1;
-        addFont(fontInfo);
-        return true;
-    }
-
-    private boolean addFont(@NonNull FontInfo fontInfo) {
-        return mFonts.putIfAbsent(fontInfo, fontInfo.mFont) == null;
-    }
-
-    /**
-     * Compute matching metric between two styles - 0 is an exact match.
-     */
-    public static int computeMatch(@NonNull FontInfo font1, @NonNull FontInfo font2) {
-        int score = Math.abs(font1.mWeight / 100 - font2.mWeight / 100);
-        if (font1.mIsItalic != font2.mIsItalic) {
-            score += 2;
-        }
-        return score;
-    }
-
-    /**
-     * Try to derive a font from {@code srcFont} for the style in {@code outFont}.
-     * <p/>
-     * {@code outFont} is updated to reflect the style of the derived font.
-     * @param srcFont the source font
-     * @param outFont contains the desired font style. Updated to contain the derived font and
-     *                its style
-     */
-    public static void deriveFont(@NonNull FontInfo srcFont, @NonNull FontInfo outFont) {
-        int desiredWeight = outFont.mWeight;
-        int srcWeight = srcFont.mWeight;
-        assert srcFont.mFont != null;
-        Font derivedFont = srcFont.mFont;
-        int derivedStyle = 0;
-        // Embolden the font if required.
-        if (desiredWeight >= BOLD_FONT_WEIGHT && desiredWeight - srcWeight > BOLD_FONT_WEIGHT_DELTA / 2) {
-            derivedStyle |= Font.BOLD;
-            srcWeight += BOLD_FONT_WEIGHT_DELTA;
-        }
-        // Italicize the font if required.
-        if (outFont.mIsItalic && !srcFont.mIsItalic) {
-            derivedStyle |= Font.ITALIC;
-        } else if (outFont.mIsItalic != srcFont.mIsItalic) {
-            // The desired font is plain, but the src font is italics. We can't convert it back. So
-            // we update the value to reflect the true style of the font we're deriving.
-            outFont.mIsItalic = srcFont.mIsItalic;
-        }
-
-        if (derivedStyle != 0) {
-            derivedFont = derivedFont.deriveFont(derivedStyle);
-        }
-
-        outFont.mFont = derivedFont;
-        outFont.mWeight = srcWeight;
-        // No need to update mIsItalics, as it's already been handled above.
-    }
-}
diff --git a/bridge/src/android/graphics/Gradient_Delegate.java b/bridge/src/android/graphics/Gradient_Delegate.java
deleted file mode 100644
index 8aed4b4..0000000
--- a/bridge/src/android/graphics/Gradient_Delegate.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import android.graphics.Shader.TileMode;
-
-import java.util.Arrays;
-
-/**
- * Base class for true Gradient shader delegate.
- */
-public abstract class Gradient_Delegate extends Shader_Delegate {
-
-    protected final int[] mColors;
-    protected final float[] mPositions;
-
-    @Override
-    public boolean isSupported() {
-        // all gradient shaders are supported.
-        return true;
-    }
-
-    @Override
-    public String getSupportMessage() {
-        // all gradient shaders are supported, no need for a gradient support
-        return null;
-    }
-
-    /**
-     * Creates the base shader and do some basic test on the parameters.
-     *
-     * @param nativeMatrix reference to the shader's native transformation matrix
-     * @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.
-     */
-    protected Gradient_Delegate(long nativeMatrix, long[] colors, float[] positions) {
-        super(nativeMatrix);
-        assert colors.length >= 2 : "needs >= 2 number of colors";
-
-        if (positions == null) {
-            float spacing = 1.f / (colors.length - 1);
-            positions = new float[colors.length];
-            positions[0] = 0.f;
-            positions[colors.length - 1] = 1.f;
-            for (int i = 1; i < colors.length - 1; i++) {
-                positions[i] = spacing * i;
-            }
-        } else {
-            assert colors.length == positions.length :
-                    "color and position " + "arrays must be of equal length";
-            positions[0] = Math.min(Math.max(0, positions[0]), 1);
-            for (int i = 1; i < positions.length; i++) {
-                positions[i] = Math.min(Math.max(positions[i-1], positions[i]), 1);
-            }
-        }
-
-        mColors = Arrays.stream(colors).mapToInt(Color::toArgb).toArray();
-        mPositions = positions;
-    }
-
-    /**
-     * Base class for (Java) Gradient Paints. This handles computing the gradient colors based
-     * on the color and position lists, as well as the {@link TileMode}
-     *
-     */
-    protected abstract static class GradientPaint implements java.awt.Paint {
-        private final static int GRADIENT_SIZE = 100;
-
-        private final int[] mColors;
-        private final float[] mPositions;
-        private final TileMode mTileMode;
-        private int[] mGradient;
-
-        protected GradientPaint(int[] colors, float[] positions, TileMode tileMode) {
-            mColors = colors;
-            mPositions = positions;
-            mTileMode = tileMode;
-        }
-
-        @Override
-        public int getTransparency() {
-            return java.awt.Paint.TRANSLUCENT;
-        }
-
-        /**
-         * Pre-computes the colors for the gradient. This must be called once before any call
-         * to {@link #getGradientColor(float)}
-         */
-        protected void precomputeGradientColors() {
-            if (mGradient == null) {
-                // actually create an array with an extra size, so that we can really go
-                // from 0 to SIZE (100%), or currentPos in the loop below will never equal 1.0
-                mGradient = new int[GRADIENT_SIZE+1];
-
-                int prevPos = 0;
-                int nextPos = 1;
-                for (int i  = 0 ; i <= GRADIENT_SIZE ; i++) {
-                    // compute current position
-                    float currentPos = (float)i/GRADIENT_SIZE;
-
-                    if (currentPos < mPositions[0]) {
-                        mGradient[i] = mColors[0];
-                        continue;
-                    }
-
-                    while (nextPos < mPositions.length && currentPos >= mPositions[nextPos]) {
-                        prevPos = nextPos++;
-                    }
-
-                    if (nextPos == mPositions.length || currentPos == prevPos) {
-                        mGradient[i] = mColors[prevPos];
-                    } else {
-                        float percent = (currentPos - mPositions[prevPos]) /
-                                (mPositions[nextPos] - mPositions[prevPos]);
-
-                        mGradient[i] = computeColor(mColors[prevPos], mColors[nextPos], percent);
-                    }
-                }
-            }
-        }
-
-        /**
-         * Returns the color based on the position in the gradient.
-         * <var>pos</var> can be anything, even &lt; 0 or &gt; > 1, as the gradient
-         * will use {@link TileMode} value to convert it into a [0,1] value.
-         */
-        protected int getGradientColor(float pos) {
-            if (pos < 0.f) {
-                if (mTileMode != null) {
-                    switch (mTileMode) {
-                        case CLAMP:
-                            pos = 0.f;
-                            break;
-                        case REPEAT:
-                            // remove the integer part to stay in the [0,1] range.
-                            // we also need to invert the value from [-1,0] to [0, 1]
-                            pos = pos - (float)Math.floor(pos);
-                            break;
-                        case MIRROR:
-                            // this is the same as the positive side, just make the value positive
-                            // first.
-                            pos = Math.abs(pos);
-
-                            // get the integer and the decimal part
-                            int intPart = (int)Math.floor(pos);
-                            pos = pos - intPart;
-                            // 0 -> 1 : normal order
-                            // 1 -> 2: mirrored
-                            // etc..
-                            // this means if the intpart is odd we invert
-                            if ((intPart % 2) == 1) {
-                                pos = 1.f - pos;
-                            }
-                            break;
-                    }
-                } else {
-                    pos = 0.0f;
-                }
-            } else if (pos > 1f) {
-                if (mTileMode != null) {
-                    switch (mTileMode) {
-                        case CLAMP:
-                            pos = 1.f;
-                            break;
-                        case REPEAT:
-                            // remove the integer part to stay in the [0,1] range
-                            pos = pos - (float)Math.floor(pos);
-                            break;
-                        case MIRROR:
-                            // get the integer and the decimal part
-                            int intPart = (int)Math.floor(pos);
-                            pos = pos - intPart;
-                            // 0 -> 1 : normal order
-                            // 1 -> 2: mirrored
-                            // etc..
-                            // this means if the intpart is odd we invert
-                            if ((intPart % 2) == 1) {
-                                pos = 1.f - pos;
-                            }
-                            break;
-                    }
-                } else {
-                    pos = 1.0f;
-                }
-            }
-
-            int index = (int)((pos * GRADIENT_SIZE) + .5);
-
-            return mGradient[index];
-        }
-
-        /**
-         * Returns the color between c1, and c2, based on the percent of the distance
-         * between c1 and c2.
-         */
-        private int computeColor(int c1, int c2, float percent) {
-            int a = computeChannel((c1 >> 24) & 0xFF, (c2 >> 24) & 0xFF, percent);
-            int r = computeChannel((c1 >> 16) & 0xFF, (c2 >> 16) & 0xFF, percent);
-            int g = computeChannel((c1 >>  8) & 0xFF, (c2 >>  8) & 0xFF, percent);
-            int b = computeChannel((c1      ) & 0xFF, (c2      ) & 0xFF, percent);
-            return a << 24 | r << 16 | g << 8 | b;
-        }
-
-        /**
-         * Returns the channel value between 2 values based on the percent of the distance between
-         * the 2 values..
-         */
-        private int computeChannel(int c1, int c2, float percent) {
-            return c1 + (int)((percent * (c2-c1)) + .5);
-        }
-    }
-}
diff --git a/bridge/src/android/graphics/HardwareRenderer_ProcessInitializer_Delegate.java b/bridge/src/android/graphics/HardwareRenderer_ProcessInitializer_Delegate.java
new file mode 100644
index 0000000..88c034b
--- /dev/null
+++ b/bridge/src/android/graphics/HardwareRenderer_ProcessInitializer_Delegate.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 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 HardwareRenderer_ProcessInitializer_Delegate {
+    public static void initSched(long renderProxy) {
+        /*
+         * This is done in order to prevent NullPointerException when creating HardwareRenderer in
+         * layoutlib
+         */
+    }
+}
diff --git a/bridge/src/android/graphics/ImageDecoder.java b/bridge/src/android/graphics/ImageDecoder.java
deleted file mode 100644
index eefdb2e..0000000
--- a/bridge/src/android/graphics/ImageDecoder.java
+++ /dev/null
@@ -1,768 +0,0 @@
-/*
- * Copyright (C) 2017 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.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ContentResolver;
-import android.content.res.AssetManager.AssetInputStream;
-import android.content.res.Resources;
-import android.graphics.drawable.AnimatedImageDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.BitmapDrawable;
-import android.net.Uri;
-import android.util.DisplayMetrics;
-import android.util.Size;
-import android.util.TypedValue;
-
-import java.nio.ByteBuffer;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.ArrayIndexOutOfBoundsException;
-import java.lang.AutoCloseable;
-import java.lang.NullPointerException;
-import java.lang.annotation.Retention;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-/**
- *  Class for decoding images as {@link Bitmap}s or {@link Drawable}s.
- */
-public final class ImageDecoder implements AutoCloseable {
-
-    /**
-     *  Source of the encoded image data.
-     */
-    public static abstract class Source {
-        private Source() {}
-
-        /* @hide */
-        @Nullable
-        Resources getResources() { return null; }
-
-        /* @hide */
-        int getDensity() { return Bitmap.DENSITY_NONE; }
-
-        /* @hide */
-        int computeDstDensity() {
-            Resources res = getResources();
-            if (res == null) {
-                return Bitmap.getDefaultDensity();
-            }
-
-            return res.getDisplayMetrics().densityDpi;
-        }
-
-        /* @hide */
-        @NonNull
-        abstract ImageDecoder createImageDecoder() throws IOException;
-    };
-
-    private static class ByteArraySource extends Source {
-        ByteArraySource(@NonNull byte[] data, int offset, int length) {
-            mData = data;
-            mOffset = offset;
-            mLength = length;
-        };
-        private final byte[] mData;
-        private final int    mOffset;
-        private final int    mLength;
-
-        @Override
-        public ImageDecoder createImageDecoder() throws IOException {
-            return new ImageDecoder();
-        }
-    }
-
-    private static class ByteBufferSource extends Source {
-        ByteBufferSource(@NonNull ByteBuffer buffer) {
-            mBuffer = buffer;
-        }
-        private final ByteBuffer mBuffer;
-
-        @Override
-        public ImageDecoder createImageDecoder() throws IOException {
-            return new ImageDecoder();
-        }
-    }
-
-    private static class ContentResolverSource extends Source {
-        ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri) {
-            mResolver = resolver;
-            mUri = uri;
-        }
-
-        private final ContentResolver mResolver;
-        private final Uri mUri;
-
-        @Override
-        public ImageDecoder createImageDecoder() throws IOException {
-            return new ImageDecoder();
-        }
-    }
-
-    /**
-     * For backwards compatibility, this does *not* close the InputStream.
-     */
-    private static class InputStreamSource extends Source {
-        InputStreamSource(Resources res, InputStream is, int inputDensity) {
-            if (is == null) {
-                throw new IllegalArgumentException("The InputStream cannot be null");
-            }
-            mResources = res;
-            mInputStream = is;
-            mInputDensity = res != null ? inputDensity : Bitmap.DENSITY_NONE;
-        }
-
-        final Resources mResources;
-        InputStream mInputStream;
-        final int mInputDensity;
-
-        @Override
-        public Resources getResources() { return mResources; }
-
-        @Override
-        public int getDensity() { return mInputDensity; }
-
-        @Override
-        public ImageDecoder createImageDecoder() throws IOException {
-            return new ImageDecoder();
-        }
-    }
-
-    /**
-     * Takes ownership of the AssetInputStream.
-     *
-     * @hide
-     */
-    public static class AssetInputStreamSource extends Source {
-        public AssetInputStreamSource(@NonNull AssetInputStream ais,
-                @NonNull Resources res, @NonNull TypedValue value) {
-            mAssetInputStream = ais;
-            mResources = res;
-
-            if (value.density == TypedValue.DENSITY_DEFAULT) {
-                mDensity = DisplayMetrics.DENSITY_DEFAULT;
-            } else if (value.density != TypedValue.DENSITY_NONE) {
-                mDensity = value.density;
-            } else {
-                mDensity = Bitmap.DENSITY_NONE;
-            }
-        }
-
-        private AssetInputStream mAssetInputStream;
-        private final Resources  mResources;
-        private final int        mDensity;
-
-        @Override
-        public Resources getResources() { return mResources; }
-
-        @Override
-        public int getDensity() {
-            return mDensity;
-        }
-
-        @Override
-        public ImageDecoder createImageDecoder() throws IOException {
-            return new ImageDecoder();
-        }
-    }
-
-    private static class ResourceSource extends Source {
-        ResourceSource(@NonNull Resources res, int resId) {
-            mResources = res;
-            mResId = resId;
-            mResDensity = Bitmap.DENSITY_NONE;
-        }
-
-        final Resources mResources;
-        final int       mResId;
-        int             mResDensity;
-
-        @Override
-        public Resources getResources() { return mResources; }
-
-        @Override
-        public int getDensity() { return mResDensity; }
-
-        @Override
-        public ImageDecoder createImageDecoder() throws IOException {
-            return new ImageDecoder();
-        }
-    }
-
-    private static class FileSource extends Source {
-        FileSource(@NonNull File file) {
-            mFile = file;
-        }
-
-        private final File mFile;
-
-        @Override
-        public ImageDecoder createImageDecoder() throws IOException {
-            return new ImageDecoder();
-        }
-    }
-
-    /**
-     *  Contains information about the encoded image.
-     */
-    public static class ImageInfo {
-        private ImageDecoder mDecoder;
-
-        private ImageInfo(@NonNull ImageDecoder decoder) {
-            mDecoder = decoder;
-        }
-
-        /**
-         * Size of the image, without scaling or cropping.
-         */
-        @NonNull
-        public Size getSize() {
-            return new Size(0, 0);
-        }
-
-        /**
-         * The mimeType of the image.
-         */
-        @NonNull
-        public String getMimeType() {
-            return "";
-        }
-
-        /**
-         * Whether the image is animated.
-         *
-         * <p>Calling {@link #decodeDrawable} will return an
-         * {@link AnimatedImageDrawable}.</p>
-         */
-        public boolean isAnimated() {
-            return mDecoder.mAnimated;
-        }
-    };
-
-    /**
-     *  Thrown if the provided data is incomplete.
-     */
-    public static class IncompleteException extends IOException {};
-
-    /**
-     *  Optional listener supplied to {@link #decodeDrawable} or
-     *  {@link #decodeBitmap}.
-     */
-    public interface OnHeaderDecodedListener {
-        /**
-         *  Called when the header is decoded and the size is known.
-         *
-         *  @param decoder allows changing the default settings of the decode.
-         *  @param info Information about the encoded image.
-         *  @param source that created the decoder.
-         */
-        void onHeaderDecoded(@NonNull ImageDecoder decoder,
-                @NonNull ImageInfo info, @NonNull Source source);
-
-    };
-
-    /**
-     *  An Exception was thrown reading the {@link Source}.
-     */
-    public static final int ERROR_SOURCE_EXCEPTION  = 1;
-
-    /**
-     *  The encoded data was incomplete.
-     */
-    public static final int ERROR_SOURCE_INCOMPLETE = 2;
-
-    /**
-     *  The encoded data contained an error.
-     */
-    public static final int ERROR_SOURCE_ERROR      = 3;
-
-    @Retention(SOURCE)
-    public @interface Error {}
-
-    /**
-     *  Optional listener supplied to the ImageDecoder.
-     *
-     *  Without this listener, errors will throw {@link java.io.IOException}.
-     */
-    public interface OnPartialImageListener {
-        /**
-         *  Called when there is only a partial image to display.
-         *
-         *  If decoding is interrupted after having decoded a partial image,
-         *  this listener lets the client know that and allows them to
-         *  optionally finish the rest of the decode/creation process to create
-         *  a partial {@link Drawable}/{@link Bitmap}.
-         *
-         *  @param error indicating what interrupted the decode.
-         *  @param source that had the error.
-         *  @return True to create and return a {@link Drawable}/{@link Bitmap}
-         *      with partial data. False (which is the default) to abort the
-         *      decode and throw {@link java.io.IOException}.
-         */
-        boolean onPartialImage(@Error int error, @NonNull Source source);
-    }
-
-    private boolean mAnimated;
-    private Rect mOutPaddingRect;
-
-    public ImageDecoder() {
-        mAnimated = true; // This is too avoid throwing an exception in AnimatedImageDrawable
-    }
-
-    /**
-     * Create a new {@link Source} from an asset.
-     * @hide
-     *
-     * @param res the {@link Resources} object containing the image data.
-     * @param resId resource ID of the image data.
-     *      // FIXME: Can be an @DrawableRes?
-     * @return a new Source object, which can be passed to
-     *      {@link #decodeDrawable} or {@link #decodeBitmap}.
-     */
-    @NonNull
-    public static Source createSource(@NonNull Resources res, int resId)
-    {
-        return new ResourceSource(res, resId);
-    }
-
-    /**
-     * Create a new {@link Source} from a {@link android.net.Uri}.
-     *
-     * @param cr to retrieve from.
-     * @param uri of the image file.
-     * @return a new Source object, which can be passed to
-     *      {@link #decodeDrawable} or {@link #decodeBitmap}.
-     */
-    @NonNull
-    public static Source createSource(@NonNull ContentResolver cr,
-            @NonNull Uri uri) {
-        return new ContentResolverSource(cr, uri);
-    }
-
-    /**
-     * Create a new {@link Source} from a byte array.
-     *
-     * @param data byte array of compressed image data.
-     * @param offset offset into data for where the decoder should begin
-     *      parsing.
-     * @param length number of bytes, beginning at offset, to parse.
-     * @throws NullPointerException if data is null.
-     * @throws ArrayIndexOutOfBoundsException if offset and length are
-     *      not within data.
-     * @hide
-     */
-    @NonNull
-    public static Source createSource(@NonNull byte[] data, int offset,
-            int length) throws ArrayIndexOutOfBoundsException {
-        if (offset < 0 || length < 0 || offset >= data.length ||
-                offset + length > data.length) {
-            throw new ArrayIndexOutOfBoundsException(
-                    "invalid offset/length!");
-        }
-        return new ByteArraySource(data, offset, length);
-    }
-
-    /**
-     * See {@link #createSource(byte[], int, int).
-     * @hide
-     */
-    @NonNull
-    public static Source createSource(@NonNull byte[] data) {
-        return createSource(data, 0, data.length);
-    }
-
-    /**
-     * Create a new {@link Source} from a {@link java.nio.ByteBuffer}.
-     *
-     * <p>The returned {@link Source} effectively takes ownership of the
-     * {@link java.nio.ByteBuffer}; i.e. no other code should modify it after
-     * this call.</p>
-     *
-     * Decoding will start from {@link java.nio.ByteBuffer#position()}. The
-     * position after decoding is undefined.
-     */
-    @NonNull
-    public static Source createSource(@NonNull ByteBuffer buffer) {
-        return new ByteBufferSource(buffer);
-    }
-
-    /**
-     * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
-     * @hide
-     */
-    public static Source createSource(Resources res, InputStream is) {
-        return new InputStreamSource(res, is, Bitmap.getDefaultDensity());
-    }
-
-    /**
-     * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
-     * @hide
-     */
-    public static Source createSource(Resources res, InputStream is, int density) {
-        return new InputStreamSource(res, is, density);
-    }
-
-    /**
-     * Create a new {@link Source} from a {@link java.io.File}.
-     */
-    @NonNull
-    public static Source createSource(@NonNull File file) {
-        return new FileSource(file);
-    }
-
-    /**
-     *  Return the width and height of a given sample size.
-     *
-     *  <p>This takes an input that functions like
-     *  {@link BitmapFactory.Options#inSampleSize}. It returns a width and
-     *  height that can be acheived by sampling the encoded image. Other widths
-     *  and heights may be supported, but will require an additional (internal)
-     *  scaling step. Such internal scaling is *not* supported with
-     *  {@link #setRequireUnpremultiplied} set to {@code true}.</p>
-     *
-     *  @param sampleSize Sampling rate of the encoded image.
-     *  @return {@link android.util.Size} of the width and height after
-     *      sampling.
-     */
-    @NonNull
-    public Size getSampledSize(int sampleSize) {
-        return new Size(0, 0);
-    }
-
-    // Modifiers
-    /**
-     *  Resize the output to have the following size.
-     *
-     *  @param width must be greater than 0.
-     *  @param height must be greater than 0.
-     */
-    public void setResize(int width, int height) {
-    }
-
-    /**
-     *  Resize based on a sample size.
-     *
-     *  <p>This has the same effect as passing the result of
-     *  {@link #getSampledSize} to {@link #setResize(int, int)}.</p>
-     *
-     *  @param sampleSize Sampling rate of the encoded image.
-     */
-    public void setResize(int sampleSize) {
-    }
-
-    // These need to stay in sync with ImageDecoder.cpp's Allocator enum.
-    /**
-     *  Use the default allocation for the pixel memory.
-     *
-     *  Will typically result in a {@link Bitmap.Config#HARDWARE}
-     *  allocation, but may be software for small images. In addition, this will
-     *  switch to software when HARDWARE is incompatible, e.g.
-     *  {@link #setMutable}, {@link #setAsAlphaMask}.
-     */
-    public static final int ALLOCATOR_DEFAULT = 0;
-
-    /**
-     *  Use a software allocation for the pixel memory.
-     *
-     *  Useful for drawing to a software {@link Canvas} or for
-     *  accessing the pixels on the final output.
-     */
-    public static final int ALLOCATOR_SOFTWARE = 1;
-
-    /**
-     *  Use shared memory for the pixel memory.
-     *
-     *  Useful for sharing across processes.
-     */
-    public static final int ALLOCATOR_SHARED_MEMORY = 2;
-
-    /**
-     *  Require a {@link Bitmap.Config#HARDWARE} {@link Bitmap}.
-     *
-     *  When this is combined with incompatible options, like
-     *  {@link #setMutable} or {@link #setAsAlphaMask}, {@link #decodeDrawable}
-     *  / {@link #decodeBitmap} will throw an
-     *  {@link java.lang.IllegalStateException}.
-     */
-    public static final int ALLOCATOR_HARDWARE = 3;
-
-    /** @hide **/
-    @Retention(SOURCE)
-    public @interface Allocator {};
-
-    /**
-     *  Choose the backing for the pixel memory.
-     *
-     *  This is ignored for animated drawables.
-     *
-     *  @param allocator Type of allocator to use.
-     */
-    public void setAllocator(@Allocator int allocator) { }
-
-    /**
-     *  Specify whether the {@link Bitmap} should have unpremultiplied pixels.
-     *
-     *  By default, ImageDecoder will create a {@link Bitmap} with
-     *  premultiplied pixels, which is required for drawing with the
-     *  {@link android.view.View} system (i.e. to a {@link Canvas}). Calling
-     *  this method with a value of {@code true} will result in
-     *  {@link #decodeBitmap} returning a {@link Bitmap} with unpremultiplied
-     *  pixels. See {@link Bitmap#isPremultiplied}. This is incompatible with
-     *  {@link #decodeDrawable}; attempting to decode an unpremultiplied
-     *  {@link Drawable} will throw an {@link java.lang.IllegalStateException}.
-     */
-    public ImageDecoder setRequireUnpremultiplied(boolean requireUnpremultiplied) {
-        return this;
-    }
-
-    /**
-     *  Modify the image after decoding and scaling.
-     *
-     *  <p>This allows adding effects prior to returning a {@link Drawable} or
-     *  {@link Bitmap}. For a {@code Drawable} or an immutable {@code Bitmap},
-     *  this is the only way to process the image after decoding.</p>
-     *
-     *  <p>If set on a nine-patch image, the nine-patch data is ignored.</p>
-     *
-     *  <p>For an animated image, the drawing commands drawn on the
-     *  {@link Canvas} will be recorded immediately and then applied to each
-     *  frame.</p>
-     */
-    public void setPostProcessor(@Nullable PostProcessor p) { }
-
-    /**
-     *  Set (replace) the {@link OnPartialImageListener} on this object.
-     *
-     *  Will be called if there is an error in the input. Without one, a
-     *  partial {@link Bitmap} will be created.
-     */
-    public void setOnPartialImageListener(@Nullable OnPartialImageListener l) { }
-
-    /**
-     *  Crop the output to {@code subset} of the (possibly) scaled image.
-     *
-     *  <p>{@code subset} must be contained within the size set by
-     *  {@link #setResize} or the bounds of the image if setResize was not
-     *  called. Otherwise an {@link IllegalStateException} will be thrown by
-     *  {@link #decodeDrawable}/{@link #decodeBitmap}.</p>
-     *
-     *  <p>NOT intended as a replacement for
-     *  {@link BitmapRegionDecoder#decodeRegion}. This supports all formats,
-     *  but merely crops the output.</p>
-     */
-    public void setCrop(@Nullable Rect subset) { }
-
-    /**
-     *  Set a Rect for retrieving nine patch padding.
-     *
-     *  If the image is a nine patch, this Rect will be set to the padding
-     *  rectangle during decode. Otherwise it will not be modified.
-     *
-     *  @hide
-     */
-    public void setOutPaddingRect(@NonNull Rect outPadding) {
-        mOutPaddingRect = outPadding;
-    }
-
-    /**
-     *  Specify whether the {@link Bitmap} should be mutable.
-     *
-     *  <p>By default, a {@link Bitmap} created will be immutable, but that can
-     *  be changed with this call.</p>
-     *
-     *  <p>Mutable Bitmaps are incompatible with {@link #ALLOCATOR_HARDWARE},
-     *  because {@link Bitmap.Config#HARDWARE} Bitmaps cannot be mutable.
-     *  Attempting to combine them will throw an
-     *  {@link java.lang.IllegalStateException}.</p>
-     *
-     *  <p>Mutable Bitmaps are also incompatible with {@link #decodeDrawable},
-     *  which would require retrieving the Bitmap from the returned Drawable in
-     *  order to modify. Attempting to decode a mutable {@link Drawable} will
-     *  throw an {@link java.lang.IllegalStateException}.</p>
-     */
-    public ImageDecoder setMutable(boolean mutable) {
-        return this;
-    }
-
-    /**
-     *  Specify whether to potentially save RAM at the expense of quality.
-     *
-     *  Setting this to {@code true} may result in a {@link Bitmap} with a
-     *  denser {@link Bitmap.Config}, depending on the image. For example, for
-     *  an opaque {@link Bitmap}, this may result in a {@link Bitmap.Config}
-     *  with no alpha information.
-     */
-    public ImageDecoder setPreferRamOverQuality(boolean preferRamOverQuality) {
-        return this;
-    }
-
-    /**
-     *  Specify whether to potentially treat the output as an alpha mask.
-     *
-     *  <p>If this is set to {@code true} and the image is encoded in a format
-     *  with only one channel, treat that channel as alpha. Otherwise this call has
-     *  no effect.</p>
-     *
-     *  <p>setAsAlphaMask is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to
-     *  combine them will result in {@link #decodeDrawable}/
-     *  {@link #decodeBitmap} throwing an
-     *  {@link java.lang.IllegalStateException}.</p>
-     */
-    public ImageDecoder setAsAlphaMask(boolean asAlphaMask) {
-        return this;
-    }
-
-    @Override
-    public void close() {
-    }
-
-    /**
-     *  Create a {@link Drawable} from a {@code Source}.
-     *
-     *  @param src representing the encoded image.
-     *  @param listener for learning the {@link ImageInfo} and changing any
-     *      default settings on the {@code ImageDecoder}. If not {@code null},
-     *      this will be called on the same thread as {@code decodeDrawable}
-     *      before that method returns.
-     *  @return Drawable for displaying the image.
-     *  @throws IOException if {@code src} is not found, is an unsupported
-     *      format, or cannot be decoded for any reason.
-     */
-    @NonNull
-    public static Drawable decodeDrawable(@NonNull Source src,
-            @Nullable OnHeaderDecodedListener listener) throws IOException {
-        Bitmap bitmap = decodeBitmap(src, listener);
-        return new BitmapDrawable(src.getResources(), bitmap);
-    }
-
-    /**
-     * See {@link #decodeDrawable(Source, OnHeaderDecodedListener)}.
-     */
-    @NonNull
-    public static Drawable decodeDrawable(@NonNull Source src)
-            throws IOException {
-        return decodeDrawable(src, null);
-    }
-
-    /**
-     *  Create a {@link Bitmap} from a {@code Source}.
-     *
-     *  @param src representing the encoded image.
-     *  @param listener for learning the {@link ImageInfo} and changing any
-     *      default settings on the {@code ImageDecoder}. If not {@code null},
-     *      this will be called on the same thread as {@code decodeBitmap}
-     *      before that method returns.
-     *  @return Bitmap containing the image.
-     *  @throws IOException if {@code src} is not found, is an unsupported
-     *      format, or cannot be decoded for any reason.
-     */
-    @NonNull
-    public static Bitmap decodeBitmap(@NonNull Source src,
-            @Nullable OnHeaderDecodedListener listener) throws IOException {
-        TypedValue value = new TypedValue();
-        value.density = src.getDensity();
-        ImageDecoder decoder = src.createImageDecoder();
-        if (listener != null) {
-            listener.onHeaderDecoded(decoder, new ImageInfo(decoder), src);
-        }
-        return BitmapFactory.decodeResourceStream(src.getResources(), value,
-                ((InputStreamSource) src).mInputStream, decoder.mOutPaddingRect, null);
-    }
-
-    /**
-     *  See {@link #decodeBitmap(Source, OnHeaderDecodedListener)}.
-     */
-    @NonNull
-    public static Bitmap decodeBitmap(@NonNull Source src) throws IOException {
-        return decodeBitmap(src, null);
-    }
-
-    public static final class DecodeException extends IOException {
-        /**
-         *  An Exception was thrown reading the {@link Source}.
-         */
-        public static final int SOURCE_EXCEPTION  = 1;
-
-        /**
-         *  The encoded data was incomplete.
-         */
-        public static final int SOURCE_INCOMPLETE = 2;
-
-        /**
-         *  The encoded data contained an error.
-         */
-        public static final int SOURCE_MALFORMED_DATA      = 3;
-
-        @Error final int mError;
-        @NonNull final Source mSource;
-
-        DecodeException(@Error int error, @Nullable Throwable cause, @NonNull Source source) {
-            super(errorMessage(error, cause), cause);
-            mError = error;
-            mSource = source;
-        }
-
-        /**
-         * Private method called by JNI.
-         */
-        @SuppressWarnings("unused")
-        DecodeException(@Error int error, @Nullable String msg, @Nullable Throwable cause,
-                @NonNull Source source) {
-            super(msg + errorMessage(error, cause), cause);
-            mError = error;
-            mSource = source;
-        }
-
-        /**
-         *  Retrieve the reason that decoding was interrupted.
-         *
-         *  <p>If the error is {@link #SOURCE_EXCEPTION}, the underlying
-         *  {@link java.lang.Throwable} can be retrieved with
-         *  {@link java.lang.Throwable#getCause}.</p>
-         */
-        @Error
-        public int getError() {
-            return mError;
-        }
-
-        /**
-         *  Retrieve the {@link Source Source} that was interrupted.
-         *
-         *  <p>This can be used for equality checking to find the Source which
-         *  failed to completely decode.</p>
-         */
-        @NonNull
-        public Source getSource() {
-            return mSource;
-        }
-
-        private static String errorMessage(@Error int error, @Nullable Throwable cause) {
-            switch (error) {
-                case SOURCE_EXCEPTION:
-                    return "Exception in input: " + cause;
-                case SOURCE_INCOMPLETE:
-                    return "Input was incomplete.";
-                case SOURCE_MALFORMED_DATA:
-                    return "Input contained an error.";
-                default:
-                    return "";
-            }
-        }
-    }
-}
diff --git a/bridge/src/android/graphics/ImageDecoder_Delegate.java b/bridge/src/android/graphics/ImageDecoder_Delegate.java
new file mode 100644
index 0000000..4eebc9d
--- /dev/null
+++ b/bridge/src/android/graphics/ImageDecoder_Delegate.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 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.util.NinePatchInputStream;
+import com.android.ninepatch.GraphicsUtilities;
+import com.android.ninepatch.NinePatch;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.annotation.NonNull;
+import android.graphics.Bitmap.Config;
+import android.graphics.ImageDecoder.InputStreamSource;
+import android.graphics.ImageDecoder.OnHeaderDecodedListener;
+import android.graphics.ImageDecoder.Source;
+
+import java.awt.image.BufferedImage;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ImageDecoder_Delegate {
+
+    @LayoutlibDelegate
+    /*package*/ static Bitmap decodeBitmapImpl(@NonNull Source src,
+            @NonNull OnHeaderDecodedListener listener) throws IOException {
+        InputStream stream = src instanceof InputStreamSource ?
+                ((InputStreamSource) src).mInputStream : null;
+        Bitmap bm = ImageDecoder.decodeBitmapImpl_Original(src, listener);
+        if (stream instanceof NinePatchInputStream && bm.getNinePatchChunk() == null) {
+            stream = new FileInputStream(((NinePatchInputStream) stream).getPath());
+            NinePatch ninePatch = NinePatch.load(stream, true /*is9Patch*/, false /* convert */);
+            BufferedImage image = ninePatch.getImage();
+
+            // width and height of the nine patch without the special border.
+            int width = image.getWidth();
+            int height = image.getHeight();
+
+            // Get pixel data from image independently of its type.
+            int[] imageData = GraphicsUtilities.getPixels(image, 0, 0, width, height, null);
+
+            bm = Bitmap.createBitmap(imageData, width, height, Config.ARGB_8888);
+
+            bm.setDensity(src.getDensity());
+            bm.setNinePatchChunk(ninePatch.getChunk().getSerializedChunk());
+        }
+        return bm;
+    }
+}
diff --git a/bridge/src/android/graphics/LayoutlibRenderer.java b/bridge/src/android/graphics/LayoutlibRenderer.java
new file mode 100644
index 0000000..e96c790
--- /dev/null
+++ b/bridge/src/android/graphics/LayoutlibRenderer.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 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.annotation.Nullable;
+
+public class LayoutlibRenderer extends HardwareRenderer {
+
+    private float scaleX = 1.0f;
+    private float scaleY = 1.0f;
+
+    /**
+     * We are overriding this method in order to call {@link Canvas#enableZ} (for shadows) and set
+     * the scale
+     */
+    @Override
+    public void setContentRoot(@Nullable RenderNode content) {
+        RecordingCanvas canvas = mRootNode.beginRecording();
+        canvas.scale(scaleX, scaleY);
+        canvas.enableZ();
+        // This way we clear the native image buffer before drawing
+        canvas.drawColor(0, BlendMode.CLEAR);
+        if (content != null) {
+            canvas.drawRenderNode(content);
+        }
+        canvas.disableZ();
+        mRootNode.endRecording();
+    }
+
+    public void setScale(float scaleX, float scaleY) {
+        this.scaleX = scaleX;
+        this.scaleY = scaleY;
+    }
+}
diff --git a/bridge/src/android/graphics/LightingColorFilter_Delegate.java b/bridge/src/android/graphics/LightingColorFilter_Delegate.java
deleted file mode 100644
index 0dd9703..0000000
--- a/bridge/src/android/graphics/LightingColorFilter_Delegate.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.LightingColorFilter
- *
- * Through the layoutlib_create tool, the original native methods of LightingColorFilter have
- * been replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original LightingColorFilter class.
- *
- * Because this extends {@link ColorFilter_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the Shader classes will be added to the manager
- * owned by {@link ColorFilter_Delegate}.
- *
- * @see ColorFilter_Delegate
- *
- */
-public class LightingColorFilter_Delegate extends ColorFilter_Delegate {
-
-    // ---- delegate data ----
-
-    // ---- Public Helper methods ----
-
-    @Override
-    public String getSupportMessage() {
-        return "Lighting Color Filters are not supported.";
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long native_CreateLightingFilter(int mul, int add) {
-        LightingColorFilter_Delegate newDelegate = new LightingColorFilter_Delegate();
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/LinearGradient_Delegate.java b/bridge/src/android/graphics/LinearGradient_Delegate.java
deleted file mode 100644
index 4574dd7..0000000
--- a/bridge/src/android/graphics/LinearGradient_Delegate.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.Shader.TileMode;
-
-import java.awt.image.ColorModel;
-import java.awt.image.DataBufferInt;
-import java.awt.image.Raster;
-import java.awt.image.SampleModel;
-
-/**
- * Delegate implementing the native methods of android.graphics.LinearGradient
- *
- * Through the layoutlib_create tool, the original native methods of LinearGradient have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original LinearGradient class.
- *
- * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
- *
- * @see Shader_Delegate
- *
- */
-public final class LinearGradient_Delegate extends Gradient_Delegate {
-
-    // ---- delegate data ----
-    private java.awt.Paint mJavaPaint;
-
-    // ---- Public Helper methods ----
-
-    @Override
-    public java.awt.Paint getJavaPaint() {
-        return mJavaPaint;
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreate(LinearGradient thisGradient, long matrix,
-            float x0, float y0, float x1, float y1, long[] colors, float[] positions,
-            int tileMode, long colorSpaceHandle) {
-        LinearGradient_Delegate newDelegate = new LinearGradient_Delegate(matrix, x0, y0,
-                x1, y1, colors, positions, Shader_Delegate.getTileMode(tileMode));
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    // ---- Private delegate/helper methods ----
-
-    /**
-     * Create a shader that draws a linear gradient along a line.
-     *
-     * @param nativeMatrix reference to the shader's native transformation matrix
-     * @param x0 The x-coordinate for the start of the gradient line
-     * @param y0 The y-coordinate for the start of the gradient line
-     * @param x1 The x-coordinate for the end of the gradient line
-     * @param y1 The y-coordinate for the end of the gradient line
-     * @param colors The colors to be distributed along the gradient line
-     * @param positions May be null. The relative positions [0..1] of each
-     *            corresponding color in the colors array. If this is null, the
-     *            the colors are distributed evenly along the gradient line.
-     * @param tile The Shader tiling mode
-     */
-    private LinearGradient_Delegate(long nativeMatrix, float x0, float y0, float x1,
-            float y1, long[] colors, float[] positions, TileMode tile) {
-        super(nativeMatrix, colors, positions);
-        mJavaPaint = new LinearGradientPaint(x0, y0, x1, y1, mColors, mPositions, tile);
-    }
-
-    // ---- Custom Java Paint ----
-    /**
-     * Linear Gradient (Java) Paint able to handle more than 2 points, as
-     * {@link java.awt.GradientPaint} only supports 2 points and does not support Android's tile
-     * modes.
-     */
-    private class LinearGradientPaint extends GradientPaint {
-
-        private final float mX0;
-        private final float mY0;
-        private final float mDx;
-        private final float mDy;
-        private final float mDSize2;
-
-        public LinearGradientPaint(float x0, float y0, float x1, float y1, int[] colors,
-                float[] positions, TileMode tile) {
-            super(colors, positions, tile);
-            mX0 = x0;
-            mY0 = y0;
-            mDx = x1 - x0;
-            mDy = y1 - y0;
-            mDSize2 = mDx * mDx + mDy * mDy;
-        }
-
-        @Override
-        public java.awt.PaintContext createContext(
-                java.awt.image.ColorModel      colorModel,
-                java.awt.Rectangle             deviceBounds,
-                java.awt.geom.Rectangle2D      userBounds,
-                java.awt.geom.AffineTransform  xform,
-                java.awt.RenderingHints        hints) {
-            precomputeGradientColors();
-
-            java.awt.geom.AffineTransform canvasMatrix;
-            try {
-                canvasMatrix = xform.createInverse();
-            } catch (java.awt.geom.NoninvertibleTransformException e) {
-                Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_INVERSE,
-                        "Unable to inverse matrix in LinearGradient", e, null, null /*data*/);
-                canvasMatrix = new java.awt.geom.AffineTransform();
-            }
-
-            java.awt.geom.AffineTransform localMatrix = getLocalMatrix();
-            try {
-                localMatrix = localMatrix.createInverse();
-            } catch (java.awt.geom.NoninvertibleTransformException e) {
-                Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_INVERSE,
-                        "Unable to inverse matrix in LinearGradient", e, null, null /*data*/);
-                localMatrix = new java.awt.geom.AffineTransform();
-            }
-
-            return new LinearGradientPaintContext(canvasMatrix, localMatrix, colorModel);
-        }
-
-        private class LinearGradientPaintContext implements java.awt.PaintContext {
-
-            private final java.awt.geom.AffineTransform mCanvasMatrix;
-            private final java.awt.geom.AffineTransform mLocalMatrix;
-            private final java.awt.image.ColorModel mColorModel;
-
-            private LinearGradientPaintContext(
-                    java.awt.geom.AffineTransform canvasMatrix,
-                    java.awt.geom.AffineTransform localMatrix,
-                    java.awt.image.ColorModel colorModel) {
-                mCanvasMatrix = canvasMatrix;
-                mLocalMatrix = localMatrix;
-                mColorModel = colorModel.hasAlpha() ? colorModel : ColorModel.getRGBdefault();
-            }
-
-            @Override
-            public void dispose() {
-            }
-
-            @Override
-            public java.awt.image.ColorModel getColorModel() {
-                return mColorModel;
-            }
-
-            @Override
-            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
-                int[] data = new int[w*h];
-
-                int index = 0;
-                float[] pt1 = new float[2];
-                float[] pt2 = new float[2];
-                for (int iy = 0 ; iy < h ; iy++) {
-                    for (int ix = 0 ; ix < w ; ix++) {
-                        // handle the canvas transform
-                        pt1[0] = x + ix;
-                        pt1[1] = y + iy;
-                        mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
-
-                        // handle the local matrix.
-                        pt1[0] = pt2[0];
-                        pt1[1] = pt2[1];
-                        mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
-
-                        data[index++] = getColor(pt2[0], pt2[1]);
-                    }
-                }
-
-                DataBufferInt dataBuffer = new DataBufferInt(data, data.length);
-                SampleModel colorModel = mColorModel.createCompatibleSampleModel(w, h);
-                return Raster.createWritableRaster(colorModel, dataBuffer, null);
-            }
-        }
-
-        /**
-         * Returns a color for an arbitrary point.
-         */
-        private int getColor(float x, float y) {
-            float pos;
-            if (mDx == 0) {
-                pos = (y - mY0) / mDy;
-            } else if (mDy == 0) {
-                pos = (x - mX0) / mDx;
-            } else {
-                // find the x position on the gradient vector.
-                float _x = (mDx*mDy*(y-mY0) + mDy*mDy*mX0 + mDx*mDx*x) / mDSize2;
-                // from it get the position relative to the vector
-                pos = (_x - mX0) / mDx;
-            }
-
-            return getGradientColor(pos);
-        }
-    }
-}
diff --git a/bridge/src/android/graphics/MaskFilter_Delegate.java b/bridge/src/android/graphics/MaskFilter_Delegate.java
deleted file mode 100644
index e726c59..0000000
--- a/bridge/src/android/graphics/MaskFilter_Delegate.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.MaskFilter
- *
- * Through the layoutlib_create tool, the original native methods of MaskFilter have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original MaskFilter class.
- *
- * This also serve as a base class for all MaskFilter delegate classes.
- *
- * @see DelegateManager
- *
- */
-public abstract class MaskFilter_Delegate {
-
-    // ---- delegate manager ----
-    protected static final DelegateManager<MaskFilter_Delegate> sManager =
-            new DelegateManager<MaskFilter_Delegate>(MaskFilter_Delegate.class);
-
-    // ---- delegate helper data ----
-
-    // ---- delegate data ----
-
-    // ---- Public Helper methods ----
-
-    public static MaskFilter_Delegate getDelegate(long nativeShader) {
-        return sManager.getDelegate(nativeShader);
-    }
-
-    public abstract boolean isSupported();
-    public abstract String getSupportMessage();
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeDestructor(long native_filter) {
-        sManager.removeJavaReferenceFor(native_filter);
-    }
-
-    // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/Matrix_Delegate.java b/bridge/src/android/graphics/Matrix_Delegate.java
deleted file mode 100644
index d0a0adc..0000000
--- a/bridge/src/android/graphics/Matrix_Delegate.java
+++ /dev/null
@@ -1,1084 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.Matrix.ScaleToFit;
-
-import java.awt.geom.AffineTransform;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.Matrix
- *
- * Through the layoutlib_create tool, the original native methods of Matrix have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Matrix class.
- *
- * @see DelegateManager
- *
- */
-public final class Matrix_Delegate {
-
-    private final static int MATRIX_SIZE = 9;
-
-    // ---- delegate manager ----
-    private static final DelegateManager<Matrix_Delegate> sManager =
-            new DelegateManager<Matrix_Delegate>(Matrix_Delegate.class);
-    private static long sFinalizer = -1;
-
-    // ---- delegate data ----
-    private float mValues[] = new float[MATRIX_SIZE];
-
-    // ---- Public Helper methods ----
-
-    public static Matrix_Delegate getDelegate(long native_instance) {
-        return sManager.getDelegate(native_instance);
-    }
-
-    /**
-     * Returns an {@link AffineTransform} matching the given Matrix.
-     */
-    public static AffineTransform getAffineTransform(Matrix m) {
-        Matrix_Delegate delegate = sManager.getDelegate(m.ni());
-        if (delegate == null) {
-            return null;
-        }
-
-        return delegate.getAffineTransform();
-    }
-
-    public static boolean hasPerspective(Matrix m) {
-        Matrix_Delegate delegate = sManager.getDelegate(m.ni());
-        if (delegate == null) {
-            return false;
-        }
-
-        return delegate.hasPerspective();
-    }
-
-    /**
-     * Sets the content of the matrix with the content of another matrix.
-     */
-    public void set(Matrix_Delegate matrix) {
-        System.arraycopy(matrix.mValues, 0, mValues, 0, MATRIX_SIZE);
-    }
-
-    /**
-     * Sets the content of the matrix with the content of another matrix represented as an array
-     * of values.
-     */
-    public void set(float[] values) {
-        System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE);
-    }
-
-    /**
-     * Resets the matrix to be the identity matrix.
-     */
-    public void reset() {
-        reset(mValues);
-    }
-
-    /**
-     * Returns whether or not the matrix is identity.
-     */
-    public boolean isIdentity() {
-        for (int i = 0, k = 0; i < 3; i++) {
-            for (int j = 0; j < 3; j++, k++) {
-                if (mValues[k] != ((i==j) ? 1 : 0)) {
-                    return false;
-                }
-            }
-        }
-
-        return true;
-    }
-
-    private static float[] setValues(AffineTransform matrix, float[] values) {
-        values[0] = (float) matrix.getScaleX();
-        values[1] = (float) matrix.getShearX();
-        values[2] = (float) matrix.getTranslateX();
-        values[3] = (float) matrix.getShearY();
-        values[4] = (float) matrix.getScaleY();
-        values[5] = (float) matrix.getTranslateY();
-        values[6] = 0.f;
-        values[7] = 0.f;
-        values[8] = 1.f;
-
-        return values;
-    }
-
-    public static float[] makeValues(AffineTransform matrix) {
-        return setValues(matrix, new float[MATRIX_SIZE]);
-    }
-
-    public static Matrix_Delegate make(AffineTransform matrix) {
-        return new Matrix_Delegate(makeValues(matrix));
-    }
-
-    public boolean mapRect(RectF dst, RectF src) {
-        // array with 4 corners
-        float[] corners = new float[] {
-                src.left, src.top,
-                src.right, src.top,
-                src.right, src.bottom,
-                src.left, src.bottom,
-        };
-
-        // apply the transform to them.
-        mapPoints(corners);
-
-        // now put the result in the rect. We take the min/max of Xs and min/max of Ys
-        dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
-        dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
-
-        dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
-        dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
-
-
-        return (computeTypeMask() & kRectStaysRect_Mask) != 0;
-    }
-
-
-    /**
-     * Returns an {@link AffineTransform} matching the matrix.
-     */
-    public AffineTransform getAffineTransform() {
-        return getAffineTransform(mValues);
-    }
-
-    public boolean hasPerspective() {
-        return (mValues[6] != 0 || mValues[7] != 0 || mValues[8] != 1);
-    }
-
-
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nCreate(long native_src_or_zero) {
-        // create the delegate
-        Matrix_Delegate newDelegate = new Matrix_Delegate();
-
-        // copy from values if needed.
-        if (native_src_or_zero > 0) {
-            Matrix_Delegate oldDelegate = sManager.getDelegate(native_src_or_zero);
-            if (oldDelegate != null) {
-                System.arraycopy(
-                        oldDelegate.mValues, 0,
-                        newDelegate.mValues, 0,
-                        MATRIX_SIZE);
-            }
-        }
-
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nIsIdentity(long native_object) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return false;
-        }
-
-        return d.isIdentity();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nIsAffine(long native_object) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return true;
-        }
-
-        return (d.computeTypeMask() & kPerspective_Mask) == 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nRectStaysRect(long native_object) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return true;
-        }
-
-        return (d.computeTypeMask() & kRectStaysRect_Mask) != 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nReset(long native_object) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return;
-        }
-
-        reset(d.mValues);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSet(long native_object, long other) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return;
-        }
-
-        Matrix_Delegate src = sManager.getDelegate(other);
-        if (src == null) {
-            return;
-        }
-
-        System.arraycopy(src.mValues, 0, d.mValues, 0, MATRIX_SIZE);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetTranslate(long native_object, float dx, float dy) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return;
-        }
-
-        setTranslate(d.mValues, dx, dy);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetScale(long native_object, float sx, float sy,
-            float px, float py) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return;
-        }
-
-        d.mValues = getScale(sx, sy, px, py);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetScale(long native_object, float sx, float sy) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return;
-        }
-
-        d.mValues[0] = sx;
-        d.mValues[1] = 0;
-        d.mValues[2] = 0;
-        d.mValues[3] = 0;
-        d.mValues[4] = sy;
-        d.mValues[5] = 0;
-        d.mValues[6] = 0;
-        d.mValues[7] = 0;
-        d.mValues[8] = 1;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetRotate(long native_object, float degrees, float px, float py) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return;
-        }
-
-        d.mValues = getRotate(degrees, px, py);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetRotate(long native_object, float degrees) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return;
-        }
-
-        setRotate(d.mValues, degrees);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetSinCos(long native_object, float sinValue, float cosValue,
-            float px, float py) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return;
-        }
-
-        // TODO: do it in one pass
-
-        // translate so that the pivot is in 0,0
-        setTranslate(d.mValues, -px, -py);
-
-        // scale
-        d.postTransform(getRotate(sinValue, cosValue));
-        // translate back the pivot
-        d.postTransform(getTranslate(px, py));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetSinCos(long native_object, float sinValue, float cosValue) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return;
-        }
-
-        setRotate(d.mValues, sinValue, cosValue);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetSkew(long native_object, float kx, float ky,
-            float px, float py) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return;
-        }
-
-        d.mValues = getSkew(kx, ky, px, py);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetSkew(long native_object, float kx, float ky) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return;
-        }
-
-        d.mValues[0] = 1;
-        d.mValues[1] = kx;
-        d.mValues[2] = -0;
-        d.mValues[3] = ky;
-        d.mValues[4] = 1;
-        d.mValues[5] = 0;
-        d.mValues[6] = 0;
-        d.mValues[7] = 0;
-        d.mValues[8] = 1;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetConcat(long native_object, long a, long b) {
-        if (a == native_object) {
-            nPreConcat(native_object, b);
-            return;
-        } else if (b == native_object) {
-            nPostConcat(native_object, a);
-            return;
-        }
-
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        Matrix_Delegate a_mtx = sManager.getDelegate(a);
-        Matrix_Delegate b_mtx = sManager.getDelegate(b);
-        if (d != null && a_mtx != null && b_mtx != null) {
-            multiply(d.mValues, a_mtx.mValues, b_mtx.mValues);
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nPreTranslate(long native_object, float dx, float dy) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d != null) {
-            d.preTransform(getTranslate(dx, dy));
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nPreScale(long native_object, float sx, float sy,
-            float px, float py) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d != null) {
-            d.preTransform(getScale(sx, sy, px, py));
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nPreScale(long native_object, float sx, float sy) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d != null) {
-            d.preTransform(getScale(sx, sy));
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nPreRotate(long native_object, float degrees,
-            float px, float py) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d != null) {
-            d.preTransform(getRotate(degrees, px, py));
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nPreRotate(long native_object, float degrees) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d != null) {
-
-            double rad = Math.toRadians(degrees);
-            float sin = (float) Math.sin(rad);
-            float cos = (float) Math.cos(rad);
-
-            d.preTransform(getRotate(sin, cos));
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nPreSkew(long native_object, float kx, float ky,
-            float px, float py) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d != null) {
-            d.preTransform(getSkew(kx, ky, px, py));
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nPreSkew(long native_object, float kx, float ky) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d != null) {
-            d.preTransform(getSkew(kx, ky));
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nPreConcat(long native_object, long other_matrix) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        Matrix_Delegate other = sManager.getDelegate(other_matrix);
-        if (d != null && other != null) {
-            d.preTransform(other.mValues);
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nPostTranslate(long native_object, float dx, float dy) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d != null) {
-            d.postTransform(getTranslate(dx, dy));
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nPostScale(long native_object, float sx, float sy,
-            float px, float py) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d != null) {
-            d.postTransform(getScale(sx, sy, px, py));
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nPostScale(long native_object, float sx, float sy) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d != null) {
-            d.postTransform(getScale(sx, sy));
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nPostRotate(long native_object, float degrees,
-            float px, float py) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d != null) {
-            d.postTransform(getRotate(degrees, px, py));
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nPostRotate(long native_object, float degrees) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d != null) {
-            d.postTransform(getRotate(degrees));
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nPostSkew(long native_object, float kx, float ky,
-            float px, float py) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d != null) {
-            d.postTransform(getSkew(kx, ky, px, py));
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nPostSkew(long native_object, float kx, float ky) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d != null) {
-            d.postTransform(getSkew(kx, ky));
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nPostConcat(long native_object, long other_matrix) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        Matrix_Delegate other = sManager.getDelegate(other_matrix);
-        if (d != null && other != null) {
-            d.postTransform(other.mValues);
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nSetRectToRect(long native_object, RectF src,
-            RectF dst, int stf) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return false;
-        }
-
-        if (src.isEmpty()) {
-            reset(d.mValues);
-            return false;
-        }
-
-        if (dst.isEmpty()) {
-            d.mValues[0] = d.mValues[1] = d.mValues[2] = d.mValues[3] = d.mValues[4] = d.mValues[5]
-               = d.mValues[6] = d.mValues[7] = 0;
-            d.mValues[8] = 1;
-        } else {
-            float    tx, sx = dst.width() / src.width();
-            float    ty, sy = dst.height() / src.height();
-            boolean  xLarger = false;
-
-            if (stf != ScaleToFit.FILL.nativeInt) {
-                if (sx > sy) {
-                    xLarger = true;
-                    sx = sy;
-                } else {
-                    sy = sx;
-                }
-            }
-
-            tx = dst.left - src.left * sx;
-            ty = dst.top - src.top * sy;
-            if (stf == ScaleToFit.CENTER.nativeInt || stf == ScaleToFit.END.nativeInt) {
-                float diff;
-
-                if (xLarger) {
-                    diff = dst.width() - src.width() * sy;
-                } else {
-                    diff = dst.height() - src.height() * sy;
-                }
-
-                if (stf == ScaleToFit.CENTER.nativeInt) {
-                    diff = diff / 2;
-                }
-
-                if (xLarger) {
-                    tx += diff;
-                } else {
-                    ty += diff;
-                }
-            }
-
-            d.mValues[0] = sx;
-            d.mValues[4] = sy;
-            d.mValues[2] = tx;
-            d.mValues[5] = ty;
-            d.mValues[1]  = d.mValues[3] = d.mValues[6] = d.mValues[7] = 0;
-
-        }
-        // shared cleanup
-        d.mValues[8] = 1;
-        return true;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nSetPolyToPoly(long native_object, float[] src, int srcIndex,
-            float[] dst, int dstIndex, int pointCount) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Matrix.setPolyToPoly is not supported.",
-                null, null, null /*data*/);
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nInvert(long native_object, long inverse) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return false;
-        }
-
-        Matrix_Delegate inv_mtx = sManager.getDelegate(inverse);
-        if (inv_mtx == null) {
-            return false;
-        }
-
-        float det = d.mValues[0] * (d.mValues[4] * d.mValues[8] - d.mValues[5] * d.mValues[7])
-                  + d.mValues[1] * (d.mValues[5] * d.mValues[6] - d.mValues[3] * d.mValues[8])
-                  + d.mValues[2] * (d.mValues[3] * d.mValues[7] - d.mValues[4] * d.mValues[6]);
-
-        if (det == 0.0) {
-            return false;
-        }
-
-        inv_mtx.mValues[0] = (d.mValues[4] * d.mValues[8] - d.mValues[5] * d.mValues[7]) / det;
-        inv_mtx.mValues[1] = (d.mValues[2] * d.mValues[7] - d.mValues[1] * d.mValues[8]) / det;
-        inv_mtx.mValues[2] = (d.mValues[1] * d.mValues[5] - d.mValues[2] * d.mValues[4]) / det;
-        inv_mtx.mValues[3] = (d.mValues[5] * d.mValues[6] - d.mValues[3] * d.mValues[8]) / det;
-        inv_mtx.mValues[4] = (d.mValues[0] * d.mValues[8] - d.mValues[2] * d.mValues[6]) / det;
-        inv_mtx.mValues[5] = (d.mValues[2] * d.mValues[3] - d.mValues[0] * d.mValues[5]) / det;
-        inv_mtx.mValues[6] = (d.mValues[3] * d.mValues[7] - d.mValues[4] * d.mValues[6]) / det;
-        inv_mtx.mValues[7] = (d.mValues[1] * d.mValues[6] - d.mValues[0] * d.mValues[7]) / det;
-        inv_mtx.mValues[8] = (d.mValues[0] * d.mValues[4] - d.mValues[1] * d.mValues[3]) / det;
-
-        return true;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nMapPoints(long native_object, float[] dst, int dstIndex,
-            float[] src, int srcIndex, int ptCount, boolean isPts) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return;
-        }
-
-        if (isPts) {
-            d.mapPoints(dst, dstIndex, src, srcIndex, ptCount);
-        } else {
-            d.mapVectors(dst, dstIndex, src, srcIndex, ptCount);
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nMapRect(long native_object, RectF dst, RectF src) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return false;
-        }
-
-        return d.mapRect(dst, src);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nMapRadius(long native_object, float radius) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return 0.f;
-        }
-
-        float[] src = new float[] { radius, 0.f, 0.f, radius };
-        d.mapVectors(src, 0, src, 0, 2);
-
-        float l1 = (float) Math.hypot(src[0], src[1]);
-        float l2 = (float) Math.hypot(src[2], src[3]);
-        return (float) Math.sqrt(l1 * l2);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nGetValues(long native_object, float[] values) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return;
-        }
-
-        System.arraycopy(d.mValues, 0, values, 0, MATRIX_SIZE);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetValues(long native_object, float[] values) {
-        Matrix_Delegate d = sManager.getDelegate(native_object);
-        if (d == null) {
-            return;
-        }
-
-        System.arraycopy(values, 0, d.mValues, 0, MATRIX_SIZE);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nEquals(long native_a, long native_b) {
-        Matrix_Delegate a = sManager.getDelegate(native_a);
-        if (a == null) {
-            return false;
-        }
-
-        Matrix_Delegate b = sManager.getDelegate(native_b);
-        if (b == null) {
-            return false;
-        }
-
-        for (int i = 0 ; i < MATRIX_SIZE ; i++) {
-            if (a.mValues[i] != b.mValues[i]) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nGetNativeFinalizer() {
-        synchronized (Matrix_Delegate.class) {
-            if (sFinalizer == -1) {
-                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
-            }
-        }
-        return sFinalizer;
-    }
-
-    // ---- Private helper methods ----
-
-    /*package*/ static AffineTransform getAffineTransform(float[] matrix) {
-        // the AffineTransform constructor takes the value in a different order
-        // for a matrix [ 0 1 2 ]
-        //              [ 3 4 5 ]
-        // the order is 0, 3, 1, 4, 2, 5...
-        return new AffineTransform(
-                matrix[0], matrix[3], matrix[1],
-                matrix[4], matrix[2], matrix[5]);
-    }
-
-    /**
-     * Reset a matrix to the identity
-     */
-    private static void reset(float[] mtx) {
-        for (int i = 0, k = 0; i < 3; i++) {
-            for (int j = 0; j < 3; j++, k++) {
-                mtx[k] = ((i==j) ? 1 : 0);
-            }
-        }
-    }
-
-    @SuppressWarnings("unused")
-    private final static int kIdentity_Mask      = 0;
-    private final static int kTranslate_Mask     = 0x01;  //!< set if the matrix has translation
-    private final static int kScale_Mask         = 0x02;  //!< set if the matrix has X or Y scale
-    private final static int kAffine_Mask        = 0x04;  //!< set if the matrix skews or rotates
-    private final static int kPerspective_Mask   = 0x08;  //!< set if the matrix is in perspective
-    private final static int kRectStaysRect_Mask = 0x10;
-    @SuppressWarnings("unused")
-    private final static int kUnknown_Mask       = 0x80;
-
-    @SuppressWarnings("unused")
-    private final static int kAllMasks           = kTranslate_Mask |
-                                                   kScale_Mask |
-                                                   kAffine_Mask |
-                                                   kPerspective_Mask |
-                                                   kRectStaysRect_Mask;
-
-    // these guys align with the masks, so we can compute a mask from a variable 0/1
-    @SuppressWarnings("unused")
-    private final static int kTranslate_Shift = 0;
-    @SuppressWarnings("unused")
-    private final static int kScale_Shift = 1;
-    @SuppressWarnings("unused")
-    private final static int kAffine_Shift = 2;
-    @SuppressWarnings("unused")
-    private final static int kPerspective_Shift = 3;
-    private final static int kRectStaysRect_Shift = 4;
-
-    private int computeTypeMask() {
-        int mask = 0;
-
-        if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) {
-            mask |= kPerspective_Mask;
-        }
-
-        if (mValues[2] != 0. || mValues[5] != 0.) {
-            mask |= kTranslate_Mask;
-        }
-
-        float m00 = mValues[0];
-        float m01 = mValues[1];
-        float m10 = mValues[3];
-        float m11 = mValues[4];
-
-        if (m01 != 0. || m10 != 0.) {
-            mask |= kAffine_Mask;
-        }
-
-        if (m00 != 1. || m11 != 1.) {
-            mask |= kScale_Mask;
-        }
-
-        if ((mask & kPerspective_Mask) == 0) {
-            // map non-zero to 1
-            int im00 = m00 != 0 ? 1 : 0;
-            int im01 = m01 != 0 ? 1 : 0;
-            int im10 = m10 != 0 ? 1 : 0;
-            int im11 = m11 != 0 ? 1 : 0;
-
-            // record if the (p)rimary and (s)econdary diagonals are all 0 or
-            // all non-zero (answer is 0 or 1)
-            int dp0 = (im00 | im11) ^ 1;  // true if both are 0
-            int dp1 = im00 & im11;        // true if both are 1
-            int ds0 = (im01 | im10) ^ 1;  // true if both are 0
-            int ds1 = im01 & im10;        // true if both are 1
-
-            // return 1 if primary is 1 and secondary is 0 or
-            // primary is 0 and secondary is 1
-            mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
-        }
-
-        return mask;
-    }
-
-    private Matrix_Delegate() {
-        reset();
-    }
-
-    private Matrix_Delegate(float[] values) {
-        System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE);
-    }
-
-    /**
-     * Adds the given transformation to the current Matrix
-     * <p/>This in effect does this = this*matrix
-     * @param matrix
-     */
-    private void postTransform(float[] matrix) {
-        float[] tmp = new float[9];
-        multiply(tmp, mValues, matrix);
-        mValues = tmp;
-    }
-
-    /**
-     * Adds the given transformation to the current Matrix
-     * <p/>This in effect does this = matrix*this
-     * @param matrix
-     */
-    private void preTransform(float[] matrix) {
-        float[] tmp = new float[9];
-        multiply(tmp, matrix, mValues);
-        mValues = tmp;
-    }
-
-    /**
-     * Apply this matrix to the array of 2D points specified by src, and write
-      * the transformed points into the array of points specified by dst. The
-      * two arrays represent their "points" as pairs of floats [x, y].
-      *
-      * @param dst   The array of dst points (x,y pairs)
-      * @param dstIndex The index of the first [x,y] pair of dst floats
-      * @param src   The array of src points (x,y pairs)
-      * @param srcIndex The index of the first [x,y] pair of src floats
-      * @param pointCount The number of points (x,y pairs) to transform
-      */
-
-     private void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
-                           int pointCount) {
-         final int count = pointCount * 2;
-
-         float[] tmpDest = dst;
-         boolean inPlace = dst == src;
-         if (inPlace) {
-             tmpDest = new float[dstIndex + count];
-         }
-
-         for (int i = 0 ; i < count ; i += 2) {
-             // just in case we are doing in place, we better put this in temp vars
-             float x = mValues[0] * src[i + srcIndex] +
-                       mValues[1] * src[i + srcIndex + 1] +
-                       mValues[2];
-             float y = mValues[3] * src[i + srcIndex] +
-                       mValues[4] * src[i + srcIndex + 1] +
-                       mValues[5];
-
-             tmpDest[i + dstIndex]     = x;
-             tmpDest[i + dstIndex + 1] = y;
-         }
-
-         if (inPlace) {
-             System.arraycopy(tmpDest, dstIndex, dst, dstIndex, count);
-         }
-     }
-
-     /**
-      * Apply this matrix to the array of 2D points, and write the transformed
-      * points back into the array
-      *
-      * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
-      */
-
-     private void mapPoints(float[] pts) {
-         mapPoints(pts, 0, pts, 0, pts.length >> 1);
-     }
-
-     private void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int ptCount) {
-         if (hasPerspective()) {
-             // transform the (0,0) point
-             float[] origin = new float[] { 0.f, 0.f};
-             mapPoints(origin);
-
-             // translate the vector data as points
-             mapPoints(dst, dstIndex, src, srcIndex, ptCount);
-
-             // then substract the transformed origin.
-             final int count = ptCount * 2;
-             for (int i = 0 ; i < count ; i += 2) {
-                 dst[dstIndex + i] = dst[dstIndex + i] - origin[0];
-                 dst[dstIndex + i + 1] = dst[dstIndex + i + 1] - origin[1];
-             }
-         } else {
-             // make a copy of the matrix
-             Matrix_Delegate copy = new Matrix_Delegate(mValues);
-
-             // remove the translation
-             setTranslate(copy.mValues, 0, 0);
-
-             // map the content as points.
-             copy.mapPoints(dst, dstIndex, src, srcIndex, ptCount);
-         }
-     }
-
-    /**
-     * multiply two matrices and store them in a 3rd.
-     * <p/>This in effect does dest = a*b
-     * dest cannot be the same as a or b.
-     */
-     /*package*/ static void multiply(float dest[], float[] a, float[] b) {
-        // first row
-        dest[0] = b[0] * a[0] + b[1] * a[3] + b[2] * a[6];
-        dest[1] = b[0] * a[1] + b[1] * a[4] + b[2] * a[7];
-        dest[2] = b[0] * a[2] + b[1] * a[5] + b[2] * a[8];
-
-        // 2nd row
-        dest[3] = b[3] * a[0] + b[4] * a[3] + b[5] * a[6];
-        dest[4] = b[3] * a[1] + b[4] * a[4] + b[5] * a[7];
-        dest[5] = b[3] * a[2] + b[4] * a[5] + b[5] * a[8];
-
-        // 3rd row
-        dest[6] = b[6] * a[0] + b[7] * a[3] + b[8] * a[6];
-        dest[7] = b[6] * a[1] + b[7] * a[4] + b[8] * a[7];
-        dest[8] = b[6] * a[2] + b[7] * a[5] + b[8] * a[8];
-    }
-
-    /**
-     * Returns a matrix that represents a given translate
-     * @param dx
-     * @param dy
-     * @return
-     */
-    /*package*/ static float[] getTranslate(float dx, float dy) {
-        return setTranslate(new float[9], dx, dy);
-    }
-
-    /*package*/ static float[] setTranslate(float[] dest, float dx, float dy) {
-        dest[0] = 1;
-        dest[1] = 0;
-        dest[2] = dx;
-        dest[3] = 0;
-        dest[4] = 1;
-        dest[5] = dy;
-        dest[6] = 0;
-        dest[7] = 0;
-        dest[8] = 1;
-        return dest;
-    }
-
-    /*package*/ static float[] getScale(float sx, float sy) {
-        return new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
-    }
-
-    /**
-     * Returns a matrix that represents the given scale info.
-     * @param sx
-     * @param sy
-     * @param px
-     * @param py
-     */
-    /*package*/ static float[] getScale(float sx, float sy, float px, float py) {
-        float[] tmp = new float[9];
-        float[] tmp2 = new float[9];
-
-        // TODO: do it in one pass
-
-        // translate tmp so that the pivot is in 0,0
-        setTranslate(tmp, -px, -py);
-
-        // scale into tmp2
-        multiply(tmp2, tmp, getScale(sx, sy));
-
-        // translate back the pivot back into tmp
-        multiply(tmp, tmp2, getTranslate(px, py));
-
-        return tmp;
-    }
-
-
-    /*package*/ static float[] getRotate(float degrees) {
-        double rad = Math.toRadians(degrees);
-        float sin = (float)Math.sin(rad);
-        float cos = (float)Math.cos(rad);
-
-        return getRotate(sin, cos);
-    }
-
-    /*package*/ static float[] getRotate(float sin, float cos) {
-        return setRotate(new float[9], sin, cos);
-    }
-
-    /*package*/ static float[] setRotate(float[] dest, float degrees) {
-        double rad = Math.toRadians(degrees);
-        float sin = (float)Math.sin(rad);
-        float cos = (float)Math.cos(rad);
-
-        return setRotate(dest, sin, cos);
-    }
-
-    /*package*/ static float[] setRotate(float[] dest, float sin, float cos) {
-        dest[0] = cos;
-        dest[1] = -sin;
-        dest[2] = 0;
-        dest[3] = sin;
-        dest[4] = cos;
-        dest[5] = 0;
-        dest[6] = 0;
-        dest[7] = 0;
-        dest[8] = 1;
-        return dest;
-    }
-
-    /*package*/ static float[] getRotate(float degrees, float px, float py) {
-        float[] tmp = new float[9];
-        float[] tmp2 = new float[9];
-
-        // TODO: do it in one pass
-
-        // translate so that the pivot is in 0,0
-        setTranslate(tmp, -px, -py);
-
-        // rotate into tmp2
-        double rad = Math.toRadians(degrees);
-        float cos = (float)Math.cos(rad);
-        float sin = (float)Math.sin(rad);
-        multiply(tmp2, tmp, getRotate(sin, cos));
-
-        // translate back the pivot back into tmp
-        multiply(tmp, tmp2, getTranslate(px, py));
-
-        return tmp;
-    }
-
-    /*package*/ static float[] getSkew(float kx, float ky) {
-        return new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 };
-    }
-
-    /*package*/ static float[] getSkew(float kx, float ky, float px, float py) {
-        float[] tmp = new float[9];
-        float[] tmp2 = new float[9];
-
-        // TODO: do it in one pass
-
-        // translate so that the pivot is in 0,0
-        setTranslate(tmp, -px, -py);
-
-        // skew into tmp2
-        multiply(tmp2, tmp, new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
-        // translate back the pivot back into tmp
-        multiply(tmp, tmp2, getTranslate(px, py));
-
-        return tmp;
-    }
-}
diff --git a/bridge/src/android/graphics/NinePatch_Delegate.java b/bridge/src/android/graphics/NinePatch_Delegate.java
deleted file mode 100644
index 28e682b..0000000
--- a/bridge/src/android/graphics/NinePatch_Delegate.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.ninepatch.NinePatchChunk;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.drawable.NinePatchDrawable;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.lang.ref.SoftReference;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Delegate implementing the native methods of android.graphics.NinePatch
- *
- * Through the layoutlib_create tool, the original native methods of NinePatch have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
- * around to map int to instance of the delegate.
- *
- */
-public final class NinePatch_Delegate {
-
-    // ---- delegate manager ----
-    private static final DelegateManager<NinePatch_Delegate> sManager =
-            new DelegateManager<>(NinePatch_Delegate.class);
-
-    // ---- delegate helper data ----
-    /**
-     * Cache map for {@link NinePatchChunk}.
-     * When the chunks are created they are serialized into a byte[], and both are put
-     * in the cache, using a {@link SoftReference} for the chunk. The default Java classes
-     * for {@link NinePatch} and {@link NinePatchDrawable} only reference to the byte[] data, and
-     * provide this for drawing.
-     * Using the cache map allows us to not have to deserialize the byte[] back into a
-     * {@link NinePatchChunk} every time a rendering is done.
-     */
-    private final static Map<byte[], SoftReference<NinePatchChunk>> sChunkCache = new HashMap<>();
-
-    // ---- delegate data ----
-    private byte[] chunk;
-
-
-    // ---- Public Helper methods ----
-
-    /**
-     * Serializes the given chunk.
-     *
-     * @return the serialized data for the chunk.
-     */
-    public static byte[] serialize(NinePatchChunk chunk) {
-        // serialize the chunk to get a byte[]
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        ObjectOutputStream oos = null;
-        try {
-            oos = new ObjectOutputStream(baos);
-            oos.writeObject(chunk);
-        } catch (IOException e) {
-            Bridge.getLog().error(null, "Failed to serialize NinePatchChunk.", e, null,
-                    null /*data*/);
-            return null;
-        } finally {
-            if (oos != null) {
-                try {
-                    oos.close();
-                } catch (IOException ignored) {
-                }
-            }
-        }
-
-        // get the array and add it to the cache
-        byte[] array = baos.toByteArray();
-        sChunkCache.put(array, new SoftReference<>(chunk));
-        return array;
-    }
-
-    /**
-     * Returns a {@link NinePatchChunk} object for the given serialized representation.
-     *
-     * If the chunk is present in the cache then the object from the cache is returned, otherwise
-     * the array is deserialized into a {@link NinePatchChunk} object.
-     *
-     * @param array the serialized representation of the chunk.
-     * @return the NinePatchChunk or null if deserialization failed.
-     */
-    public static NinePatchChunk getChunk(byte[] array) {
-        SoftReference<NinePatchChunk> chunkRef = sChunkCache.get(array);
-        NinePatchChunk chunk = chunkRef == null ? null : chunkRef.get();
-        if (chunk == null) {
-            ByteArrayInputStream bais = new ByteArrayInputStream(array);
-            try (ObjectInputStream ois = new ObjectInputStream(bais)) {
-                chunk = (NinePatchChunk) ois.readObject();
-
-                // put back the chunk in the cache
-                if (chunk != null) {
-                    sChunkCache.put(array, new SoftReference<>(chunk));
-                }
-            } catch (IOException e) {
-                Bridge.getLog().error(ILayoutLog.TAG_BROKEN,
-                        "Failed to deserialize NinePatchChunk content.", e, null, null /*data*/);
-                return null;
-            } catch (ClassNotFoundException e) {
-                Bridge.getLog().error(ILayoutLog.TAG_BROKEN,
-                        "Failed to deserialize NinePatchChunk class.", e, null, null /*data*/);
-                return null;
-            }
-        }
-
-        return chunk;
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static boolean isNinePatchChunk(byte[] chunk) {
-        NinePatchChunk chunkObject = getChunk(chunk);
-        return chunkObject != null;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long validateNinePatchChunk(byte[] chunk) {
-        // the default JNI implementation only checks that the byte[] has the same
-        // size as the C struct it represent. Since we cannot do the same check (serialization
-        // will return different size depending on content), we do nothing.
-        NinePatch_Delegate newDelegate = new NinePatch_Delegate();
-        newDelegate.chunk = chunk;
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeFinalize(long nativeNinePatch) {
-        NinePatch_Delegate delegate = sManager.getDelegate(nativeNinePatch);
-        if (delegate != null && delegate.chunk != null) {
-            sChunkCache.remove(delegate.chunk);
-        }
-        sManager.removeJavaReferenceFor(nativeNinePatch);
-    }
-
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeGetTransparentRegion(long bitmapHandle, long chunk,
-            Rect location) {
-        return 0;
-    }
-
-    static byte[] getChunk(long nativeNinePatch) {
-        NinePatch_Delegate delegate = sManager.getDelegate(nativeNinePatch);
-        if (delegate != null) {
-            return delegate.chunk;
-        }
-        return null;
-    }
-
-    public static void clearCache() {
-        sChunkCache.clear();
-    }
-}
diff --git a/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java b/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java
deleted file mode 100644
index fa20746..0000000
--- a/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.PaintFlagsDrawFilter
- *
- * Through the layoutlib_create tool, the original native methods of PaintFlagsDrawFilter have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original PaintFlagsDrawFilter class.
- *
- * Because this extends {@link DrawFilter_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the DrawFilter classes will be added to the manager owned by
- * {@link DrawFilter_Delegate}.
- *
- * @see DrawFilter_Delegate
- *
- */
-public class PaintFlagsDrawFilter_Delegate extends DrawFilter_Delegate {
-
-    // ---- delegate data ----
-
-    // ---- Public Helper methods ----
-
-    @Override
-    public boolean isSupported() {
-        return false;
-    }
-
-    @Override
-    public String getSupportMessage() {
-        return "Paint Flags Draw Filters are not supported.";
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeConstructor(int clearBits, int setBits) {
-        PaintFlagsDrawFilter_Delegate newDelegate = new PaintFlagsDrawFilter_Delegate();
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/Paint_Delegate.java b/bridge/src/android/graphics/Paint_Delegate.java
deleted file mode 100644
index f6c6d71..0000000
--- a/bridge/src/android/graphics/Paint_Delegate.java
+++ /dev/null
@@ -1,1373 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.FontFamily_Delegate.FontVariant;
-import android.graphics.Paint.FontMetrics;
-import android.graphics.Paint.FontMetricsInt;
-import android.text.TextUtils;
-
-import java.awt.BasicStroke;
-import java.awt.Font;
-import java.awt.Shape;
-import java.awt.Stroke;
-import java.awt.Toolkit;
-import java.awt.geom.AffineTransform;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.Paint
- *
- * Through the layoutlib_create tool, the original native methods of Paint have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Paint class.
- *
- * @see DelegateManager
- *
- */
-public class Paint_Delegate {
-    private static final float DEFAULT_TEXT_SIZE = 20.f;
-    private static final float DEFAULT_TEXT_SCALE_X = 1.f;
-    private static final float DEFAULT_TEXT_SKEW_X = 0.f;
-
-    /**
-     * Class associating a {@link Font} and its {@link java.awt.FontMetrics}.
-     */
-    /*package*/ static final class FontInfo {
-        final Font mFont;
-        final java.awt.FontMetrics mMetrics;
-
-        FontInfo(@NonNull Font font, @NonNull java.awt.FontMetrics fontMetrics) {
-            this.mFont = font;
-            this.mMetrics = fontMetrics;
-        }
-    }
-
-    // ---- delegate manager ----
-    private static final DelegateManager<Paint_Delegate> sManager =
-            new DelegateManager<>(Paint_Delegate.class);
-    private static long sFinalizer = -1;
-
-    // ---- delegate helper data ----
-
-    // This list can contain null elements.
-    @Nullable
-    private List<FontInfo> mFonts;
-
-    // ---- delegate data ----
-    private int mFlags;
-    private int mColor;
-    private int mStyle;
-    private int mCap;
-    private int mJoin;
-    private int mTextAlign;
-    private Typeface_Delegate mTypeface;
-    private float mStrokeWidth;
-    private float mStrokeMiter;
-    private float mTextSize;
-    private float mTextScaleX;
-    private float mTextSkewX;
-    private int mHintingMode = Paint.HINTING_ON;
-    private int mStartHyphenEdit;
-    private int mEndHyphenEdit;
-    private float mLetterSpacing;  // not used in actual text rendering.
-    private float mWordSpacing;  // not used in actual text rendering.
-    // Variant of the font. A paint's variant can only be compact or elegant.
-    private FontVariant mFontVariant = FontVariant.COMPACT;
-
-    private int mPorterDuffMode = Xfermode.DEFAULT;
-    private ColorFilter_Delegate mColorFilter;
-    private Shader_Delegate mShader;
-    private PathEffect_Delegate mPathEffect;
-    private MaskFilter_Delegate mMaskFilter;
-
-    @SuppressWarnings("FieldCanBeLocal") // Used to store the locale for future use
-    private Locale mLocale = Locale.getDefault();
-
-    // ---- Public Helper methods ----
-
-    @Nullable
-    public static Paint_Delegate getDelegate(long native_paint) {
-        return sManager.getDelegate(native_paint);
-    }
-
-    /**
-     * Returns the list of {@link Font} objects.
-     */
-    @NonNull
-    public List<FontInfo> getFonts() {
-        Typeface_Delegate typeface = mTypeface;
-        if (typeface == null) {
-            if (Typeface.sDefaultTypeface == null) {
-                return Collections.emptyList();
-            }
-
-            typeface = Typeface_Delegate.getDelegate(Typeface.sDefaultTypeface.native_instance);
-        }
-
-        if (mFonts != null) {
-            return mFonts;
-        }
-
-        // Apply an optional transformation for skew and scale
-        AffineTransform affineTransform = mTextScaleX != 1.0 || mTextSkewX != 0 ?
-                new AffineTransform(mTextScaleX, mTextSkewX, 0, 1, 0, 0) :
-                null;
-
-        List<FontInfo> infoList = StreamSupport.stream(typeface.getFonts(mFontVariant).spliterator
-                (), false)
-                .filter(Objects::nonNull)
-                .map(font -> getFontInfo(font, mTextSize, affineTransform))
-                .collect(Collectors.toList());
-        mFonts = Collections.unmodifiableList(infoList);
-
-        return mFonts;
-    }
-
-    public boolean isAntiAliased() {
-        return (mFlags & Paint.ANTI_ALIAS_FLAG) != 0;
-    }
-
-    public boolean isFilterBitmap() {
-        return (mFlags & Paint.FILTER_BITMAP_FLAG) != 0;
-    }
-
-    public int getStyle() {
-        return mStyle;
-    }
-
-    public int getColor() {
-        return mColor;
-    }
-
-    public int getAlpha() {
-        return mColor >>> 24;
-    }
-
-    public void setAlpha(int alpha) {
-        mColor = (alpha << 24) | (mColor & 0x00FFFFFF);
-    }
-
-    public int getTextAlign() {
-        return mTextAlign;
-    }
-
-    public float getStrokeWidth() {
-        return mStrokeWidth;
-    }
-
-    /**
-     * returns the value of stroke miter needed by the java api.
-     */
-    public float getJavaStrokeMiter() {
-        return mStrokeMiter;
-    }
-
-    public int getJavaCap() {
-        switch (Paint.sCapArray[mCap]) {
-            case BUTT:
-                return BasicStroke.CAP_BUTT;
-            case ROUND:
-                return BasicStroke.CAP_ROUND;
-            default:
-            case SQUARE:
-                return BasicStroke.CAP_SQUARE;
-        }
-    }
-
-    public int getJavaJoin() {
-        switch (Paint.sJoinArray[mJoin]) {
-            default:
-            case MITER:
-                return BasicStroke.JOIN_MITER;
-            case ROUND:
-                return BasicStroke.JOIN_ROUND;
-            case BEVEL:
-                return BasicStroke.JOIN_BEVEL;
-        }
-    }
-
-    public Stroke getJavaStroke() {
-        if (mPathEffect != null) {
-            if (mPathEffect.isSupported()) {
-                Stroke stroke = mPathEffect.getStroke(this);
-                assert stroke != null;
-                //noinspection ConstantConditions
-                if (stroke != null) {
-                    return stroke;
-                }
-            } else {
-                Bridge.getLog().fidelityWarning(ILayoutLog.TAG_PATHEFFECT,
-                        mPathEffect.getSupportMessage(),
-                        null, null, null /*data*/);
-            }
-        }
-
-        // if no custom stroke as been set, set the default one.
-        return new BasicStroke(
-                    getStrokeWidth(),
-                    getJavaCap(),
-                    getJavaJoin(),
-                    getJavaStrokeMiter());
-    }
-
-    /**
-     * Returns the {@link PorterDuff.Mode} as an int
-     */
-    public int getPorterDuffMode() {
-        return mPorterDuffMode;
-    }
-
-    /**
-     * Returns the {@link ColorFilter} delegate or null if none have been set
-     *
-     * @return the delegate or null.
-     */
-    public ColorFilter_Delegate getColorFilter() {
-        return mColorFilter;
-    }
-
-    public void setColorFilter(long colorFilterPtr) {
-        mColorFilter = ColorFilter_Delegate.getDelegate(colorFilterPtr);
-    }
-
-    public void setShader(long shaderPtr) {
-        mShader = Shader_Delegate.getDelegate(shaderPtr);
-    }
-
-    /**
-     * Returns the {@link Shader} delegate or null if none have been set
-     *
-     * @return the delegate or null.
-     */
-    public Shader_Delegate getShader() {
-        return mShader;
-    }
-
-    /**
-     * Returns the {@link MaskFilter} delegate or null if none have been set
-     *
-     * @return the delegate or null.
-     */
-    public MaskFilter_Delegate getMaskFilter() {
-        return mMaskFilter;
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetFlags(long nativePaint) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return 0;
-        }
-
-        return delegate.mFlags;
-    }
-
-
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetFlags(long nativePaint, int flags) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.mFlags = flags;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetFilterBitmap(long nativePaint, boolean filter) {
-        setFlag(nativePaint, Paint.FILTER_BITMAP_FLAG, filter);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetHinting(long nativePaint) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return Paint.HINTING_ON;
-        }
-
-        return delegate.mHintingMode;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetHinting(long nativePaint, int mode) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.mHintingMode = mode;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetAntiAlias(long nativePaint, boolean aa) {
-        setFlag(nativePaint, Paint.ANTI_ALIAS_FLAG, aa);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetSubpixelText(long nativePaint,
-            boolean subpixelText) {
-        setFlag(nativePaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetUnderlineText(long nativePaint,
-            boolean underlineText) {
-        setFlag(nativePaint, Paint.UNDERLINE_TEXT_FLAG, underlineText);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetStrikeThruText(long nativePaint,
-            boolean strikeThruText) {
-        setFlag(nativePaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetFakeBoldText(long nativePaint,
-            boolean fakeBoldText) {
-        setFlag(nativePaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetDither(long nativePaint, boolean dither) {
-        setFlag(nativePaint, Paint.DITHER_FLAG, dither);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetLinearText(long nativePaint, boolean linearText) {
-        setFlag(nativePaint, Paint.LINEAR_TEXT_FLAG, linearText);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetColor(long paintPtr, long colorSpaceHandle, long color) {
-        Paint_Delegate delegate = sManager.getDelegate(paintPtr);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.mColor = Color.toArgb(color);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetColor(long paintPtr, int color) {
-        Paint_Delegate delegate = sManager.getDelegate(paintPtr);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.mColor = color;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetAlpha(long nativePaint, int a) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.setAlpha(a);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetStrokeWidth(long nativePaint) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return 1.f;
-        }
-
-        return delegate.mStrokeWidth;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetStrokeWidth(long nativePaint, float width) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.mStrokeWidth = width;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetStrokeMiter(long nativePaint) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return 1.f;
-        }
-
-        return delegate.mStrokeMiter;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetStrokeMiter(long nativePaint, float miter) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.mStrokeMiter = miter;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetShadowLayer(long paintPtr,
-            float radius, float dx, float dy, long colorSpaceHandle,
-            long shadowColor) {
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Paint.setShadowLayer is not supported.", null, null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nHasShadowLayer(long paint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Paint.hasShadowLayer is not supported.", null, null, null /*data*/);
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nIsElegantTextHeight(long nativePaint) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        return delegate != null && delegate.mFontVariant == FontVariant.ELEGANT;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetElegantTextHeight(long nativePaint,
-            boolean elegant) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.mFontVariant = elegant ? FontVariant.ELEGANT : FontVariant.COMPACT;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetTextSize(long nativePaint) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return 1.f;
-        }
-
-        return delegate.mTextSize;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetTextSize(long nativePaint, float textSize) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return;
-        }
-
-        if (delegate.mTextSize != textSize) {
-            delegate.mTextSize = textSize;
-            delegate.invalidateFonts();
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetTextScaleX(long nativePaint) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return 1.f;
-        }
-
-        return delegate.mTextScaleX;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetTextScaleX(long nativePaint, float scaleX) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return;
-        }
-
-        if (delegate.mTextScaleX != scaleX) {
-            delegate.mTextScaleX = scaleX;
-            delegate.invalidateFonts();
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetTextSkewX(long nativePaint) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return 1.f;
-        }
-
-        return delegate.mTextSkewX;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetTextSkewX(long nativePaint, float skewX) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return;
-        }
-
-        if (delegate.mTextSkewX != skewX) {
-            delegate.mTextSkewX = skewX;
-            delegate.invalidateFonts();
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nAscent(long nativePaint) {
-        // get the delegate
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return 0;
-        }
-
-        List<FontInfo> fonts = delegate.getFonts();
-        if (fonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = fonts.get(0).mMetrics;
-            // Android expects negative ascent so we invert the value from Java.
-            return - javaMetrics.getAscent();
-        }
-
-        return 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nDescent(long nativePaint) {
-        // get the delegate
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return 0;
-        }
-
-        List<FontInfo> fonts = delegate.getFonts();
-        if (fonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = fonts.get(0).mMetrics;
-            return javaMetrics.getDescent();
-        }
-
-        return 0;
-
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetFontMetrics(long nativePaint,
-            FontMetrics metrics) {
-        // get the delegate
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return 0;
-        }
-
-        return delegate.getFontMetrics(metrics);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetFontMetricsInt(long nativePaint, FontMetricsInt fmi) {
-        // get the delegate
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return 0;
-        }
-
-        List<FontInfo> fonts = delegate.getFonts();
-        if (fonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = fonts.get(0).mMetrics;
-            if (fmi != null) {
-                // Android expects negative ascent so we invert the value from Java.
-                fmi.top = (int)(- javaMetrics.getMaxAscent() * 1.15);
-                fmi.ascent = - javaMetrics.getAscent();
-                fmi.descent = javaMetrics.getDescent();
-                fmi.bottom = (int)(javaMetrics.getMaxDescent() * 1.15);
-                fmi.leading = javaMetrics.getLeading();
-            }
-
-            return javaMetrics.getHeight();
-        }
-
-        return 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nBreakText(long nativePaint, char[] text,
-            int index, int count, float maxWidth, int bidiFlags, float[] measuredWidth) {
-
-        // get the delegate
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return 0;
-        }
-
-        int inc = count > 0 ? 1 : -1;
-
-        int measureIndex = 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
-            RectF bounds = delegate.measureText(text, start, end - start + 1, null, 0, bidiFlags);
-            float res = bounds.right - bounds.left;
-
-            if (measuredWidth != null) {
-                measuredWidth[measureIndex] = res;
-            }
-
-            if (res > maxWidth) {
-                // we should not return this char index, but since it's 0-based
-                // and we need to return a count, we simply return measureIndex;
-                return measureIndex;
-            }
-
-        }
-
-        return measureIndex;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nBreakText(long nativePaint, String text, boolean measureForwards,
-            float maxWidth, int bidiFlags, float[] measuredWidth) {
-        return nBreakText(nativePaint, text.toCharArray(), 0, text.length(),
-                maxWidth, bidiFlags, measuredWidth);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nInit() {
-        Paint_Delegate newDelegate = new Paint_Delegate();
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nInitWithPaint(long paint) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(paint);
-        if (delegate == null) {
-            return 0;
-        }
-
-        Paint_Delegate newDelegate = new Paint_Delegate(delegate);
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nReset(long native_object) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.reset();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSet(long native_dst, long native_src) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate_dst = sManager.getDelegate(native_dst);
-        if (delegate_dst == null) {
-            return;
-        }
-
-        // get the delegate from the native int.
-        Paint_Delegate delegate_src = sManager.getDelegate(native_src);
-        if (delegate_src == null) {
-            return;
-        }
-
-        delegate_dst.set(delegate_src);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetStyle(long native_object) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return 0;
-        }
-
-        return delegate.mStyle;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetStyle(long native_object, int style) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.mStyle = style;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetStrokeCap(long native_object) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return 0;
-        }
-
-        return delegate.mCap;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetStrokeCap(long native_object, int cap) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.mCap = cap;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetStrokeJoin(long native_object) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return 0;
-        }
-
-        return delegate.mJoin;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetStrokeJoin(long native_object, int join) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.mJoin = join;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nGetFillPath(long native_object, long src, long dst) {
-        Paint_Delegate paint = sManager.getDelegate(native_object);
-        if (paint == null) {
-            return false;
-        }
-
-        Path_Delegate srcPath = Path_Delegate.getDelegate(src);
-        if (srcPath == null) {
-            return true;
-        }
-
-        Path_Delegate dstPath = Path_Delegate.getDelegate(dst);
-        if (dstPath == null) {
-            return true;
-        }
-
-        Stroke stroke = paint.getJavaStroke();
-        Shape strokeShape = stroke.createStrokedShape(srcPath.getJavaShape());
-
-        dstPath.setJavaShape(strokeShape);
-
-        // FIXME figure out the return value?
-        return true;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nSetShader(long native_object, long shader) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return shader;
-        }
-
-        delegate.mShader = Shader_Delegate.getDelegate(shader);
-
-        return shader;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nSetColorFilter(long native_object, long filter) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return filter;
-        }
-
-        delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);
-
-        // Log warning if it's not supported.
-        if (delegate.mColorFilter != null && !delegate.mColorFilter.isSupported()) {
-            Bridge.getLog().fidelityWarning(ILayoutLog.TAG_COLORFILTER,
-                    delegate.mColorFilter.getSupportMessage(), null, null, null /*data*/);
-        }
-
-        return filter;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetXfermode(long native_object, int xfermode) {
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return;
-        }
-        delegate.mPorterDuffMode = xfermode;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nSetPathEffect(long native_object, long effect) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return effect;
-        }
-
-        delegate.mPathEffect = PathEffect_Delegate.getDelegate(effect);
-
-        return effect;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nSetMaskFilter(long native_object, long maskfilter) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return maskfilter;
-        }
-
-        delegate.mMaskFilter = MaskFilter_Delegate.getDelegate(maskfilter);
-
-        // since none of those are supported, display a fidelity warning right away
-        if (delegate.mMaskFilter != null && !delegate.mMaskFilter.isSupported()) {
-            Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MASKFILTER,
-                    delegate.mMaskFilter.getSupportMessage(), null, null, null /*data*/);
-        }
-
-        return maskfilter;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetTypeface(long native_object, long typeface) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return;
-        }
-
-        Typeface_Delegate typefaceDelegate = Typeface_Delegate.getDelegate(typeface);
-        if (delegate.mTypeface != typefaceDelegate) {
-            delegate.mTypeface = typefaceDelegate;
-            delegate.invalidateFonts();
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetTextAlign(long native_object) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return 0;
-        }
-
-        return delegate.mTextAlign;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetTextAlign(long native_object, int align) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.mTextAlign = align;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nSetTextLocales(long native_object, String locale) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return 0;
-        }
-
-        delegate.setTextLocale(locale);
-        return 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetTextLocalesByMinikinLocaleListId(long paintPtr,
-            int mMinikinLangListId) {
-        // FIXME
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetTextAdvances(long native_object, char[] text, int index,
-            int count, int contextIndex, int contextCount,
-            int bidiFlags, float[] advances, int advancesIndex) {
-
-        if (advances != null)
-            for (int i = advancesIndex; i< advancesIndex+count; i++)
-                advances[i]=0;
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return 0.f;
-        }
-
-        RectF bounds = delegate.measureText(text, index, count, advances, advancesIndex, bidiFlags);
-        return bounds.right - bounds.left;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetTextAdvances(long native_object, String text, int start, int end,
-            int contextStart, int contextEnd, int bidiFlags, float[] advances, int advancesIndex) {
-        // FIXME: support contextStart and contextEnd
-        int count = end - start;
-        char[] buffer = TemporaryBuffer.obtain(count);
-        TextUtils.getChars(text, start, end, buffer, 0);
-
-        return nGetTextAdvances(native_object, buffer, 0, count,
-                contextStart, contextEnd - contextStart, bidiFlags, advances, advancesIndex);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, char[] text,
-            int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Paint.getTextRunCursor is not supported.", null, null, null /*data*/);
-        return 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, String text,
-            int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Paint.getTextRunCursor is not supported.", null, null, null /*data*/);
-        return 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nGetTextPath(long native_object, int bidiFlags, char[] text,
-            int index, int count, float x, float y, long path) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Paint.getTextPath is not supported.", null, null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nGetTextPath(long native_object, int bidiFlags, String text, int start,
-            int end, float x, float y, long path) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Paint.getTextPath is not supported.", null, null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nGetStringBounds(long nativePaint, String text, int start, int end,
-            int bidiFlags, Rect bounds) {
-        nGetCharArrayBounds(nativePaint, text.toCharArray(), start,
-                end - start, bidiFlags, bounds);
-    }
-
-    @LayoutlibDelegate
-    public static void nGetCharArrayBounds(long nativePaint, char[] text, int index,
-            int count, int bidiFlags, Rect bounds) {
-
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return;
-        }
-
-        delegate.measureText(text, index, count, null, 0, bidiFlags).roundOut(bounds);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nGetNativeFinalizer() {
-        synchronized (Paint_Delegate.class) {
-            if (sFinalizer == -1) {
-                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
-                        sManager::removeJavaReferenceFor);
-            }
-        }
-        return sFinalizer;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetLetterSpacing(long nativePaint) {
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return 0;
-        }
-        return delegate.mLetterSpacing;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetLetterSpacing(long nativePaint, float letterSpacing) {
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_TEXT_RENDERING,
-                "Paint.setLetterSpacing() not supported.", null, null, null);
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return;
-        }
-        delegate.mLetterSpacing = letterSpacing;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetWordSpacing(long nativePaint) {
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return 0;
-        }
-        return delegate.mWordSpacing;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetWordSpacing(long nativePaint, float wordSpacing) {
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return;
-        }
-        delegate.mWordSpacing = wordSpacing;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetFontFeatureSettings(long nativePaint, String settings) {
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_TEXT_RENDERING,
-                "Paint.setFontFeatureSettings() not supported.", null, null, null);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetStartHyphenEdit(long nativePaint) {
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return 0;
-        }
-        return delegate.mStartHyphenEdit;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetStartHyphenEdit(long nativePaint, int hyphen) {
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return;
-        }
-        delegate.mStartHyphenEdit = hyphen;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetEndHyphenEdit(long nativePaint) {
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return 0;
-        }
-        return delegate.mEndHyphenEdit;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetEndHyphenEdit(long nativePaint, int hyphen) {
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return;
-        }
-        delegate.mEndHyphenEdit = hyphen;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nHasGlyph(long nativePaint, int bidiFlags, String string) {
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return false;
-        }
-        if (string.length() == 0) {
-            return false;
-        }
-        if (string.length() > 1) {
-            Bridge.getLog().fidelityWarning(ILayoutLog.TAG_TEXT_RENDERING,
-                    "Paint.hasGlyph() is not supported for ligatures.", null, null, null);
-            return false;
-        }
-
-        char c = string.charAt(0);
-        for (Font font : delegate.mTypeface.getFonts(delegate.mFontVariant)) {
-            if (font.canDisplay(c)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetRunAdvance(long nativePaint, @NonNull char[] text, int start,
-            int end, int contextStart, int contextEnd,
-            boolean isRtl, int offset) {
-        int count = end - start;
-        float[] advances = new float[count];
-        int bidiFlags = isRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR;
-        nGetTextAdvances(nativePaint, text, start, count, contextStart,
-                contextEnd - contextStart, bidiFlags, advances, 0);
-        int startOffset = offset - start;  // offset from start.
-        float sum = 0;
-        for (int i = 0; i < startOffset; i++) {
-            sum += advances[i];
-        }
-        return sum;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetOffsetForAdvance(long nativePaint, char[] text, int start,
-            int end, int contextStart, int contextEnd, boolean isRtl, float advance) {
-        int count = end - start;
-        float[] advances = new float[count];
-        int bidiFlags = isRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR;
-        nGetTextAdvances(nativePaint, text, start, count, contextStart,
-                contextEnd - contextStart, bidiFlags, advances, 0);
-        float sum = 0;
-        int i;
-        for (i = 0; i < count && sum < advance; i++) {
-            sum += advances[i];
-        }
-        float distanceToI = sum - advance;
-        float distanceToIMinus1 = advance - (sum - advances[i]);
-        return distanceToI > distanceToIMinus1 ? i : i - 1;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetUnderlinePosition(long paintPtr) {
-        return (1.0f / 9.0f) * nGetTextSize(paintPtr);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetUnderlineThickness(long paintPtr) {
-        return (1.0f / 18.0f) * nGetTextSize(paintPtr);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetStrikeThruPosition(long paintPtr) {
-        return (-79.0f / 252.0f) * nGetTextSize(paintPtr);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetStrikeThruThickness(long paintPtr) {
-        return (1.0f / 18.0f) * nGetTextSize(paintPtr);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr) {
-        return leftPaintPtr == rightPaintPtr;
-    }
-
-    // ---- Private delegate/helper methods ----
-
-    /*package*/ Paint_Delegate() {
-        reset();
-    }
-
-    private Paint_Delegate(Paint_Delegate paint) {
-        set(paint);
-    }
-
-    private void set(Paint_Delegate paint) {
-        mFlags = paint.mFlags;
-        mColor = paint.mColor;
-        mStyle = paint.mStyle;
-        mCap = paint.mCap;
-        mJoin = paint.mJoin;
-        mTextAlign = paint.mTextAlign;
-
-        if (mTypeface != paint.mTypeface) {
-            mTypeface = paint.mTypeface;
-            invalidateFonts();
-        }
-
-        if (mTextSize != paint.mTextSize) {
-            mTextSize = paint.mTextSize;
-            invalidateFonts();
-        }
-
-        if (mTextScaleX != paint.mTextScaleX) {
-            mTextScaleX = paint.mTextScaleX;
-            invalidateFonts();
-        }
-
-        if (mTextSkewX != paint.mTextSkewX) {
-            mTextSkewX = paint.mTextSkewX;
-            invalidateFonts();
-        }
-
-        mStrokeWidth = paint.mStrokeWidth;
-        mStrokeMiter = paint.mStrokeMiter;
-        mPorterDuffMode = paint.mPorterDuffMode;
-        mColorFilter = paint.mColorFilter;
-        mShader = paint.mShader;
-        mPathEffect = paint.mPathEffect;
-        mMaskFilter = paint.mMaskFilter;
-        mHintingMode = paint.mHintingMode;
-    }
-
-    private void reset() {
-        Typeface_Delegate defaultTypeface =
-                Typeface_Delegate.getDelegate(Typeface.sDefaults[0].native_instance);
-
-        mFlags = Paint.HIDDEN_DEFAULT_PAINT_FLAGS;
-        mColor = 0xFF000000;
-        mStyle = Paint.Style.FILL.nativeInt;
-        mCap = Paint.Cap.BUTT.nativeInt;
-        mJoin = Paint.Join.MITER.nativeInt;
-        mTextAlign = 0;
-
-        if (mTypeface != defaultTypeface) {
-            mTypeface = defaultTypeface;
-            invalidateFonts();
-        }
-
-        mStrokeWidth = 1.f;
-        mStrokeMiter = 4.f;
-
-        if (mTextSize != DEFAULT_TEXT_SIZE) {
-            mTextSize = DEFAULT_TEXT_SIZE;
-            invalidateFonts();
-        }
-
-        if (mTextScaleX != DEFAULT_TEXT_SCALE_X) {
-            mTextScaleX = DEFAULT_TEXT_SCALE_X;
-            invalidateFonts();
-        }
-
-        if (mTextSkewX != DEFAULT_TEXT_SKEW_X) {
-            mTextSkewX = DEFAULT_TEXT_SKEW_X;
-            invalidateFonts();
-        }
-
-        mPorterDuffMode = Xfermode.DEFAULT;
-        mColorFilter = null;
-        mShader = null;
-        mPathEffect = null;
-        mMaskFilter = null;
-        mHintingMode = Paint.HINTING_ON;
-    }
-
-    private void invalidateFonts() {
-        mFonts = null;
-    }
-
-    @Nullable
-    private static FontInfo getFontInfo(@Nullable Font font, float textSize,
-            @Nullable AffineTransform transform) {
-        if (font == null) {
-            return null;
-        }
-
-        Font transformedFont = font.deriveFont(textSize);
-        if (transform != null) {
-            // TODO: support skew
-            transformedFont = transformedFont.deriveFont(transform);
-        }
-
-        // The metrics here don't have anti-aliasing set.
-        return new FontInfo(transformedFont,
-                Toolkit.getDefaultToolkit().getFontMetrics(transformedFont));
-    }
-
-    /*package*/ RectF measureText(char[] text, int index, int count, float[] advances,
-            int advancesIndex, int bidiFlags) {
-        return new BidiRenderer(null, this, text)
-                .renderText(index, index + count, bidiFlags, advances, advancesIndex, false);
-    }
-
-    /*package*/ RectF measureText(char[] text, int index, int count, float[] advances,
-            int advancesIndex, boolean isRtl) {
-        return new BidiRenderer(null, this, text)
-                .renderText(index, index + count, isRtl, advances, advancesIndex, false);
-    }
-
-    private float getFontMetrics(FontMetrics metrics) {
-        List<FontInfo> fonts = getFonts();
-        if (fonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = fonts.get(0).mMetrics;
-            if (metrics != null) {
-                // Android expects negative ascent so we invert the value from Java.
-                metrics.top = - javaMetrics.getMaxAscent();
-                metrics.ascent = - javaMetrics.getAscent();
-                metrics.descent = javaMetrics.getDescent();
-                metrics.bottom = javaMetrics.getMaxDescent();
-                metrics.leading = javaMetrics.getLeading();
-            }
-
-            return javaMetrics.getHeight();
-        }
-
-        return 0;
-    }
-
-    private void setTextLocale(String locale) {
-        mLocale = new Locale(locale);
-    }
-
-    private static void setFlag(long nativePaint, int flagMask, boolean flagValue) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return;
-        }
-
-        if (flagValue) {
-            delegate.mFlags |= flagMask;
-        } else {
-            delegate.mFlags &= ~flagMask;
-        }
-    }
-}
diff --git a/bridge/src/android/graphics/PathDashPathEffect_Delegate.java b/bridge/src/android/graphics/PathDashPathEffect_Delegate.java
deleted file mode 100644
index fd9ba62..0000000
--- a/bridge/src/android/graphics/PathDashPathEffect_Delegate.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Stroke;
-
-/**
- * Delegate implementing the native methods of android.graphics.PathDashPathEffect
- *
- * Through the layoutlib_create tool, the original native methods of PathDashPathEffect have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original PathDashPathEffect class.
- *
- * Because this extends {@link PathEffect_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link PathEffect_Delegate}.
- *
- * @see PathEffect_Delegate
- *
- */
-public class PathDashPathEffect_Delegate extends PathEffect_Delegate {
-
-    // ---- delegate data ----
-
-    // ---- Public Helper methods ----
-
-    @Override
-    public Stroke getStroke(Paint_Delegate paint) {
-        // FIXME
-        return null;
-    }
-
-    @Override
-    public boolean isSupported() {
-        return false;
-    }
-
-    @Override
-    public String getSupportMessage() {
-        return "Path Dash Path Effects are not supported in Layout Preview mode.";
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreate(long native_path, float advance, float phase,
-            int native_style) {
-        PathDashPathEffect_Delegate newDelegate = new PathDashPathEffect_Delegate();
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/PathEffect_Delegate.java b/bridge/src/android/graphics/PathEffect_Delegate.java
deleted file mode 100644
index 000481e..0000000
--- a/bridge/src/android/graphics/PathEffect_Delegate.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Stroke;
-
-/**
- * Delegate implementing the native methods of android.graphics.PathEffect
- *
- * Through the layoutlib_create tool, the original native methods of PathEffect have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original PathEffect class.
- *
- * This also serve as a base class for all PathEffect delegate classes.
- *
- * @see DelegateManager
- *
- */
-public abstract class PathEffect_Delegate {
-
-    // ---- delegate manager ----
-    protected static final DelegateManager<PathEffect_Delegate> sManager =
-            new DelegateManager<PathEffect_Delegate>(PathEffect_Delegate.class);
-
-    // ---- delegate helper data ----
-
-    // ---- delegate data ----
-
-    // ---- Public Helper methods ----
-
-    public static PathEffect_Delegate getDelegate(long nativeShader) {
-        return sManager.getDelegate(nativeShader);
-    }
-
-    public abstract Stroke getStroke(Paint_Delegate paint);
-    public abstract boolean isSupported();
-    public abstract String getSupportMessage();
-
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeDestructor(long native_patheffect) {
-        sManager.removeJavaReferenceFor(native_patheffect);
-    }
-
-    // ---- Private delegate/helper methods ----
-
-}
diff --git a/bridge/src/android/graphics/PathMeasure_Delegate.java b/bridge/src/android/graphics/PathMeasure_Delegate.java
deleted file mode 100644
index 83a4ff1..0000000
--- a/bridge/src/android/graphics/PathMeasure_Delegate.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.util.CachedPathIteratorFactory;
-import com.android.layoutlib.bridge.util.CachedPathIteratorFactory.CachedPathIterator;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.geom.PathIterator;
-
-/**
- * Delegate implementing the native methods of {@link android.graphics.PathMeasure}
- * <p/>
- * Through the layoutlib_create tool, the original native methods of PathMeasure have been
- * replaced by
- * calls to methods of the same name in this delegate class.
- * <p/>
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between it
- * and the original PathMeasure class.
- *
- * @see DelegateManager
- */
-public final class PathMeasure_Delegate {
-
-    // ---- delegate manager ----
-    private static final DelegateManager<PathMeasure_Delegate> sManager =
-            new DelegateManager<PathMeasure_Delegate>(PathMeasure_Delegate.class);
-
-    // ---- delegate data ----
-    private CachedPathIteratorFactory mOriginalPathIterator;
-
-    private long mNativePath;
-
-
-    private PathMeasure_Delegate(long native_path, boolean forceClosed) {
-        mNativePath = native_path;
-        if (native_path != 0) {
-            if (forceClosed) {
-                // Copy the path and call close
-                native_path = Path_Delegate.nInit(native_path);
-                Path_Delegate.nClose(native_path);
-            }
-
-            Path_Delegate pathDelegate = Path_Delegate.getDelegate(native_path);
-            mOriginalPathIterator = new CachedPathIteratorFactory(pathDelegate.getJavaShape()
-                    .getPathIterator(null));
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long native_create(long native_path, boolean forceClosed) {
-        return sManager.addNewDelegate(new PathMeasure_Delegate(native_path, forceClosed));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void native_destroy(long native_instance) {
-        sManager.removeJavaReferenceFor(native_instance);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean native_getPosTan(long native_instance, float distance, float pos[],
-            float tan[]) {
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "PathMeasure.getPostTan is not supported.", null, null, null);
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean native_getMatrix(long native_instance, float distance, long
-            native_matrix, int flags) {
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "PathMeasure.getMatrix is not supported.", null, null, null);
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean native_nextContour(long native_instance) {
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "PathMeasure.nextContour is not supported.", null, null, null);
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void native_setPath(long native_instance, long native_path, boolean
-            forceClosed) {
-        PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
-        assert pathMeasure != null;
-
-        if (native_path != 0) {
-            if (forceClosed) {
-                // Copy the path and call close
-                native_path = Path_Delegate.nInit(native_path);
-                Path_Delegate.nClose(native_path);
-            }
-
-            Path_Delegate pathDelegate = Path_Delegate.getDelegate(native_path);
-            pathMeasure.mOriginalPathIterator = new CachedPathIteratorFactory(pathDelegate.getJavaShape()
-                    .getPathIterator(null));
-        }
-
-        pathMeasure.mNativePath = native_path;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float native_getLength(long native_instance) {
-        PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
-        assert pathMeasure != null;
-
-        if (pathMeasure.mOriginalPathIterator == null) {
-            return 0;
-        }
-
-        return pathMeasure.mOriginalPathIterator.iterator().getTotalLength();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean native_isClosed(long native_instance) {
-        PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
-        assert pathMeasure != null;
-
-        Path_Delegate path = Path_Delegate.getDelegate(pathMeasure.mNativePath);
-        if (path == null) {
-            return false;
-        }
-
-        int type = 0;
-        float segment[] = new float[6];
-        for (PathIterator pi = path.getJavaShape().getPathIterator(null); !pi.isDone(); pi.next()) {
-            type = pi.currentSegment(segment);
-        }
-
-        // A path is a closed path if the last element is SEG_CLOSE
-        return type == PathIterator.SEG_CLOSE;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean native_getSegment(long native_instance, float startD, float stopD,
-            long native_dst_path, boolean startWithMoveTo) {
-        if (startD < 0) {
-            startD = 0;
-        }
-
-        if (startD >= stopD) {
-            return false;
-        }
-
-        PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
-        assert pathMeasure != null;
-
-        CachedPathIterator iterator = pathMeasure.mOriginalPathIterator.iterator();
-        float accLength = startD;
-        boolean isZeroLength = true; // Whether the output has zero length or not
-        float[] points = new float[6];
-
-        iterator.jumpToSegment(accLength);
-        while (!iterator.isDone() && (stopD - accLength > 0.1f)) {
-            int type = iterator.currentSegment(points, stopD - accLength);
-
-            if (accLength - iterator.getCurrentSegmentLength() <= stopD) {
-                if (startWithMoveTo) {
-                    startWithMoveTo = false;
-
-                    // If this segment is a MOVETO, then we just use that one. If not, then we issue
-                    // a first moveto
-                    if (type != PathIterator.SEG_MOVETO) {
-                        float[] lastPoint = new float[2];
-                        iterator.getCurrentSegmentEnd(lastPoint);
-                        Path_Delegate.nMoveTo(native_dst_path, lastPoint[0], lastPoint[1]);
-                    }
-                }
-
-                isZeroLength = isZeroLength && iterator.getCurrentSegmentLength() > 0;
-                switch (type) {
-                    case PathIterator.SEG_MOVETO:
-                        Path_Delegate.nMoveTo(native_dst_path, points[0], points[1]);
-                        break;
-                    case PathIterator.SEG_LINETO:
-                        Path_Delegate.nLineTo(native_dst_path, points[0], points[1]);
-                        break;
-                    case PathIterator.SEG_CLOSE:
-                        Path_Delegate.nClose(native_dst_path);
-                        break;
-                    case PathIterator.SEG_CUBICTO:
-                        Path_Delegate.nCubicTo(native_dst_path, points[0], points[1],
-                                points[2], points[3],
-                                points[4], points[5]);
-                        break;
-                    case PathIterator.SEG_QUADTO:
-                        Path_Delegate.nQuadTo(native_dst_path, points[0], points[1],
-                                points[2],
-                                points[3]);
-                        break;
-                    default:
-                        assert false;
-                }
-            }
-
-            accLength += iterator.getCurrentSegmentLength();
-            iterator.next();
-        }
-
-        return !isZeroLength;
-    }
-}
diff --git a/bridge/src/android/graphics/Path_Delegate.java b/bridge/src/android/graphics/Path_Delegate.java
index 88f2815..6e84eea 100644
--- a/bridge/src/android/graphics/Path_Delegate.java
+++ b/bridge/src/android/graphics/Path_Delegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -16,894 +16,12 @@
 
 package android.graphics;
 
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.graphics.Path.Direction;
-import android.graphics.Path.FillType;
-
-import java.awt.Shape;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Arc2D;
-import java.awt.geom.Area;
-import java.awt.geom.Ellipse2D;
-import java.awt.geom.GeneralPath;
-import java.awt.geom.Path2D;
-import java.awt.geom.PathIterator;
-import java.awt.geom.Point2D;
-import java.awt.geom.Rectangle2D;
-import java.awt.geom.RoundRectangle2D;
-import java.util.ArrayList;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.Path
- *
- * Through the layoutlib_create tool, the original native methods of Path have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Path class.
- *
- * @see DelegateManager
- *
- */
-public final class Path_Delegate {
-
-    // ---- delegate manager ----
-    private static final DelegateManager<Path_Delegate> sManager =
-            new DelegateManager<Path_Delegate>(Path_Delegate.class);
-
-    private static final float EPSILON = 1e-4f;
-
-    private static long sFinalizer = -1;
-
-    // ---- delegate data ----
-    private FillType mFillType = FillType.WINDING;
-    private Path2D mPath = new Path2D.Double();
-
-    private float mLastX = 0;
-    private float mLastY = 0;
-
-    // true if the path contains does not contain a curve or line.
-    private boolean mCachedIsEmpty = true;
-
-    // ---- Public Helper methods ----
-
-    public static Path_Delegate getDelegate(long nPath) {
-        return sManager.getDelegate(nPath);
-    }
-
-    public Path2D getJavaShape() {
-        return mPath;
-    }
-
-    public void setJavaShape(Shape shape) {
-        reset();
-        mPath.append(shape, false /*connect*/);
-    }
-
-    public void reset() {
-        mPath.reset();
-        mLastX = 0;
-        mLastY = 0;
-    }
-
-    public void setPathIterator(PathIterator iterator) {
-        reset();
-        mPath.append(iterator, false /*connect*/);
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nInit() {
-        // create the delegate
-        Path_Delegate newDelegate = new Path_Delegate();
-
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nInit(long nPath) {
-        // create the delegate
-        Path_Delegate newDelegate = new Path_Delegate();
-
-        // get the delegate to copy, which could be null if nPath is 0
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate != null) {
-            newDelegate.set(pathDelegate);
+public class Path_Delegate {
+    static long nInit(long nPath) {
+        if (nPath == 0) {
+            throw new IllegalArgumentException(
+                    "A null argument to the Path constructor causes a crash in Android");
         }
-
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nReset(long nPath) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.reset();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nRewind(long nPath) {
-        // call out to reset since there's nothing to optimize in
-        // terms of data structs.
-        nReset(nPath);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSet(long native_dst, long nSrc) {
-        Path_Delegate pathDstDelegate = sManager.getDelegate(native_dst);
-        if (pathDstDelegate == null) {
-            return;
-        }
-
-        Path_Delegate pathSrcDelegate = sManager.getDelegate(nSrc);
-        if (pathSrcDelegate == null) {
-            return;
-        }
-
-        pathDstDelegate.set(pathSrcDelegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nIsConvex(long nPath) {
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Path.isConvex is not supported.", null, null, null);
-        return true;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetFillType(long nPath) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return 0;
-        }
-
-        return pathDelegate.mFillType.nativeInt;
-    }
-
-    @LayoutlibDelegate
-    public static void nSetFillType(long nPath, int ft) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.setFillType(Path.sFillTypeArray[ft]);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nIsEmpty(long nPath) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        return pathDelegate == null || pathDelegate.isEmpty();
-
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nIsRect(long nPath, RectF rect) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return false;
-        }
-
-        // create an Area that can test if the path is a rect
-        Area area = new Area(pathDelegate.mPath);
-        if (area.isRectangular()) {
-            if (rect != null) {
-                pathDelegate.fillBounds(rect);
-            }
-
-            return true;
-        }
-
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nComputeBounds(long nPath, RectF bounds) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.fillBounds(bounds);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nIncReserve(long nPath, int extraPtCount) {
-        // since we use a java2D path, there's no way to pre-allocate new points,
-        // so we do nothing.
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nMoveTo(long nPath, float x, float y) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.moveTo(x, y);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nRMoveTo(long nPath, float dx, float dy) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.rMoveTo(dx, dy);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nLineTo(long nPath, float x, float y) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.lineTo(x, y);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nRLineTo(long nPath, float dx, float dy) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.rLineTo(dx, dy);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nQuadTo(long nPath, float x1, float y1, float x2, float y2) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.quadTo(x1, y1, x2, y2);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nRQuadTo(long nPath, float dx1, float dy1, float dx2, float dy2) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.rQuadTo(dx1, dy1, dx2, dy2);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nCubicTo(long nPath, float x1, float y1,
-            float x2, float y2, float x3, float y3) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.cubicTo(x1, y1, x2, y2, x3, y3);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nRCubicTo(long nPath, float x1, float y1,
-            float x2, float y2, float x3, float y3) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.rCubicTo(x1, y1, x2, y2, x3, y3);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nArcTo(long nPath, float left, float top, float right,
-            float bottom,
-                    float startAngle, float sweepAngle, boolean forceMoveTo) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.arcTo(left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nClose(long nPath) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.close();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nAddRect(long nPath,
-            float left, float top, float right, float bottom, int dir) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.addRect(left, top, right, bottom, dir);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nAddOval(long nPath, float left, float top, float right,
-            float bottom, int dir) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.mPath.append(new Ellipse2D.Float(
-                left, top, right - left, bottom - top), false);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nAddCircle(long nPath, float x, float y, float radius, int dir) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        // because x/y is the center of the circle, need to offset this by the radius
-        pathDelegate.mPath.append(new Ellipse2D.Float(
-                x - radius, y - radius, radius * 2, radius * 2), false);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nAddArc(long nPath, float left, float top, float right,
-            float bottom, float startAngle, float sweepAngle) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        // because x/y is the center of the circle, need to offset this by the radius
-        pathDelegate.mPath.append(new Arc2D.Float(
-                left, top, right - left, bottom - top,
-                -startAngle, -sweepAngle, Arc2D.OPEN), false);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nAddRoundRect(long nPath, float left, float top, float right,
-            float bottom, float rx, float ry, int dir) {
-
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.mPath.append(new RoundRectangle2D.Float(
-                left, top, right - left, bottom - top, rx * 2, ry * 2), false);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nAddRoundRect(long nPath, float left, float top, float right,
-            float bottom, float[] radii, int dir) {
-
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        float[] cornerDimensions = new float[radii.length];
-        for (int i = 0; i < radii.length; i++) {
-            cornerDimensions[i] = 2 * radii[i];
-        }
-        pathDelegate.mPath.append(new RoundRectangle(left, top, right - left, bottom - top,
-                cornerDimensions), false);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nAddPath(long nPath, long src, float dx, float dy) {
-        addPath(nPath, src, AffineTransform.getTranslateInstance(dx, dy));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nAddPath(long nPath, long src) {
-        addPath(nPath, src, null /*transform*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nAddPath(long nPath, long src, long matrix) {
-        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix);
-        if (matrixDelegate == null) {
-            return;
-        }
-
-        addPath(nPath, src, matrixDelegate.getAffineTransform());
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nOffset(long nPath, float dx, float dy) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.offset(dx, dy);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetLastPoint(long nPath, float dx, float dy) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        pathDelegate.mLastX = dx;
-        pathDelegate.mLastY = dy;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nTransform(long nPath, long matrix,
-                                                long dst_path) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix);
-        if (matrixDelegate == null) {
-            return;
-        }
-
-        // this can be null if dst_path is 0
-        Path_Delegate dstDelegate = sManager.getDelegate(dst_path);
-
-        pathDelegate.transform(matrixDelegate, dstDelegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nTransform(long nPath, long matrix) {
-        nTransform(nPath, matrix, 0);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nOp(long nPath1, long nPath2, int op, long result) {
-        Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED, "Path.op() not supported", null, null);
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nGetFinalizer() {
-        synchronized (Path_Delegate.class) {
-            if (sFinalizer == -1) {
-                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
-                        sManager::removeJavaReferenceFor);
-            }
-        }
-        return sFinalizer;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float[] nApproximate(long nPath, float error) {
-        Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return null;
-        }
-        // Get a FlatteningIterator
-        PathIterator iterator = pathDelegate.getJavaShape().getPathIterator(null, error);
-
-        float segment[] = new float[6];
-        float totalLength = 0;
-        ArrayList<Point2D.Float> points = new ArrayList<Point2D.Float>();
-        Point2D.Float previousPoint = null;
-        while (!iterator.isDone()) {
-            int type = iterator.currentSegment(segment);
-            Point2D.Float currentPoint = new Point2D.Float(segment[0], segment[1]);
-            // MoveTo shouldn't affect the length
-            if (previousPoint != null && type != PathIterator.SEG_MOVETO) {
-                totalLength += currentPoint.distance(previousPoint);
-            }
-            previousPoint = currentPoint;
-            points.add(currentPoint);
-            iterator.next();
-        }
-
-        int nPoints = points.size();
-        float[] result = new float[nPoints * 3];
-        previousPoint = null;
-        // Distance that we've covered so far. Used to calculate the fraction of the path that
-        // we've covered up to this point.
-        float walkedDistance = .0f;
-        for (int i = 0; i < nPoints; i++) {
-            Point2D.Float point = points.get(i);
-            float distance = previousPoint != null ? (float) previousPoint.distance(point) : .0f;
-            walkedDistance += distance;
-            result[i * 3] = walkedDistance / totalLength;
-            result[i * 3 + 1] = point.x;
-            result[i * 3 + 2] = point.y;
-
-            previousPoint = point;
-        }
-
-        return result;
-    }
-
-    // ---- Private helper methods ----
-
-    private void set(Path_Delegate delegate) {
-        mPath.reset();
-        setFillType(delegate.mFillType);
-        mPath.append(delegate.mPath, false /*connect*/);
-    }
-
-    private void setFillType(FillType fillType) {
-        mFillType = fillType;
-        mPath.setWindingRule(getWindingRule(fillType));
-    }
-
-    /**
-     * Returns the Java2D winding rules matching a given Android {@link FillType}.
-     * @param type the android fill type
-     * @return the matching java2d winding rule.
-     */
-    private static int getWindingRule(FillType type) {
-        switch (type) {
-            case WINDING:
-            case INVERSE_WINDING:
-                return GeneralPath.WIND_NON_ZERO;
-            case EVEN_ODD:
-            case INVERSE_EVEN_ODD:
-                return GeneralPath.WIND_EVEN_ODD;
-
-            default:
-                assert false;
-                return GeneralPath.WIND_NON_ZERO;
-        }
-    }
-
-    @NonNull
-    private static Direction getDirection(int direction) {
-        for (Direction d : Direction.values()) {
-            if (direction == d.nativeInt) {
-                return d;
-            }
-        }
-
-        assert false;
-        return null;
-    }
-
-    public static void addPath(long destPath, long srcPath, AffineTransform transform) {
-        Path_Delegate destPathDelegate = sManager.getDelegate(destPath);
-        if (destPathDelegate == null) {
-            return;
-        }
-
-        Path_Delegate srcPathDelegate = sManager.getDelegate(srcPath);
-        if (srcPathDelegate == null) {
-            return;
-        }
-
-        if (transform != null) {
-            destPathDelegate.mPath.append(
-                    srcPathDelegate.mPath.getPathIterator(transform), false);
-        } else {
-            destPathDelegate.mPath.append(srcPathDelegate.mPath, false);
-        }
-    }
-
-
-    /**
-     * Returns whether the path already contains any points.
-     * Note that this is different to
-     * {@link #isEmpty} because if all elements are {@link PathIterator#SEG_MOVETO},
-     * {@link #isEmpty} will return true while hasPoints will return false.
-     */
-    public boolean hasPoints() {
-        return !mPath.getPathIterator(null).isDone();
-    }
-
-    /**
-     * Returns whether the path is empty (contains no lines or curves).
-     * @see Path#isEmpty
-     */
-    public boolean isEmpty() {
-        if (!mCachedIsEmpty) {
-            return false;
-        }
-
-        float[] coords = new float[6];
-        mCachedIsEmpty = Boolean.TRUE;
-        for (PathIterator it = mPath.getPathIterator(null); !it.isDone(); it.next()) {
-            int type = it.currentSegment(coords);
-            if (type != PathIterator.SEG_MOVETO) {
-                // Once we know that the path is not empty, we do not need to check again unless
-                // Path#reset is called.
-                mCachedIsEmpty = false;
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Fills the given {@link RectF} with the path bounds.
-     * @param bounds the RectF to be filled.
-     */
-    public void fillBounds(RectF bounds) {
-        Rectangle2D rect = mPath.getBounds2D();
-        bounds.left = (float)rect.getMinX();
-        bounds.right = (float)rect.getMaxX();
-        bounds.top = (float)rect.getMinY();
-        bounds.bottom = (float)rect.getMaxY();
-    }
-
-    /**
-     * Set the beginning of the next contour to the point (x,y).
-     *
-     * @param x The x-coordinate of the start of a new contour
-     * @param y The y-coordinate of the start of a new contour
-     */
-    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) {
-        if (!hasPoints()) {
-            mPath.moveTo(mLastX = 0, mLastY = 0);
-        }
-        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 (!hasPoints()) {
-            mPath.moveTo(mLastX = 0, mLastY = 0);
-        }
-
-        if (Math.abs(dx) < EPSILON && Math.abs(dy) < EPSILON) {
-            // The delta is so small that this shouldn't generate a line
-            return;
-        }
-
-        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 (!hasPoints()) {
-            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) {
-        if (!hasPoints()) {
-            mPath.moveTo(0, 0);
-        }
-        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 (!hasPoints()) {
-            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 left        The left of oval defining shape and size of the arc
-     * @param top         The top of oval defining shape and size of the arc
-     * @param right       The right of oval defining shape and size of the arc
-     * @param bottom      The bottom 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(float left, float top, float right, float bottom, float startAngle,
-            float sweepAngle,
-            boolean forceMoveTo) {
-        Arc2D arc = new Arc2D.Float(left, top, right - left, bottom - top, -startAngle,
-                -sweepAngle, Arc2D.OPEN);
-        mPath.append(arc, true /*connect*/);
-
-        resetLastPointFromPath();
-    }
-
-    /**
-     * Close the current contour. If the current point is not equal to the
-     * first point of the contour, a line segment is automatically added.
-     */
-    public void close() {
-        mPath.closePath();
-    }
-
-    private void resetLastPointFromPath() {
-        Point2D last = mPath.getCurrentPoint();
-        mLastX = (float) last.getX();
-        mLastY = (float) last.getY();
-    }
-
-    /**
-     * Add a closed rectangle contour to the path
-     *
-     * @param left   The left side of a rectangle to add to the path
-     * @param top    The top of a rectangle to add to the path
-     * @param right  The right side of a rectangle to add to the path
-     * @param bottom The bottom of a rectangle to add to the path
-     * @param dir    The direction to wind the rectangle's contour
-     */
-    public void addRect(float left, float top, float right, float bottom,
-                        int dir) {
-        moveTo(left, top);
-
-        Direction direction = getDirection(dir);
-
-        switch (direction) {
-            case CW:
-                lineTo(right, top);
-                lineTo(right, bottom);
-                lineTo(left, bottom);
-                break;
-            case CCW:
-                lineTo(left, bottom);
-                lineTo(right, bottom);
-                lineTo(right, top);
-                break;
-        }
-
-        close();
-
-        resetLastPointFromPath();
-    }
-
-    /**
-     * Offset the path by (dx,dy), returning true on success
-     *
-     * @param dx  The amount in the X direction to offset the entire path
-     * @param dy  The amount in the Y direction to offset the entire path
-     */
-    public void offset(float dx, float dy) {
-        GeneralPath newPath = new GeneralPath();
-
-        PathIterator iterator = mPath.getPathIterator(new AffineTransform(0, 0, dx, 0, 0, dy));
-
-        newPath.append(iterator, false /*connect*/);
-        mPath = newPath;
-    }
-
-    /**
-     * Transform the points in this path by matrix, and write the answer
-     * into dst. If dst is null, then the the original path is modified.
-     *
-     * @param matrix The matrix to apply to the path
-     * @param dst    The transformed path is written here. If dst is null,
-     *               then the the original path is modified
-     */
-    public void transform(Matrix_Delegate matrix, Path_Delegate dst) {
-        if (matrix.hasPerspective()) {
-            assert false;
-            Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_AFFINE,
-                    "android.graphics.Path#transform() only " +
-                    "supports affine transformations.", null, null, null /*data*/);
-        }
-
-        GeneralPath newPath = new GeneralPath();
-
-        PathIterator iterator = mPath.getPathIterator(matrix.getAffineTransform());
-
-        newPath.append(iterator, false /*connect*/);
-
-        if (dst != null) {
-            dst.mPath = newPath;
-        } else {
-            mPath = newPath;
-        }
+        return Path.nInit(nPath);
     }
 }
diff --git a/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java b/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
deleted file mode 100644
index 0dc6f61..0000000
--- a/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.PorterDuff.Mode;
-
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
-
-import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getComposite;
-import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getPorterDuffMode;
-
-/**
- * Delegate implementing the native methods of android.graphics.PorterDuffColorFilter
- *
- * Through the layoutlib_create tool, the original native methods of PorterDuffColorFilter have
- * been replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original PorterDuffColorFilter class.
- *
- * Because this extends {@link ColorFilter_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the Shader classes will be added to the manager
- * owned by {@link ColorFilter_Delegate}.
- *
- * @see ColorFilter_Delegate
- *
- */
-public class PorterDuffColorFilter_Delegate extends ColorFilter_Delegate {
-
-    // ---- delegate data ----
-
-    private final java.awt.Color mSrcColor;
-    private final Mode mMode;
-
-
-    // ---- Public Helper methods ----
-
-    @Override
-    public boolean isSupported() {
-        return true;
-    }
-
-    @Override
-    public String getSupportMessage() {
-        return "PorterDuff Color Filter is not supported for mode: " + mMode.name() + ".";
-    }
-
-    @Override
-    public void applyFilter(Graphics2D g, int width, int height) {
-        g.setComposite(getComposite(mMode, 0xFF));
-        g.setColor(mSrcColor);
-        g.fillRect(0, 0, width, height);
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long native_CreateBlendModeFilter(int srcColor, int porterDuffMode) {
-        PorterDuffColorFilter_Delegate newDelegate =
-                new PorterDuffColorFilter_Delegate(srcColor, porterDuffMode);
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-
-    // ---- Private delegate/helper methods ----
-
-    private PorterDuffColorFilter_Delegate(int srcColor, int mode) {
-        mSrcColor = new java.awt.Color(srcColor, true /* hasAlpha */);
-        mMode = getCompatibleMode(getPorterDuffMode(mode));
-    }
-
-    // For filtering the colors, the src image should contain the "color" only for pixel values
-    // which are not transparent in the target image. But, we are using a simple rectangular image
-    // completely filled with color. Hence some Composite rules do not apply as intended. However,
-    // in such cases, they can usually be mapped to some other mode, which produces an approximately
-    // equivalent result.
-    private Mode getCompatibleMode(Mode mode) {
-        Mode m = mode;
-        // Modes that are directly supported:
-        // CLEAR, DST, SRC_IN, DST_IN, DST_OUT, SRC_ATOP, DARKEN, LIGHTEN, MULTIPLY, SCREEN,
-        // ADD, OVERLAY
-        switch (mode) {
-        // Modes that can be mapped to one of the supported modes.
-        case SRC:
-            m = Mode.SRC_IN;
-            break;
-        case SRC_OVER:
-            m = Mode.SRC_ATOP;
-            break;
-        case DST_OVER:
-            m = Mode.DST;
-            break;
-        case SRC_OUT:
-            m = Mode.CLEAR;
-            break;
-        case DST_ATOP:
-            m = Mode.DST_IN;
-            break;
-        case XOR:
-            m = Mode.DST_OUT;
-            break;
-        }
-        return m;
-    }
-}
diff --git a/bridge/src/android/graphics/RadialGradient_Delegate.java b/bridge/src/android/graphics/RadialGradient_Delegate.java
deleted file mode 100644
index 4a18219..0000000
--- a/bridge/src/android/graphics/RadialGradient_Delegate.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.Shader.TileMode;
-
-import java.awt.image.ColorModel;
-import java.awt.image.DataBufferInt;
-import java.awt.image.Raster;
-import java.awt.image.SampleModel;
-
-/**
- * Delegate implementing the native methods of android.graphics.RadialGradient
- *
- * Through the layoutlib_create tool, the original native methods of RadialGradient have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original RadialGradient class.
- *
- * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
- *
- * @see Shader_Delegate
- *
- */
-public class RadialGradient_Delegate extends Gradient_Delegate {
-
-    // ---- delegate data ----
-    private java.awt.Paint mJavaPaint;
-
-    // ---- Public Helper methods ----
-
-    @Override
-    public java.awt.Paint getJavaPaint() {
-        return mJavaPaint;
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreate(long matrix, float x, float y, float radius,
-            long[] colors, float[] positions, int tileMode, long colorSpaceHandle) {
-        RadialGradient_Delegate newDelegate = new RadialGradient_Delegate(matrix, x, y, radius,
-                colors, positions, Shader_Delegate.getTileMode(tileMode));
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    // ---- Private delegate/helper methods ----
-
-    /**
-     * Create a shader that draws a radial gradient given the center and radius.
-     *
-     * @param nativeMatrix reference to the shader's native transformation matrix
-     * @param x The x-coordinate of the center of the radius
-     * @param y The y-coordinate of the center of the radius
-     * @param radius Must be positive. The radius of the circle for this
-     *            gradient
-     * @param colors The colors to be distributed between the center and edge of
-     *            the circle
-     * @param positions May be NULL. The relative position of each corresponding
-     *            color in the colors array. If this is NULL, the the colors are
-     *            distributed evenly between the center and edge of the circle.
-     * @param tile The Shader tiling mode
-     */
-    private RadialGradient_Delegate(long nativeMatrix, float x, float y, float radius,
-            long[] colors, float[] positions, TileMode tile) {
-        super(nativeMatrix, colors, positions);
-        mJavaPaint = new RadialGradientPaint(x, y, radius, mColors, mPositions, tile);
-    }
-
-    private class RadialGradientPaint extends GradientPaint {
-
-        private final float mX;
-        private final float mY;
-        private final float mRadius;
-
-        public RadialGradientPaint(float x, float y, float radius,
-                int[] colors, float[] positions, TileMode mode) {
-            super(colors, positions, mode);
-            mX = x;
-            mY = y;
-            mRadius = radius;
-        }
-
-        @Override
-        public java.awt.PaintContext createContext(
-                java.awt.image.ColorModel     colorModel,
-                java.awt.Rectangle            deviceBounds,
-                java.awt.geom.Rectangle2D     userBounds,
-                java.awt.geom.AffineTransform xform,
-                java.awt.RenderingHints       hints) {
-            precomputeGradientColors();
-
-            java.awt.geom.AffineTransform canvasMatrix;
-            try {
-                canvasMatrix = xform.createInverse();
-            } catch (java.awt.geom.NoninvertibleTransformException e) {
-                Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_INVERSE,
-                        "Unable to inverse matrix in RadialGradient", e, null, null /*data*/);
-                canvasMatrix = new java.awt.geom.AffineTransform();
-            }
-
-            java.awt.geom.AffineTransform localMatrix = getLocalMatrix();
-            try {
-                localMatrix = localMatrix.createInverse();
-            } catch (java.awt.geom.NoninvertibleTransformException e) {
-                Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_INVERSE,
-                        "Unable to inverse matrix in RadialGradient", e, null, null /*data*/);
-                localMatrix = new java.awt.geom.AffineTransform();
-            }
-
-            return new RadialGradientPaintContext(canvasMatrix, localMatrix, colorModel);
-        }
-
-        private class RadialGradientPaintContext implements java.awt.PaintContext {
-
-            private final java.awt.geom.AffineTransform mCanvasMatrix;
-            private final java.awt.geom.AffineTransform mLocalMatrix;
-            private final java.awt.image.ColorModel mColorModel;
-
-            public RadialGradientPaintContext(
-                    java.awt.geom.AffineTransform canvasMatrix,
-                    java.awt.geom.AffineTransform localMatrix,
-                    java.awt.image.ColorModel colorModel) {
-                mCanvasMatrix = canvasMatrix;
-                mLocalMatrix = localMatrix;
-                mColorModel = colorModel.hasAlpha() ? colorModel : ColorModel.getRGBdefault();
-            }
-
-            @Override
-            public void dispose() {
-            }
-
-            @Override
-            public java.awt.image.ColorModel getColorModel() {
-                return mColorModel;
-            }
-
-            @Override
-            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
-                int[] data = new int[w*h];
-
-                // compute distance from each point to the center, and figure out the distance from
-                // it.
-                int index = 0;
-                float[] pt1 = new float[2];
-                float[] pt2 = new float[2];
-
-                for (int iy = 0 ; iy < h ; iy++) {
-                    for (int ix = 0 ; ix < w ; ix++) {
-                        // handle the canvas transform
-                        pt1[0] = x + ix;
-                        pt1[1] = y + iy;
-                        mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
-
-                        // handle the local matrix
-                        pt1[0] = pt2[0];
-                        pt1[1] = pt2[1];
-                        mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
-
-                        float _x = pt2[0] - mX;
-                        float _y = pt2[1] - mY;
-                        float distance = (float) Math.hypot(_x, _y);
-
-                        data[index++] = getGradientColor(distance / mRadius);
-                    }
-                }
-
-                DataBufferInt dataBuffer = new DataBufferInt(data, data.length);
-                SampleModel colorModel = mColorModel.createCompatibleSampleModel(w, h);
-                return Raster.createWritableRaster(colorModel, dataBuffer, null);
-            }
-
-        }
-    }
-
-}
diff --git a/bridge/src/android/graphics/Region_Delegate.java b/bridge/src/android/graphics/Region_Delegate.java
deleted file mode 100644
index 254e825..0000000
--- a/bridge/src/android/graphics/Region_Delegate.java
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.os.Parcel;
-
-import java.awt.Rectangle;
-import java.awt.Shape;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Area;
-import java.awt.geom.Rectangle2D;
-
-/**
- * Delegate implementing the native methods of android.graphics.Region
- *
- * Through the layoutlib_create tool, the original native methods of Region have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Region class.
- *
- * This also serve as a base class for all Region delegate classes.
- *
- * @see DelegateManager
- *
- */
-public class Region_Delegate {
-
-    // ---- delegate manager ----
-    protected static final DelegateManager<Region_Delegate> sManager =
-            new DelegateManager<Region_Delegate>(Region_Delegate.class);
-
-    // ---- delegate helper data ----
-
-    // ---- delegate data ----
-    private Area mArea = new Area();
-
-    // ---- Public Helper methods ----
-
-    public static Region_Delegate getDelegate(long nativeShader) {
-        return sManager.getDelegate(nativeShader);
-    }
-
-    public Area getJavaArea() {
-        return mArea;
-    }
-
-    /**
-     * Combines two {@link Shape} into another one (actually an {@link Area}), according
-     * to the given {@link Region.Op}.
-     *
-     * If the Op is not one that combines two shapes, then this return null
-     *
-     * @param shape1 the firt shape to combine which can be null if there's no original clip.
-     * @param shape2 the 2nd shape to combine
-     * @param regionOp the operande for the combine
-     * @return a new area or null.
-     */
-    public static Area combineShapes(Shape shape1, Shape shape2, int regionOp) {
-        if (regionOp == Region.Op.DIFFERENCE.nativeInt) {
-            // if shape1 is null (empty), then the result is null.
-            if (shape1 == null) {
-                return null;
-            }
-
-            // result is always a new area.
-            Area result = new Area(shape1);
-            result.subtract(shape2 instanceof Area ? (Area) shape2 : new Area(shape2));
-            return result;
-
-        } else if (regionOp == Region.Op.INTERSECT.nativeInt) {
-            // if shape1 is null, then the result is simply shape2.
-            if (shape1 == null) {
-                return new Area(shape2);
-            }
-
-            // result is always a new area.
-            Area result = new Area(shape1);
-            result.intersect(shape2 instanceof Area ? (Area) shape2 : new Area(shape2));
-            return result;
-
-        } else if (regionOp == Region.Op.UNION.nativeInt) {
-            // if shape1 is null, then the result is simply shape2.
-            if (shape1 == null) {
-                return new Area(shape2);
-            }
-
-            // result is always a new area.
-            Area result = new Area(shape1);
-            result.add(shape2 instanceof Area ? (Area) shape2 : new Area(shape2));
-            return result;
-
-        } else if (regionOp == Region.Op.XOR.nativeInt) {
-            // if shape1 is null, then the result is simply shape2
-            if (shape1 == null) {
-                return new Area(shape2);
-            }
-
-            // result is always a new area.
-            Area result = new Area(shape1);
-            result.exclusiveOr(shape2 instanceof Area ? (Area) shape2 : new Area(shape2));
-            return result;
-
-        } else if (regionOp == Region.Op.REVERSE_DIFFERENCE.nativeInt) {
-            // result is always a new area.
-            Area result = new Area(shape2);
-
-            if (shape1 != null) {
-                result.subtract(shape1 instanceof Area ? (Area) shape1 : new Area(shape1));
-            }
-
-            return result;
-        }
-
-        return null;
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static boolean isEmpty(Region thisRegion) {
-        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
-        if (regionDelegate == null) {
-            return true;
-        }
-
-        return regionDelegate.mArea.isEmpty();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean isRect(Region thisRegion) {
-        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
-        if (regionDelegate == null) {
-            return true;
-        }
-
-        return regionDelegate.mArea.isRectangular();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean isComplex(Region thisRegion) {
-        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
-        if (regionDelegate == null) {
-            return true;
-        }
-
-        return regionDelegate.mArea.isSingular() == false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean contains(Region thisRegion, int x, int y) {
-        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
-        if (regionDelegate == null) {
-            return false;
-        }
-
-        return regionDelegate.mArea.contains(x, y);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean quickContains(Region thisRegion,
-            int left, int top, int right, int bottom) {
-        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
-        if (regionDelegate == null) {
-            return false;
-        }
-
-        return regionDelegate.mArea.isRectangular() &&
-                regionDelegate.mArea.contains(left, top, right - left, bottom - top);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean quickReject(Region thisRegion,
-            int left, int top, int right, int bottom) {
-        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
-        if (regionDelegate == null) {
-            return false;
-        }
-
-        return regionDelegate.mArea.isEmpty() ||
-                regionDelegate.mArea.intersects(left, top, right - left, bottom - top) == false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean quickReject(Region thisRegion, Region rgn) {
-        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
-        if (regionDelegate == null) {
-            return false;
-        }
-
-        Region_Delegate targetRegionDelegate = sManager.getDelegate(rgn.mNativeRegion);
-        if (targetRegionDelegate == null) {
-            return false;
-        }
-
-        return regionDelegate.mArea.isEmpty() ||
-                regionDelegate.mArea.getBounds().intersects(
-                        targetRegionDelegate.mArea.getBounds()) == false;
-
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void translate(Region thisRegion, int dx, int dy, Region dst) {
-        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
-        if (regionDelegate == null) {
-            return;
-        }
-
-        Region_Delegate targetRegionDelegate = sManager.getDelegate(dst.mNativeRegion);
-        if (targetRegionDelegate == null) {
-            return;
-        }
-
-        if (regionDelegate.mArea.isEmpty()) {
-            targetRegionDelegate.mArea = new Area();
-        } else {
-            targetRegionDelegate.mArea = new Area(regionDelegate.mArea);
-            AffineTransform mtx = new AffineTransform();
-            mtx.translate(dx, dy);
-            targetRegionDelegate.mArea.transform(mtx);
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void scale(Region thisRegion, float scale, Region dst) {
-        Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
-        if (regionDelegate == null) {
-            return;
-        }
-
-        Region_Delegate targetRegionDelegate = sManager.getDelegate(dst.mNativeRegion);
-        if (targetRegionDelegate == null) {
-            return;
-        }
-
-        if (regionDelegate.mArea.isEmpty()) {
-            targetRegionDelegate.mArea = new Area();
-        } else {
-            targetRegionDelegate.mArea = new Area(regionDelegate.mArea);
-            AffineTransform mtx = new AffineTransform();
-            mtx.scale(scale, scale);
-            targetRegionDelegate.mArea.transform(mtx);
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeConstructor() {
-        Region_Delegate newDelegate = new Region_Delegate();
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeDestructor(long native_region) {
-        sManager.removeJavaReferenceFor(native_region);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeSetRegion(long native_dst, long native_src) {
-        Region_Delegate dstRegion = sManager.getDelegate(native_dst);
-        if (dstRegion == null) {
-            return;
-        }
-
-        Region_Delegate srcRegion = sManager.getDelegate(native_src);
-        if (srcRegion == null) {
-            return;
-        }
-
-        dstRegion.mArea.reset();
-        dstRegion.mArea.add(srcRegion.mArea);
-
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeSetRect(long native_dst,
-            int left, int top, int right, int bottom) {
-        Region_Delegate dstRegion = sManager.getDelegate(native_dst);
-        if (dstRegion == null) {
-            return true;
-        }
-
-        dstRegion.mArea = new Area(new Rectangle2D.Float(left, top, right - left, bottom - top));
-        return dstRegion.mArea.getBounds().isEmpty() == false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeSetPath(long native_dst, long native_path, long native_clip) {
-        Region_Delegate dstRegion = sManager.getDelegate(native_dst);
-        if (dstRegion == null) {
-            return true;
-        }
-
-        Path_Delegate path = Path_Delegate.getDelegate(native_path);
-        if (path == null) {
-            return true;
-        }
-
-        dstRegion.mArea = new Area(path.getJavaShape());
-
-        Region_Delegate clip = sManager.getDelegate(native_clip);
-        if (clip != null) {
-            dstRegion.mArea.subtract(clip.getJavaArea());
-        }
-
-        return dstRegion.mArea.getBounds().isEmpty() == false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeGetBounds(long native_region, Rect rect) {
-        Region_Delegate region = sManager.getDelegate(native_region);
-        if (region == null) {
-            return true;
-        }
-
-        Rectangle bounds = region.mArea.getBounds();
-        if (bounds.isEmpty()) {
-            rect.left = rect.top = rect.right = rect.bottom = 0;
-            return false;
-        }
-
-        rect.left = bounds.x;
-        rect.top = bounds.y;
-        rect.right = bounds.x + bounds.width;
-        rect.bottom = bounds.y + bounds.height;
-        return true;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeGetBoundaryPath(long native_region, long native_path) {
-        Region_Delegate region = sManager.getDelegate(native_region);
-        if (region == null) {
-            return false;
-        }
-
-        Path_Delegate path = Path_Delegate.getDelegate(native_path);
-        if (path == null) {
-            return false;
-        }
-
-        if (region.mArea.isEmpty()) {
-            path.reset();
-            return false;
-        }
-
-        path.setPathIterator(region.mArea.getPathIterator(new AffineTransform()));
-        return true;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeOp(long native_dst,
-            int left, int top, int right, int bottom, int op) {
-        Region_Delegate region = sManager.getDelegate(native_dst);
-        if (region == null) {
-            return false;
-        }
-
-        region.mArea = combineShapes(region.mArea,
-                new Rectangle2D.Float(left, top, right - left, bottom - top), op);
-
-        assert region.mArea != null;
-        if (region.mArea != null) {
-            region.mArea = new Area();
-        }
-
-        return region.mArea.getBounds().isEmpty() == false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeOp(long native_dst, Rect rect, long native_region, int op) {
-        Region_Delegate region = sManager.getDelegate(native_dst);
-        if (region == null) {
-            return false;
-        }
-
-        region.mArea = combineShapes(region.mArea,
-                new Rectangle2D.Float(rect.left, rect.top, rect.width(), rect.height()), op);
-
-        assert region.mArea != null;
-        if (region.mArea != null) {
-            region.mArea = new Area();
-        }
-
-        return region.mArea.getBounds().isEmpty() == false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeOp(long native_dst,
-            long native_region1, long native_region2, int op) {
-        Region_Delegate dstRegion = sManager.getDelegate(native_dst);
-        if (dstRegion == null) {
-            return true;
-        }
-
-        Region_Delegate region1 = sManager.getDelegate(native_region1);
-        if (region1 == null) {
-            return false;
-        }
-
-        Region_Delegate region2 = sManager.getDelegate(native_region2);
-        if (region2 == null) {
-            return false;
-        }
-
-        dstRegion.mArea = combineShapes(region1.mArea, region2.mArea, op);
-
-        assert dstRegion.mArea != null;
-        if (dstRegion.mArea != null) {
-            dstRegion.mArea = new Area();
-        }
-
-        return dstRegion.mArea.getBounds().isEmpty() == false;
-
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreateFromParcel(Parcel p) {
-        // This is only called by Region.CREATOR (Parcelable.Creator<Region>), which is only
-        // used during aidl call so really this should not be called.
-        Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
-                "AIDL is not suppored, and therefore Regions cannot be created from parcels.",
-                null, null /*data*/);
-        return 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeWriteToParcel(long native_region,
-                                                      Parcel p) {
-        // This is only called when sending a region through aidl, so really this should not
-        // be called.
-        Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
-                "AIDL is not suppored, and therefore Regions cannot be written to parcels.",
-                null, null /*data*/);
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nativeEquals(long native_r1, long native_r2) {
-        Region_Delegate region1 = sManager.getDelegate(native_r1);
-        if (region1 == null) {
-            return false;
-        }
-
-        Region_Delegate region2 = sManager.getDelegate(native_r2);
-        if (region2 == null) {
-            return false;
-        }
-
-        return region1.mArea.equals(region2.mArea);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static String nativeToString(long native_region) {
-        Region_Delegate region = sManager.getDelegate(native_region);
-        if (region == null) {
-            return "not found";
-        }
-
-        return region.mArea.toString();
-    }
-
-    // ---- Private delegate/helper methods ----
-
-}
diff --git a/bridge/src/android/graphics/RenderNode_Delegate.java b/bridge/src/android/graphics/RenderNode_Delegate.java
deleted file mode 100644
index ae08b00..0000000
--- a/bridge/src/android/graphics/RenderNode_Delegate.java
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate implementing the native methods of {@link RenderNode}
- * <p/>
- * Through the layoutlib_create tool, some native methods of RenderNode have been replaced by calls
- * to methods of the same name in this delegate class.
- *
- * @see DelegateManager
- */
-public class RenderNode_Delegate {
-
-
-    // ---- delegate manager ----
-    private static final DelegateManager<RenderNode_Delegate> sManager =
-            new DelegateManager<RenderNode_Delegate>(RenderNode_Delegate.class);
-    private static long sFinalizer = -1;
-
-    private float mLift;
-    private float mTranslationX;
-    private float mTranslationY;
-    private float mTranslationZ;
-    private float mRotation;
-    private float mScaleX = 1;
-    private float mScaleY = 1;
-    private float mPivotX;
-    private float mPivotY;
-    private boolean mPivotExplicitlySet;
-    private int mLeft;
-    private int mRight;
-    private int mTop;
-    private int mBottom;
-    @SuppressWarnings("UnusedDeclaration")
-    private String mName;
-
-    @LayoutlibDelegate
-    /*package*/ static long nCreate(String name) {
-        RenderNode_Delegate renderNodeDelegate = new RenderNode_Delegate();
-        renderNodeDelegate.mName = name;
-        return sManager.addNewDelegate(renderNodeDelegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nGetNativeFinalizer() {
-        synchronized (RenderNode_Delegate.class) {
-            if (sFinalizer == -1) {
-                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
-            }
-        }
-        return sFinalizer;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nSetElevation(long renderNode, float lift) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null && delegate.mLift != lift) {
-            delegate.mLift = lift;
-            return true;
-        }
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetElevation(long renderNode) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null) {
-            return delegate.mLift;
-        }
-        return 0f;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nSetTranslationX(long renderNode, float translationX) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null && delegate.mTranslationX != translationX) {
-            delegate.mTranslationX = translationX;
-            return true;
-        }
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetTranslationX(long renderNode) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null) {
-            return delegate.mTranslationX;
-        }
-        return 0f;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nSetTranslationY(long renderNode, float translationY) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null && delegate.mTranslationY != translationY) {
-            delegate.mTranslationY = translationY;
-            return true;
-        }
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetTranslationY(long renderNode) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null) {
-            return delegate.mTranslationY;
-        }
-        return 0f;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nSetTranslationZ(long renderNode, float translationZ) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null && delegate.mTranslationZ != translationZ) {
-            delegate.mTranslationZ = translationZ;
-            return true;
-        }
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetTranslationZ(long renderNode) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null) {
-            return delegate.mTranslationZ;
-        }
-        return 0f;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nSetRotation(long renderNode, float rotation) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null && delegate.mRotation != rotation) {
-            delegate.mRotation = rotation;
-            return true;
-        }
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetRotation(long renderNode) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null) {
-            return delegate.mRotation;
-        }
-        return 0f;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void getMatrix(RenderNode renderNode, Matrix outMatrix) {
-        outMatrix.reset();
-        if (renderNode != null) {
-            float rotation = renderNode.getRotationZ();
-            float translationX = renderNode.getTranslationX();
-            float translationY = renderNode.getTranslationY();
-            float pivotX = renderNode.getPivotX();
-            float pivotY = renderNode.getPivotY();
-            float scaleX = renderNode.getScaleX();
-            float scaleY = renderNode.getScaleY();
-
-            outMatrix.setTranslate(translationX, translationY);
-            outMatrix.preRotate(rotation, pivotX, pivotY);
-            outMatrix.preScale(scaleX, scaleY, pivotX, pivotY);
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nSetLeft(long renderNode, int left) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null && delegate.mLeft != left) {
-            delegate.mLeft = left;
-            return true;
-        }
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nSetTop(long renderNode, int top) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null && delegate.mTop != top) {
-            delegate.mTop = top;
-            return true;
-        }
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nSetRight(long renderNode, int right) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null && delegate.mRight != right) {
-            delegate.mRight = right;
-            return true;
-        }
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nSetBottom(long renderNode, int bottom) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null && delegate.mBottom != bottom) {
-            delegate.mBottom = bottom;
-            return true;
-        }
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nSetLeftTopRightBottom(long renderNode, int left, int top, int right,
-            int bottom) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null && (delegate.mLeft != left || delegate.mTop != top || delegate
-                .mRight != right || delegate.mBottom != bottom)) {
-            delegate.mLeft = left;
-            delegate.mTop = top;
-            delegate.mRight = right;
-            delegate.mBottom = bottom;
-            return true;
-        }
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nIsPivotExplicitlySet(long renderNode) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        return delegate != null && delegate.mPivotExplicitlySet;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nSetPivotX(long renderNode, float pivotX) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null) {
-            delegate.mPivotX = pivotX;
-            delegate.mPivotExplicitlySet = true;
-            return true;
-        }
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetPivotX(long renderNode) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null) {
-            if (delegate.mPivotExplicitlySet) {
-                return delegate.mPivotX;
-            } else {
-                return (delegate.mRight - delegate.mLeft) / 2.0f;
-            }
-        }
-        return 0f;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nSetPivotY(long renderNode, float pivotY) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null) {
-            delegate.mPivotY = pivotY;
-            delegate.mPivotExplicitlySet = true;
-            return true;
-        }
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetPivotY(long renderNode) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null) {
-            if (delegate.mPivotExplicitlySet) {
-                return delegate.mPivotY;
-            } else {
-                return (delegate.mBottom - delegate.mTop) / 2.0f;
-            }
-        }
-        return 0f;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nSetScaleX(long renderNode, float scaleX) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null && delegate.mScaleX != scaleX) {
-            delegate.mScaleX = scaleX;
-            return true;
-        }
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetScaleX(long renderNode) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null) {
-            return delegate.mScaleX;
-        }
-        return 0f;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nSetScaleY(long renderNode, float scaleY) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null && delegate.mScaleY != scaleY) {
-            delegate.mScaleY = scaleY;
-            return true;
-        }
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetScaleY(long renderNode) {
-        RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
-        if (delegate != null) {
-            return delegate.mScaleY;
-        }
-        return 0f;
-    }
-}
diff --git a/bridge/src/android/graphics/RoundRectangle.java b/bridge/src/android/graphics/RoundRectangle.java
deleted file mode 100644
index 736f03e..0000000
--- a/bridge/src/android/graphics/RoundRectangle.java
+++ /dev/null
@@ -1,368 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import java.awt.geom.AffineTransform;
-import java.awt.geom.PathIterator;
-import java.awt.geom.Rectangle2D;
-import java.awt.geom.RectangularShape;
-import java.awt.geom.RoundRectangle2D;
-import java.util.EnumSet;
-import java.util.NoSuchElementException;
-
-/**
- * Defines a rectangle with rounded corners, where the sizes of the corners
- * are potentially different.
- */
-public class RoundRectangle extends RectangularShape {
-    public double x;
-    public double y;
-    public double width;
-    public double height;
-    public double ulWidth;
-    public double ulHeight;
-    public double urWidth;
-    public double urHeight;
-    public double lrWidth;
-    public double lrHeight;
-    public double llWidth;
-    public double llHeight;
-
-    private enum Zone {
-        CLOSE_OUTSIDE,
-        CLOSE_INSIDE,
-        MIDDLE,
-        FAR_INSIDE,
-        FAR_OUTSIDE
-    }
-
-    private final EnumSet<Zone> close = EnumSet.of(Zone.CLOSE_OUTSIDE, Zone.CLOSE_INSIDE);
-    private final EnumSet<Zone> far = EnumSet.of(Zone.FAR_OUTSIDE, Zone.FAR_INSIDE);
-
-    /**
-     * @param cornerDimensions array of 8 floating-point number corresponding to the width and
-     * the height of each corner in the following order: upper-left, upper-right, lower-right,
-     * lower-left. It assumes for the size the same convention as {@link RoundRectangle2D}, that
-     * is that the width and height of a corner correspond to the total width and height of the
-     * ellipse that corner is a quarter of.
-     */
-    public RoundRectangle(float x, float y, float width, float height, float[] cornerDimensions) {
-        assert cornerDimensions.length == 8 : "The array of corner dimensions must have eight " +
-                    "elements";
-
-        this.x = x;
-        this.y = y;
-        this.width = width;
-        this.height = height;
-
-        float[] dimensions = cornerDimensions.clone();
-        // If a value is negative, the corresponding corner is squared
-        for (int i = 0; i < dimensions.length; i += 2) {
-            if (dimensions[i] < 0 || dimensions[i + 1] < 0) {
-                dimensions[i] = 0;
-                dimensions[i + 1] = 0;
-            }
-        }
-
-        double topCornerWidth = (dimensions[0] + dimensions[2]) / 2d;
-        double bottomCornerWidth = (dimensions[4] + dimensions[6]) / 2d;
-        double leftCornerHeight = (dimensions[1] + dimensions[7]) / 2d;
-        double rightCornerHeight = (dimensions[3] + dimensions[5]) / 2d;
-
-        // Rescale the corner dimensions if they are bigger than the rectangle
-        double scale = Math.min(1.0, width / topCornerWidth);
-        scale = Math.min(scale, width / bottomCornerWidth);
-        scale = Math.min(scale, height / leftCornerHeight);
-        scale = Math.min(scale, height / rightCornerHeight);
-
-        this.ulWidth = dimensions[0] * scale;
-        this.ulHeight = dimensions[1] * scale;
-        this.urWidth = dimensions[2] * scale;
-        this.urHeight = dimensions[3] * scale;
-        this.lrWidth = dimensions[4] * scale;
-        this.lrHeight = dimensions[5] * scale;
-        this.llWidth = dimensions[6] * scale;
-        this.llHeight = dimensions[7] * scale;
-    }
-
-    @Override
-    public double getX() {
-        return x;
-    }
-
-    @Override
-    public double getY() {
-        return y;
-    }
-
-    @Override
-    public double getWidth() {
-        return width;
-    }
-
-    @Override
-    public double getHeight() {
-        return height;
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return (width <= 0d) || (height <= 0d);
-    }
-
-    @Override
-    public void setFrame(double x, double y, double w, double h) {
-        this.x = x;
-        this.y = y;
-        this.width = w;
-        this.height = h;
-    }
-
-    @Override
-    public Rectangle2D getBounds2D() {
-        return new Rectangle2D.Double(x, y, width, height);
-    }
-
-    @Override
-    public boolean contains(double x, double y) {
-        if (isEmpty()) {
-            return false;
-        }
-
-        double x0 = getX();
-        double y0 = getY();
-        double x1 = x0 + getWidth();
-        double y1 = y0 + getHeight();
-        // Check for trivial rejection - point is outside bounding rectangle
-        if (x < x0 || y < y0 || x >= x1 || y >= y1) {
-            return false;
-        }
-
-        double insideTopX0 = x0 + ulWidth / 2d;
-        double insideLeftY0 = y0 + ulHeight / 2d;
-        if (x < insideTopX0 && y < insideLeftY0) {
-            // In the upper-left corner
-            return isInsideCorner(x - insideTopX0, y - insideLeftY0, ulWidth / 2d, ulHeight / 2d);
-        }
-
-        double insideTopX1 = x1 - urWidth / 2d;
-        double insideRightY0 = y0 + urHeight / 2d;
-        if (x > insideTopX1 && y < insideRightY0) {
-            // In the upper-right corner
-            return isInsideCorner(x - insideTopX1, y - insideRightY0, urWidth / 2d, urHeight / 2d);
-        }
-
-        double insideBottomX1 = x1 - lrWidth / 2d;
-        double insideRightY1 = y1 - lrHeight / 2d;
-        if (x > insideBottomX1 && y > insideRightY1) {
-            // In the lower-right corner
-            return isInsideCorner(x - insideBottomX1, y - insideRightY1, lrWidth / 2d,
-                    lrHeight / 2d);
-        }
-
-        double insideBottomX0 = x0 + llWidth / 2d;
-        double insideLeftY1 = y1 - llHeight / 2d;
-        if (x < insideBottomX0 && y > insideLeftY1) {
-            // In the lower-left corner
-            return isInsideCorner(x - insideBottomX0, y - insideLeftY1, llWidth / 2d,
-                    llHeight / 2d);
-        }
-
-        // In the central part of the rectangle
-        return true;
-    }
-
-    private boolean isInsideCorner(double x, double y, double width, double height) {
-        double squareDist = height * height * x * x + width * width * y * y;
-        return squareDist <= width * width * height * height;
-    }
-
-    private Zone classify(double coord, double side1, double arcSize1, double side2,
-            double arcSize2) {
-        if (coord < side1) {
-            return Zone.CLOSE_OUTSIDE;
-        } else if (coord < side1 + arcSize1) {
-            return Zone.CLOSE_INSIDE;
-        } else if (coord < side2 - arcSize2) {
-            return Zone.MIDDLE;
-        } else if (coord < side2) {
-            return Zone.FAR_INSIDE;
-        } else {
-            return Zone.FAR_OUTSIDE;
-        }
-    }
-
-    public boolean intersects(double x, double y, double w, double h) {
-        if (isEmpty() || w <= 0 || h <= 0) {
-            return false;
-        }
-        double x0 = getX();
-        double y0 = getY();
-        double x1 = x0 + getWidth();
-        double y1 = y0 + getHeight();
-        // Check for trivial rejection - bounding rectangles do not intersect
-        if (x + w <= x0 || x >= x1 || y + h <= y0 || y >= y1) {
-            return false;
-        }
-
-        double maxLeftCornerWidth = Math.max(ulWidth, llWidth) / 2d;
-        double maxRightCornerWidth = Math.max(urWidth, lrWidth) / 2d;
-        double maxUpperCornerHeight = Math.max(ulHeight, urHeight) / 2d;
-        double maxLowerCornerHeight = Math.max(llHeight, lrHeight) / 2d;
-        Zone x0class = classify(x, x0, maxLeftCornerWidth, x1, maxRightCornerWidth);
-        Zone x1class = classify(x + w, x0, maxLeftCornerWidth, x1, maxRightCornerWidth);
-        Zone y0class = classify(y, y0, maxUpperCornerHeight, y1, maxLowerCornerHeight);
-        Zone y1class = classify(y + h, y0, maxUpperCornerHeight, y1, maxLowerCornerHeight);
-
-        // Trivially accept if any point is inside inner rectangle
-        if (x0class == Zone.MIDDLE || x1class == Zone.MIDDLE || y0class == Zone.MIDDLE || y1class == Zone.MIDDLE) {
-            return true;
-        }
-        // Trivially accept if either edge spans inner rectangle
-        if ((close.contains(x0class) && far.contains(x1class)) || (close.contains(y0class) &&
-                far.contains(y1class))) {
-            return true;
-        }
-
-        // Since neither edge spans the center, then one of the corners
-        // must be in one of the rounded edges.  We detect this case if
-        // a [xy]0class is 3 or a [xy]1class is 1.  One of those two cases
-        // must be true for each direction.
-        // We now find a "nearest point" to test for being inside a rounded
-        // corner.
-        if (x1class == Zone.CLOSE_INSIDE && y1class == Zone.CLOSE_INSIDE) {
-            // Potentially in upper-left corner
-            x = x + w - x0 - ulWidth / 2d;
-            y = y + h - y0 - ulHeight / 2d;
-            return x > 0 || y > 0 || isInsideCorner(x, y, ulWidth / 2d, ulHeight / 2d);
-        }
-        if (x1class == Zone.CLOSE_INSIDE) {
-            // Potentially in lower-left corner
-            x = x + w - x0 - llWidth / 2d;
-            y = y - y1 + llHeight / 2d;
-            return x > 0 || y < 0 || isInsideCorner(x, y, llWidth / 2d, llHeight / 2d);
-        }
-        if (y1class == Zone.CLOSE_INSIDE) {
-            //Potentially in the upper-right corner
-            x = x - x1 + urWidth / 2d;
-            y = y + h - y0 - urHeight / 2d;
-            return x < 0 || y > 0 || isInsideCorner(x, y, urWidth / 2d, urHeight / 2d);
-        }
-        // Potentially in the lower-right corner
-        x = x - x1 + lrWidth / 2d;
-        y = y - y1 + lrHeight / 2d;
-        return x < 0 || y < 0 || isInsideCorner(x, y, lrWidth / 2d, lrHeight / 2d);
-    }
-
-    @Override
-    public boolean contains(double x, double y, double w, double h) {
-        if (isEmpty() || w <= 0 || h <= 0) {
-            return false;
-        }
-        return (contains(x, y) &&
-                contains(x + w, y) &&
-                contains(x, y + h) &&
-                contains(x + w, y + h));
-    }
-
-    @Override
-    public PathIterator getPathIterator(final AffineTransform at) {
-        return new PathIterator() {
-            int index;
-
-            // ArcIterator.btan(Math.PI/2)
-            public static final double CtrlVal = 0.5522847498307933;
-            private final double ncv = 1.0 - CtrlVal;
-
-            // Coordinates of control points for Bezier curves approximating the straight lines
-            // and corners of the rounded rectangle.
-            private final double[][] ctrlpts = {
-                    {0.0, 0.0, 0.0, ulHeight},
-                    {0.0, 0.0, 1.0, -llHeight},
-                    {0.0, 0.0, 1.0, -llHeight * ncv, 0.0, ncv * llWidth, 1.0, 0.0, 0.0, llWidth,
-                            1.0, 0.0},
-                    {1.0, -lrWidth, 1.0, 0.0},
-                    {1.0, -lrWidth * ncv, 1.0, 0.0, 1.0, 0.0, 1.0, -lrHeight * ncv, 1.0, 0.0, 1.0,
-                            -lrHeight},
-                    {1.0, 0.0, 0.0, urHeight},
-                    {1.0, 0.0, 0.0, ncv * urHeight, 1.0, -urWidth * ncv, 0.0, 0.0, 1.0, -urWidth,
-                            0.0, 0.0},
-                    {0.0, ulWidth, 0.0, 0.0},
-                    {0.0, ncv * ulWidth, 0.0, 0.0, 0.0, 0.0, 0.0, ncv * ulHeight, 0.0, 0.0, 0.0,
-                            ulHeight},
-                    {}
-            };
-            private final int[] types = {
-                    SEG_MOVETO,
-                    SEG_LINETO, SEG_CUBICTO,
-                    SEG_LINETO, SEG_CUBICTO,
-                    SEG_LINETO, SEG_CUBICTO,
-                    SEG_LINETO, SEG_CUBICTO,
-                    SEG_CLOSE,
-            };
-
-            @Override
-            public int getWindingRule() {
-                return WIND_NON_ZERO;
-            }
-
-            @Override
-            public boolean isDone() {
-                return index >= ctrlpts.length;
-            }
-
-            @Override
-            public void next() {
-                index++;
-            }
-
-            @Override
-            public int currentSegment(float[] coords) {
-                if (isDone()) {
-                    throw new NoSuchElementException("roundrect iterator out of bounds");
-                }
-                int nc = 0;
-                double ctrls[] = ctrlpts[index];
-                for (int i = 0; i < ctrls.length; i += 4) {
-                    coords[nc++] = (float) (x + ctrls[i] * width + ctrls[i + 1] / 2d);
-                    coords[nc++] = (float) (y + ctrls[i + 2] * height + ctrls[i + 3] / 2d);
-                }
-                if (at != null) {
-                    at.transform(coords, 0, coords, 0, nc / 2);
-                }
-                return types[index];
-            }
-
-            @Override
-            public int currentSegment(double[] coords) {
-                if (isDone()) {
-                    throw new NoSuchElementException("roundrect iterator out of bounds");
-                }
-                int nc = 0;
-                double ctrls[] = ctrlpts[index];
-                for (int i = 0; i < ctrls.length; i += 4) {
-                    coords[nc++] = x + ctrls[i] * width + ctrls[i + 1] / 2d;
-                    coords[nc++] = y + ctrls[i + 2] * height + ctrls[i + 3] / 2d;
-                }
-                if (at != null) {
-                    at.transform(coords, 0, coords, 0, nc / 2);
-                }
-                return types[index];
-            }
-        };
-    }
-}
diff --git a/bridge/src/android/graphics/Shader_Delegate.java b/bridge/src/android/graphics/Shader_Delegate.java
deleted file mode 100644
index d606e2d..0000000
--- a/bridge/src/android/graphics/Shader_Delegate.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.Shader.TileMode;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.Shader
- *
- * Through the layoutlib_create tool, the original native methods of Shader have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Shader class.
- *
- * This also serve as a base class for all Shader delegate classes.
- *
- * @see DelegateManager
- *
- */
-public abstract class Shader_Delegate {
-
-    // ---- delegate manager ----
-    protected static final DelegateManager<Shader_Delegate> sManager =
-            new DelegateManager<Shader_Delegate>(Shader_Delegate.class);
-    private static long sFinalizer = -1;
-
-    // ---- delegate helper data ----
-
-    // ---- delegate data ----
-    private Matrix_Delegate mLocalMatrix = null;
-    private float mAlpha = 1.0f;
-
-    // ---- Public Helper methods ----
-
-    public static Shader_Delegate getDelegate(long nativeShader) {
-        return sManager.getDelegate(nativeShader);
-    }
-
-    /**
-     * Returns the {@link TileMode} matching the given int.
-     * @param tileMode the tile mode int value
-     * @return the TileMode enum.
-     */
-    public static TileMode getTileMode(int tileMode) {
-        for (TileMode tm : TileMode.values()) {
-            if (tm.nativeInt == tileMode) {
-                return tm;
-            }
-        }
-
-        assert false;
-        return TileMode.CLAMP;
-    }
-
-    public abstract java.awt.Paint getJavaPaint();
-    public abstract boolean isSupported();
-    public abstract String getSupportMessage();
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeGetFinalizer() {
-        synchronized (Shader_Delegate.class) {
-            if (sFinalizer == -1) {
-                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
-                        sManager::removeJavaReferenceFor);
-            }
-        }
-        return sFinalizer;
-    }
-
-    // ---- Private delegate/helper methods ----
-
-    protected Shader_Delegate(long nativeMatrix) {
-        setLocalMatrix(nativeMatrix);
-    }
-
-    public void setLocalMatrix(long nativeMatrix) {
-        mLocalMatrix = Matrix_Delegate.getDelegate(nativeMatrix);
-    }
-
-    protected java.awt.geom.AffineTransform getLocalMatrix() {
-        if (mLocalMatrix != null) {
-            return mLocalMatrix.getAffineTransform();
-        }
-
-        return new java.awt.geom.AffineTransform();
-    }
-
-    public void setAlpha(float alpha) {
-        mAlpha = alpha;
-    }
-
-    public float getAlpha() {
-        return mAlpha;
-    }
-}
diff --git a/bridge/src/android/graphics/SumPathEffect_Delegate.java b/bridge/src/android/graphics/SumPathEffect_Delegate.java
deleted file mode 100644
index 6d2e9b4..0000000
--- a/bridge/src/android/graphics/SumPathEffect_Delegate.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Stroke;
-
-/**
- * Delegate implementing the native methods of android.graphics.SumPathEffect
- *
- * Through the layoutlib_create tool, the original native methods of SumPathEffect have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original SumPathEffect class.
- *
- * Because this extends {@link PathEffect_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link PathEffect_Delegate}.
- *
- * @see PathEffect_Delegate
- *
- */
-public class SumPathEffect_Delegate extends PathEffect_Delegate {
-
-    // ---- delegate data ----
-
-    // ---- Public Helper methods ----
-
-    @Override
-    public Stroke getStroke(Paint_Delegate paint) {
-        // FIXME
-        return null;
-    }
-
-    @Override
-    public boolean isSupported() {
-        return false;
-    }
-
-    @Override
-    public String getSupportMessage() {
-        return "Sum Path Effects are not supported in Layout Preview mode.";
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreate(long first, long second) {
-        SumPathEffect_Delegate newDelegate = new SumPathEffect_Delegate();
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/SweepGradient_Delegate.java b/bridge/src/android/graphics/SweepGradient_Delegate.java
deleted file mode 100644
index 01ef140..0000000
--- a/bridge/src/android/graphics/SweepGradient_Delegate.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.image.DataBufferInt;
-import java.awt.image.Raster;
-import java.awt.image.SampleModel;
-
-/**
- * Delegate implementing the native methods of android.graphics.SweepGradient
- *
- * Through the layoutlib_create tool, the original native methods of SweepGradient have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original SweepGradient class.
- *
- * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
- *
- * @see Shader_Delegate
- *
- */
-public class SweepGradient_Delegate extends Gradient_Delegate {
-
-    // ---- delegate data ----
-    private java.awt.Paint mJavaPaint;
-
-    // ---- Public Helper methods ----
-
-    @Override
-    public java.awt.Paint getJavaPaint() {
-        return mJavaPaint;
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreate(long matrix, float x, float y, long[] colors,
-            float[] positions, long colorSpaceHandle) {
-        SweepGradient_Delegate newDelegate = new SweepGradient_Delegate(matrix, x, y, colors,
-                positions);
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    // ---- Private delegate/helper methods ----
-
-    /**
-     * A subclass of Shader that draws a sweep gradient around a center point.
-     *
-     * @param nativeMatrix reference to the shader's native transformation matrix
-     * @param cx       The x-coordinate of the center
-     * @param cy       The y-coordinate of the center
-     * @param colors   The colors to be distributed between around the center.
-     *                 There must be at least 2 colors in the array.
-     * @param positions May be NULL. The relative position of
-     *                 each corresponding color in the colors array, beginning
-     *                 with 0 and ending with 1.0. If the values are not
-     *                 monotonic, the drawing may produce unexpected results.
-     *                 If positions is NULL, then the colors are automatically
-     *                 spaced evenly.
-     */
-    private SweepGradient_Delegate(long nativeMatrix, float cx, float cy,
-            long[] colors, float[] positions) {
-        super(nativeMatrix, colors, positions);
-        mJavaPaint = new SweepGradientPaint(cx, cy, mColors, mPositions);
-    }
-
-    private class SweepGradientPaint extends GradientPaint {
-
-        private final float mCx;
-        private final float mCy;
-
-        public SweepGradientPaint(float cx, float cy, int[] colors,
-                float[] positions) {
-            super(colors, positions, null /*tileMode*/);
-            mCx = cx;
-            mCy = cy;
-        }
-
-        @Override
-        public java.awt.PaintContext createContext(
-                java.awt.image.ColorModel     colorModel,
-                java.awt.Rectangle            deviceBounds,
-                java.awt.geom.Rectangle2D     userBounds,
-                java.awt.geom.AffineTransform xform,
-                java.awt.RenderingHints       hints) {
-            precomputeGradientColors();
-
-            java.awt.geom.AffineTransform canvasMatrix;
-            try {
-                canvasMatrix = xform.createInverse();
-            } catch (java.awt.geom.NoninvertibleTransformException e) {
-                Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_INVERSE,
-                        "Unable to inverse matrix in SweepGradient", e, null, null /*data*/);
-                canvasMatrix = new java.awt.geom.AffineTransform();
-            }
-
-            java.awt.geom.AffineTransform localMatrix = getLocalMatrix();
-            try {
-                localMatrix = localMatrix.createInverse();
-            } catch (java.awt.geom.NoninvertibleTransformException e) {
-                Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_INVERSE,
-                        "Unable to inverse matrix in SweepGradient", e, null, null /*data*/);
-                localMatrix = new java.awt.geom.AffineTransform();
-            }
-
-            return new SweepGradientPaintContext(canvasMatrix, localMatrix, colorModel);
-        }
-
-        private class SweepGradientPaintContext implements java.awt.PaintContext {
-
-            private final java.awt.geom.AffineTransform mCanvasMatrix;
-            private final java.awt.geom.AffineTransform mLocalMatrix;
-            private final java.awt.image.ColorModel mColorModel;
-
-            public SweepGradientPaintContext(
-                    java.awt.geom.AffineTransform canvasMatrix,
-                    java.awt.geom.AffineTransform localMatrix,
-                    java.awt.image.ColorModel colorModel) {
-                mCanvasMatrix = canvasMatrix;
-                mLocalMatrix = localMatrix;
-                mColorModel = colorModel;
-            }
-
-            @Override
-            public void dispose() {
-            }
-
-            @Override
-            public java.awt.image.ColorModel getColorModel() {
-                return mColorModel;
-            }
-
-            @Override
-            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
-
-                int[] data = new int[w*h];
-
-                // compute angle from each point to the center, and figure out the distance from
-                // it.
-                int index = 0;
-                float[] pt1 = new float[2];
-                float[] pt2 = new float[2];
-                for (int iy = 0 ; iy < h ; iy++) {
-                    for (int ix = 0 ; ix < w ; ix++) {
-                        // handle the canvas transform
-                        pt1[0] = x + ix;
-                        pt1[1] = y + iy;
-                        mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
-
-                        // handle the local matrix
-                        pt1[0] = pt2[0] - mCx;
-                        pt1[1] = pt2[1] - mCy;
-                        mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
-
-                        float dx = pt2[0];
-                        float dy = pt2[1];
-
-                        float angle;
-                        if (dx == 0) {
-                            angle = (float) (dy < 0 ? 3 * Math.PI / 2 : Math.PI / 2);
-                        } else if (dy == 0) {
-                            angle = (float) (dx < 0 ? Math.PI : 0);
-                        } else {
-                            angle = (float) Math.atan(dy / dx);
-                            if (dx > 0) {
-                                if (dy < 0) {
-                                    angle += Math.PI * 2;
-                                }
-                            } else {
-                                angle += Math.PI;
-                            }
-                        }
-
-                        // convert to 0-1. value and get color
-                        data[index++] = getGradientColor((float) (angle / (2 * Math.PI)));
-                    }
-                }
-
-                DataBufferInt dataBuffer = new DataBufferInt(data, data.length);
-                SampleModel colorModel = mColorModel.createCompatibleSampleModel(w, h);
-                return Raster.createWritableRaster(colorModel, dataBuffer, null);
-            }
-
-        }
-    }
-}
diff --git a/bridge/src/android/graphics/Typeface_Delegate.java b/bridge/src/android/graphics/Typeface_Delegate.java
index 6c0ab20..d9d38ad 100644
--- a/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/bridge/src/android/graphics/Typeface_Delegate.java
@@ -16,13 +16,12 @@
 
 package android.graphics;
 
-import com.android.SdkConstants;
+import com.android.ide.common.rendering.api.AndroidConstants;
 import com.android.ide.common.rendering.api.ILayoutLog;
 import com.android.ide.common.rendering.api.ResourceNamespace;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
-import com.android.layoutlib.bridge.android.RenderParamsFlags;
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.layoutlib.bridge.impl.RenderAction;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
@@ -33,23 +32,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.FontResourcesParser;
-import android.graphics.FontFamily_Delegate.FontVariant;
-import android.graphics.fonts.FontFamily_Builder_Delegate;
-import android.graphics.fonts.FontVariationAxis;
 
-import java.awt.Font;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Spliterator;
-import java.util.Spliterators;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
 
 /**
  * Delegate implementing the native methods of android.graphics.Typeface
@@ -64,174 +50,6 @@
  * @see DelegateManager
  */
 public final class Typeface_Delegate {
-
-    public static final String SYSTEM_FONTS = "/system/fonts/";
-
-    public static final Map<String, FontFamily_Delegate[]> sGenericNativeFamilies = new HashMap<>();
-
-    // ---- delegate manager ----
-    private static final DelegateManager<Typeface_Delegate> sManager =
-            new DelegateManager<>(Typeface_Delegate.class);
-    private static long sFinalizer = -1;
-
-    // ---- delegate data ----
-    private static long sDefaultTypeface;
-    @NonNull
-    private final FontFamily_Delegate[] mFontFamilies;  // the reference to FontFamily_Delegate.
-    @NonNull
-    private final FontFamily_Builder_Delegate[] mFontFamilyBuilders;  // the reference to
-    // FontFamily_Builder_Delegate.
-    /** @see Font#getStyle() */
-    private final int mStyle;
-    private final int mWeight;
-
-
-    // ---- Public Helper methods ----
-
-    private Typeface_Delegate(@NonNull FontFamily_Delegate[] fontFamilies,
-            @NonNull FontFamily_Builder_Delegate[] fontFamilyBuilders, int style,
-            int weight) {
-        mFontFamilies = fontFamilies;
-        mFontFamilyBuilders = fontFamilyBuilders;
-        mStyle = style;
-        mWeight = weight;
-    }
-
-    public static Typeface_Delegate getDelegate(long nativeTypeface) {
-        return sManager.getDelegate(nativeTypeface);
-    }
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static synchronized long nativeCreateFromTypeface(long native_instance, int style) {
-        Typeface_Delegate delegate = sManager.getDelegate(native_instance);
-        if (delegate == null) {
-            delegate = sManager.getDelegate(sDefaultTypeface);
-        }
-        if (delegate == null) {
-            return 0;
-        }
-
-        return sManager.addNewDelegate(
-                new Typeface_Delegate(delegate.mFontFamilies, delegate.mFontFamilyBuilders, style,
-                        delegate.mWeight));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreateFromTypefaceWithExactStyle(long native_instance, int weight,
-            boolean italic) {
-        Typeface_Delegate delegate = sManager.getDelegate(native_instance);
-        if (delegate == null) {
-            delegate = sManager.getDelegate(sDefaultTypeface);
-        }
-        if (delegate == null) {
-            return 0;
-        }
-
-        int style = weight >= 600 ? (italic ? Typeface.BOLD_ITALIC : Typeface.BOLD) :
-                (italic ? Typeface.ITALIC : Typeface.NORMAL);
-        return sManager.addNewDelegate(
-                new Typeface_Delegate(delegate.mFontFamilies, delegate.mFontFamilyBuilders, style,
-                        weight));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static synchronized long nativeCreateFromTypefaceWithVariation(long native_instance,
-            List<FontVariationAxis> axes) {
-        long newInstance = nativeCreateFromTypeface(native_instance, 0);
-
-        if (newInstance != 0) {
-            Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                    "nativeCreateFromTypefaceWithVariation is not supported", null, null, null);
-        }
-        return newInstance;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static synchronized int[] nativeGetSupportedAxes(long native_instance) {
-        // nativeCreateFromTypefaceWithVariation is not supported so we do not keep the axes
-        return null;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreateWeightAlias(long native_instance, int weight) {
-        Typeface_Delegate delegate = sManager.getDelegate(native_instance);
-        if (delegate == null) {
-            delegate = sManager.getDelegate(sDefaultTypeface);
-        }
-        if (delegate == null) {
-            return 0;
-        }
-        Typeface_Delegate weightAlias =
-                new Typeface_Delegate(delegate.mFontFamilies, delegate.mFontFamilyBuilders,
-                        delegate.mStyle,
-                        weight);
-        return sManager.addNewDelegate(weightAlias);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static synchronized long nativeCreateFromArray(long[] familyArray, int weight,
-            int italic) {
-        List<FontFamily_Delegate> fontFamilies = new ArrayList<>();
-        List<FontFamily_Builder_Delegate> fontFamilyBuilders = new ArrayList<>();
-        for (long aFamilyArray : familyArray) {
-            try {
-                fontFamilies.add(FontFamily_Delegate.getDelegate(aFamilyArray));
-            } catch (ClassCastException e) {
-                fontFamilyBuilders.add(FontFamily_Builder_Delegate.getDelegate(aFamilyArray));
-            }
-        }
-        if (weight == Typeface.RESOLVE_BY_FONT_TABLE) {
-            weight = 400;
-        }
-        if (italic == Typeface.RESOLVE_BY_FONT_TABLE) {
-            italic = 0;
-        }
-        int style = weight >= 600 ? (italic == 1 ? Typeface.BOLD_ITALIC : Typeface.BOLD) :
-                (italic == 1 ? Typeface.ITALIC : Typeface.NORMAL);
-        Typeface_Delegate delegate =
-                new Typeface_Delegate(fontFamilies.toArray(new FontFamily_Delegate[0]),
-                fontFamilyBuilders.toArray(new FontFamily_Builder_Delegate[0]),
-                style, weight);
-        return sManager.addNewDelegate(delegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeGetReleaseFunc() {
-        synchronized (Typeface_Delegate.class) {
-            if (sFinalizer == -1) {
-                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
-                        sManager::removeJavaReferenceFor);
-            }
-        }
-        return sFinalizer;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nativeGetStyle(long native_instance) {
-        Typeface_Delegate delegate = sManager.getDelegate(native_instance);
-        if (delegate == null) {
-            return 0;
-        }
-
-        return delegate.mStyle;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeSetDefault(long native_instance) {
-        sDefaultTypeface = native_instance;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nativeGetWeight(long native_instance) {
-        Typeface_Delegate delegate = sManager.getDelegate(native_instance);
-        if (delegate == null) {
-            return 0;
-        }
-        return delegate.mWeight;
-    }
-
     /**
      * Loads a single font or font family from disk
      */
@@ -245,16 +63,9 @@
         }
 
         String lowerCaseValue = path.toLowerCase();
-        if (lowerCaseValue.endsWith(SdkConstants.DOT_XML)) {
+        if (lowerCaseValue.endsWith(AndroidConstants.DOT_XML)) {
             // create a block parser for the file
-            Boolean psiParserSupport = context.getLayoutlibCallback().getFlag(
-                    RenderParamsFlags.FLAG_KEY_XML_FILE_PARSER_SUPPORT);
-            XmlPullParser parser;
-            if (psiParserSupport != null && psiParserSupport) {
-                parser = context.getLayoutlibCallback().createXmlParserForPsiFile(path);
-            } else {
-                parser = context.getLayoutlibCallback().createXmlParserForFile(path);
-            }
+            XmlPullParser parser = context.getLayoutlibCallback().createXmlParserForPsiFile(path);
 
             if (parser != null) {
                 // TODO(b/156609434): The aapt namespace should not matter for parsing font files?
@@ -266,8 +77,8 @@
                             FontResourcesParser.parse(blockParser, context.getResources());
                     typeface = Typeface.createFromResources(entry, context.getAssets(), path);
                 } catch (XmlPullParserException | IOException e) {
-                    Bridge.getLog().error(null, "Failed to parse file " + path, e, null, null /*data
-                    */);
+                    Bridge.getLog().error(null, "Failed to parse file " + path, e, null,
+                            null /*data*/);
                 } finally {
                     blockParser.ensurePopped();
                 }
@@ -303,125 +114,4 @@
     /*package*/ static Typeface create(Typeface family, int style, boolean isItalic) {
         return Typeface.create_Original(family, style, isItalic);
     }
-
-    @LayoutlibDelegate
-    /*package*/ static void nativeRegisterGenericFamily(String str, long nativePtr) {
-        Typeface_Delegate delegate = sManager.getDelegate(nativePtr);
-        if (delegate == null) {
-            return;
-        }
-        sGenericNativeFamilies.put(str, delegate.mFontFamilies);
-    }
-
-    // ---- Private delegate/helper methods ----
-
-    /**
-     * Return an Iterable of fonts that match the style and variant. The list is ordered
-     * according to preference of fonts.
-     * <p>
-     * The Iterator may contain null when the font failed to load. If null is reached when trying to
-     * render with this list of fonts, then a warning should be logged letting the user know that
-     * some font failed to load.
-     *
-     * @param variant The variant preferred. Can only be {@link FontVariant#COMPACT} or {@link
-     * FontVariant#ELEGANT}
-     */
-    @NonNull
-    public Iterable<Font> getFonts(final FontVariant variant) {
-        assert variant != FontVariant.NONE;
-
-        return new FontsIterator(mFontFamilies, mFontFamilyBuilders, variant, mWeight, mStyle);
-    }
-
-    private static class FontsIterator implements Iterator<Font>, Iterable<Font> {
-        private final FontFamily_Delegate[] fontFamilies;
-        private final FontFamily_Builder_Delegate[] fontFamilyBuilders;
-        private final int weight;
-        private final boolean isItalic;
-        private final FontVariant variant;
-
-        private int index = 0;
-
-        private FontsIterator(@NonNull FontFamily_Delegate[] fontFamilies,
-                @NonNull FontFamily_Builder_Delegate[] fontFamilyBuilders,
-                @NonNull FontVariant variant, int weight, int style) {
-            // Calculate the required weight based on style and weight of this typeface.
-            int boldExtraWeight =
-                    ((style & Font.BOLD) == 0 ? 0 : FontFamily_Delegate.BOLD_FONT_WEIGHT_DELTA);
-            this.weight = Math.min(Math.max(100, weight + 50 + boldExtraWeight), 1000);
-            this.isItalic = (style & Font.ITALIC) != 0;
-            this.fontFamilies = fontFamilies;
-            this.fontFamilyBuilders = fontFamilyBuilders;
-            this.variant = variant;
-        }
-
-        @Override
-        public boolean hasNext() {
-            return index < (fontFamilies.length + fontFamilyBuilders.length);
-        }
-
-        @Override
-        @Nullable
-        public Font next() {
-            Font font;
-            FontVariant ffdVariant;
-            if (index < fontFamilies.length) {
-                FontFamily_Delegate ffd = fontFamilies[index++];
-                if (ffd == null || !ffd.isValid()) {
-                    return null;
-                }
-                font = ffd.getFont(weight, isItalic);
-                ffdVariant = ffd.getVariant();
-            } else {
-                FontFamily_Builder_Delegate ffd = fontFamilyBuilders[index++ - fontFamilies.length];
-                if (ffd == null) {
-                    return null;
-                }
-                font = ffd.getFont(weight, isItalic);
-                ffdVariant = ffd.getVariant();
-            }
-
-            if (font == null) {
-                // The FontFamily is valid but doesn't contain any matching font. This means
-                // that the font failed to load. We add null to the list of fonts. Don't throw
-                // the warning just yet. If this is a non-english font, we don't want to warn
-                // users who are trying to render only english text.
-                return null;
-            }
-
-            if (ffdVariant == FontVariant.NONE || ffdVariant == variant) {
-                return font;
-            }
-
-            // We cannot open each font and get locales supported, etc to match the fonts.
-            // As a workaround, we hardcode certain assumptions like Elegant and Compact
-            // always appear in pairs.
-            if (index < fontFamilies.length) {
-                assert index < fontFamilies.length - 1;
-                FontFamily_Delegate ffd2 = fontFamilies[index++];
-                assert ffd2 != null;
-
-                return ffd2.getFont(weight, isItalic);
-            } else {
-                assert index < fontFamilies.length + fontFamilyBuilders.length - 1;
-                FontFamily_Builder_Delegate ffd2 = fontFamilyBuilders[index++ - fontFamilies.length];
-                assert ffd2 != null;
-
-                return ffd2.getFont(weight, isItalic);
-            }
-        }
-
-        @NonNull
-        @Override
-        public Iterator<Font> iterator() {
-            return this;
-        }
-
-        @Override
-        public Spliterator<Font> spliterator() {
-            return Spliterators.spliterator(iterator(),
-                    fontFamilies.length + fontFamilyBuilders.length,
-                    Spliterator.IMMUTABLE | Spliterator.SIZED);
-        }
-    }
-}
+}
\ No newline at end of file
diff --git a/bridge/src/android/graphics/animation/NativeInterpolatorFactory_Delegate.java b/bridge/src/android/graphics/animation/NativeInterpolatorFactory_Delegate.java
deleted file mode 100644
index 38ad602..0000000
--- a/bridge/src/android/graphics/animation/NativeInterpolatorFactory_Delegate.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics.animation;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.Path;
-import android.util.MathUtils;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AnticipateInterpolator;
-import android.view.animation.AnticipateOvershootInterpolator;
-import android.view.animation.BaseInterpolator;
-import android.view.animation.BounceInterpolator;
-import android.view.animation.CycleInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.OvershootInterpolator;
-import android.view.animation.PathInterpolator;
-
-/**
- * Delegate used to provide new implementation of a select few methods of {@link
- * NativeInterpolatorFactory}
- * <p>
- * Through the layoutlib_create tool, the original  methods of NativeInterpolatorFactory have
- * been replaced by calls to methods of the same name in this delegate class.
- */
-@SuppressWarnings("unused")
-public class NativeInterpolatorFactory_Delegate {
-    private static final DelegateManager<Interpolator> sManager = new DelegateManager<>
-            (Interpolator.class);
-
-    public static Interpolator getDelegate(long nativePtr) {
-        return sManager.getDelegate(nativePtr);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long createAccelerateDecelerateInterpolator() {
-        return sManager.addNewDelegate(new AccelerateDecelerateInterpolator());
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long createAccelerateInterpolator(float factor) {
-        return sManager.addNewDelegate(new AccelerateInterpolator(factor));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long createAnticipateInterpolator(float tension) {
-        return sManager.addNewDelegate(new AnticipateInterpolator(tension));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long createAnticipateOvershootInterpolator(float tension) {
-        return sManager.addNewDelegate(new AnticipateOvershootInterpolator(tension));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long createBounceInterpolator() {
-        return sManager.addNewDelegate(new BounceInterpolator());
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long createCycleInterpolator(float cycles) {
-        return sManager.addNewDelegate(new CycleInterpolator(cycles));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long createDecelerateInterpolator(float factor) {
-        return sManager.addNewDelegate(new DecelerateInterpolator(factor));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long createLinearInterpolator() {
-        return sManager.addNewDelegate(new LinearInterpolator());
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long createOvershootInterpolator(float tension) {
-        return sManager.addNewDelegate(new OvershootInterpolator(tension));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long createPathInterpolator(float[] x, float[] y) {
-        Path path = new Path();
-        path.moveTo(x[0], y[0]);
-        for (int i = 1; i < x.length; i++) {
-            path.lineTo(x[i], y[i]);
-        }
-        return sManager.addNewDelegate(new PathInterpolator(path));
-    }
-
-    private static class LutInterpolator extends BaseInterpolator {
-        private final float[] mValues;
-        private final int mSize;
-
-        private LutInterpolator(float[] values) {
-            mValues = values;
-            mSize = mValues.length;
-        }
-
-        @Override
-        public float getInterpolation(float input) {
-            float lutpos = input * (mSize - 1);
-            if (lutpos >= (mSize - 1)) {
-                return mValues[mSize - 1];
-            }
-
-            int ipart = (int) lutpos;
-            float weight = lutpos - ipart;
-
-            int i1 = ipart;
-            int i2 = Math.min(i1 + 1, mSize - 1);
-
-            assert i1 >= 0 && i2 >= 0 : "Negatives in the interpolation";
-
-            return MathUtils.lerp(mValues[i1], mValues[i2], weight);
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long createLutInterpolator(float[] values) {
-        return sManager.addNewDelegate(new LutInterpolator(values));
-    }
-}
diff --git a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
index 9c272fa..92f4ab8 100644
--- a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
+++ b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License") {}
+ * 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
  *
@@ -16,283 +16,30 @@
 
 package android.graphics.drawable;
 
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.animation.NativeInterpolatorFactory_Delegate;
+import android.graphics.Canvas;
 import android.graphics.drawable.AnimatedVectorDrawable.VectorDrawableAnimatorRT;
-import android.graphics.drawable.VectorDrawable_Delegate.VFullPath_Delegate;
-import android.graphics.drawable.VectorDrawable_Delegate.VGroup_Delegate;
-import android.graphics.drawable.VectorDrawable_Delegate.VNativeObject;
-import android.graphics.drawable.VectorDrawable_Delegate.VPathRenderer_Delegate;
 
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-/**
- * Delegate used to provide new implementation of a select few methods of {@link
- * AnimatedVectorDrawable}
- * <p>
- * Through the layoutlib_create tool, the original  methods of AnimatedVectorDrawable have been
- * replaced by calls to methods of the same name in this delegate class.
- */
-@SuppressWarnings("unused")
 public class AnimatedVectorDrawable_Delegate {
-    private static DelegateManager<AnimatorSetHolder> sAnimatorSets = new
-            DelegateManager<>(AnimatorSetHolder.class);
-    private static DelegateManager<PropertySetter> sHolders = new
-            DelegateManager<>(PropertySetter.class);
-
-
-    @LayoutlibDelegate
-    /*package*/ static long nCreateAnimatorSet() {
-        return sAnimatorSets.addNewDelegate(new AnimatorSetHolder());
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetVectorDrawableTarget(long animatorPtr, long vectorDrawablePtr) {
-        // TODO: implement
-    }
-    @LayoutlibDelegate
-    /*package*/ static void nAddAnimator(long setPtr, long propertyValuesHolder,
-            long nativeInterpolator, long startDelay, long duration, int repeatCount,
-            int repeatMode) {
-        PropertySetter holder = sHolders.getDelegate(propertyValuesHolder);
-        if (holder == null || holder.getValues() == null) {
-            return;
-        }
-
-        ObjectAnimator animator = new ObjectAnimator();
-        animator.setValues(holder.getValues());
-        animator.setInterpolator(
-                NativeInterpolatorFactory_Delegate.getDelegate(nativeInterpolator));
-        animator.setStartDelay(startDelay);
-        animator.setDuration(duration);
-        animator.setRepeatCount(repeatCount);
-        animator.setRepeatMode(repeatMode);
-        animator.setTarget(holder);
-        animator.setPropertyName(holder.getValues().getPropertyName());
-
-        AnimatorSetHolder set = sAnimatorSets.getDelegate(setPtr);
-        assert set != null;
-        set.addAnimator(animator);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nCreateGroupPropertyHolder(long nativePtr, int propertyId,
-            float startValue, float endValue) {
-        VGroup_Delegate group = VNativeObject.getDelegate(nativePtr);
-        Consumer<Float> setter = group.getPropertySetter(propertyId);
-
-        return sHolders.addNewDelegate(FloatPropertySetter.of(setter, startValue,
-                endValue));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nCreatePathDataPropertyHolder(long nativePtr, long startValuePtr,
-            long endValuePtr) {
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED, "AnimatedVectorDrawable path " +
-                "animations are not supported.", null, null, null);
-        return 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nCreatePathColorPropertyHolder(long nativePtr, int propertyId,
-            int startValue, int endValue) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(nativePtr);
-        Consumer<Integer> setter = path.getIntPropertySetter(propertyId);
-
-        return sHolders.addNewDelegate(IntPropertySetter.of(setter, startValue,
-                endValue));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nCreatePathPropertyHolder(long nativePtr, int propertyId,
-            float startValue, float endValue) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(nativePtr);
-        Consumer<Float> setter = path.getFloatPropertySetter(propertyId);
-
-        return sHolders.addNewDelegate(FloatPropertySetter.of(setter, startValue,
-                endValue));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nCreateRootAlphaPropertyHolder(long nativePtr, float startValue,
-            float endValue) {
-        VPathRenderer_Delegate renderer = VNativeObject.getDelegate(nativePtr);
-
-        return sHolders.addNewDelegate(FloatPropertySetter.of(renderer::setRootAlpha,
-                startValue,
-                endValue));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetPropertyHolderData(long nativePtr, float[] data, int length) {
-        PropertySetter setter = sHolders.getDelegate(nativePtr);
-        assert setter != null;
-
-        setter.setValues(data);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetPropertyHolderData(long nativePtr, int[] data, int length) {
-        PropertySetter setter = sHolders.getDelegate(nativePtr);
-        assert setter != null;
-
-        setter.setValues(data);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id) {
-        AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
-        assert animatorSet != null;
-
-        animatorSet.start();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id) {
-        AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
-        assert animatorSet != null;
-
-        animatorSet.reverse();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nEnd(long animatorSetPtr) {
-        AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
-        assert animatorSet != null;
-
-        animatorSet.end();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nReset(long animatorSetPtr) {
-        AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
-        assert animatorSet != null;
-
-        animatorSet.end();
-        animatorSet.start();
-    }
-
-    private static class AnimatorSetHolder {
-        private ArrayList<Animator> mAnimators = new ArrayList<>();
-        private AnimatorSet mAnimatorSet = null;
-
-        private void addAnimator(@NonNull Animator animator) {
-            mAnimators.add(animator);
-        }
-
-        private void ensureAnimatorSet() {
-            if (mAnimatorSet == null) {
-                mAnimatorSet = new AnimatorSet();
-                mAnimatorSet.playTogether(mAnimators);
-            }
-        }
-
-        private void start() {
-            ensureAnimatorSet();
-
-            mAnimatorSet.start();
-        }
-
-        private void end() {
-            mAnimatorSet.end();
-        }
-
-        private void reset() {
-            end();
-            start();
-        }
-
-        private void reverse() {
-            mAnimatorSet.reverse();
-        }
-    }
 
     /**
-     * Class that allows setting a value and holds the range of values for the given property.
-     *
-     * @param <T> the type of the property
+     * We would like to do the same as in {@link AnimatedVectorDrawable#draw}, but bypass the
+     * {@link Canvas#isHardwareAccelerated} check and call
+     * {@link AnimatedVectorDrawable#forceAnimationOnUI}. We need this for the callbacks to be
+     * properly set up so that {@link AnimatedVectorDrawable} receives property updates.
+     * TODO (b/141682855): Figure out how to properly manage this in the hardware accelerated case
      */
-    private static class PropertySetter<T> {
-        final Consumer<T> mValueSetter;
-        private PropertyValuesHolder mValues;
-
-        private PropertySetter(@NonNull Consumer<T> valueSetter) {
-            mValueSetter = valueSetter;
+    @LayoutlibDelegate
+    static void draw(AnimatedVectorDrawable thisDrawable, Canvas canvas) {
+        if (thisDrawable.mAnimatorSet instanceof VectorDrawableAnimatorRT) {
+            // If we have SW canvas and the RT animation is waiting to start, We need to fallback
+            // to UI thread animation for AVD.
+            if (!thisDrawable.mAnimatorSet.isRunning() &&
+                    ((VectorDrawableAnimatorRT)thisDrawable.mAnimatorSet).mPendingAnimationActions.size() > 0) {
+                thisDrawable.forceAnimationOnUI();
+            }
         }
-
-        /**
-         * Method to set an {@link Integer} value for this property. The default implementation of
-         * this method doesn't do anything. This method is accessed via reflection by the
-         * PropertyValuesHolder.
-         */
-        public void setIntValue(Integer value) {
-        }
-
-        /**
-         * Method to set an {@link Integer} value for this property. The default implementation of
-         * this method doesn't do anything. This method is accessed via reflection by the
-         * PropertyValuesHolder.
-         */
-        public void setFloatValue(Float value) {
-        }
-
-        void setValues(float... values) {
-            mValues = PropertyValuesHolder.ofFloat("floatValue", values);
-        }
-
-        @Nullable
-        PropertyValuesHolder getValues() {
-            return mValues;
-        }
-
-        void setValues(int... values) {
-            mValues = PropertyValuesHolder.ofInt("intValue", values);
-        }
-    }
-
-    private static class IntPropertySetter extends PropertySetter<Integer> {
-        private IntPropertySetter(Consumer<Integer> valueSetter) {
-            super(valueSetter);
-        }
-
-        private static PropertySetter of(Consumer<Integer> valueSetter, int... values) {
-            PropertySetter setter = new IntPropertySetter(valueSetter);
-            setter.setValues(values);
-
-            return setter;
-        }
-
-        public void setIntValue(Integer value) {
-            mValueSetter.accept(value);
-        }
-    }
-
-    private static class FloatPropertySetter extends PropertySetter<Float> {
-        private FloatPropertySetter(Consumer<Float> valueSetter) {
-            super(valueSetter);
-        }
-
-        private static PropertySetter of(Consumer<Float> valueSetter, float... values) {
-            PropertySetter setter = new FloatPropertySetter(valueSetter);
-            setter.setValues(values);
-
-            return setter;
-        }
-
-        public void setFloatValue(Float value) {
-            mValueSetter.accept(value);
-        }
-
+        thisDrawable.draw_Original(canvas);
     }
 }
diff --git a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java
deleted file mode 100644
index fc848d9..0000000
--- a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics.drawable;
-
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.Canvas;
-import android.graphics.drawable.AnimatedVectorDrawable.VectorDrawableAnimatorRT;
-
-public class AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate {
-    @LayoutlibDelegate
-    /*package*/ static boolean useLastSeenTarget(VectorDrawableAnimatorRT thisDrawableAnimator) {
-        return true;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void onDraw(VectorDrawableAnimatorRT thisDrawableAnimator, Canvas canvas) {
-        // Do not attempt to record as we are not using a DisplayListCanvas
-    }
-}
diff --git a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorUI_Delegate.java b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorUI_Delegate.java
new file mode 100644
index 0000000..b21b372
--- /dev/null
+++ b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorUI_Delegate.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") {}
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.animation.AnimationHandler;
+import android.graphics.Canvas;
+import android.graphics.drawable.AnimatedVectorDrawable.VectorDrawableAnimatorUI;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link
+ * AnimatedVectorDrawable}
+ * <p>
+ * Through the layoutlib_create tool, the original  methods of AnimatedVectorDrawable have been
+ * replaced by calls to methods of the same name in this delegate class.
+ */
+@SuppressWarnings("unused")
+public class AnimatedVectorDrawable_VectorDrawableAnimatorUI_Delegate {
+
+    public static long sFrameTime;
+
+    @LayoutlibDelegate
+    /*package*/ static void onDraw(VectorDrawableAnimatorUI thisDrawableAnimator, Canvas canvas) {
+        thisDrawableAnimator.onDraw_Original(canvas);
+        AnimationHandler handler = AnimationHandler.getInstance();
+        if (thisDrawableAnimator.mSet.mLastFrameTime < 0) {
+            handler.doAnimationFrame(0);
+        }
+        handler.doAnimationFrame(sFrameTime);
+    }
+}
diff --git a/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java b/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java
deleted file mode 100644
index a3ad2aa..0000000
--- a/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics.drawable;
-
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.Path;
-import android.graphics.drawable.GradientDrawable.GradientState;
-
-import java.lang.reflect.Field;
-
-/**
- * Delegate implementing the native methods of {@link GradientDrawable}
- *
- * Through the layoutlib_create tool, the original native methods of GradientDrawable have been
- * replaced by calls to methods of the same name in this delegate class.
- */
-public class GradientDrawable_Delegate {
-
-    /**
-     * The ring can be built either by drawing full circles, or by drawing arcs in case the
-     * circle isn't complete. LayoutLib cannot handle drawing full circles (requires path
-     * subtraction). So, if we need to draw full circles, we switch to drawing 99% circle.
-     */
-    @LayoutlibDelegate
-    /*package*/ static Path buildRing(GradientDrawable thisDrawable, GradientState st) {
-        boolean useLevel = st.mUseLevelForShape;
-        int level = thisDrawable.getLevel();
-        // 10000 is the max level. See android.graphics.drawable.Drawable#getLevel()
-        float sweep = useLevel ? (360.0f * level / 10000.0f) : 360f;
-        Field mLevel = null;
-        if (sweep >= 360 || sweep <= -360) {
-            st.mUseLevelForShape = true;
-            // Use reflection to set the value of the field to prevent setting the drawable to
-            // dirty again.
-            try {
-                mLevel = Drawable.class.getDeclaredField("mLevel");
-                mLevel.setAccessible(true);
-                mLevel.setInt(thisDrawable, 9999);  // set to one less than max.
-            } catch (NoSuchFieldException e) {
-                // The field has been removed in a recent framework change. Fall back to old
-                // buggy behaviour.
-            } catch (IllegalAccessException e) {
-                // We've already set the field to be accessible.
-                assert false;
-            }
-        }
-        Path path = thisDrawable.buildRing_Original(st);
-        st.mUseLevelForShape = useLevel;
-        if (mLevel != null) {
-            try {
-                mLevel.setInt(thisDrawable, level);
-            } catch (IllegalAccessException e) {
-                assert false;
-            }
-        }
-        return path;
-    }
-}
diff --git a/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java b/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
deleted file mode 100644
index 828df6b..0000000
--- a/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
+++ /dev/null
@@ -1,1299 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics.drawable;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.content.res.Resources;
-import android.content.res.Resources.Theme;
-import android.content.res.TypedArray;
-import android.graphics.BaseCanvas_Delegate;
-import android.graphics.Canvas_Delegate;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Paint.Cap;
-import android.graphics.Paint.Join;
-import android.graphics.Paint_Delegate;
-import android.graphics.Path;
-import android.graphics.Path.FillType;
-import android.graphics.PathMeasure;
-import android.graphics.Path_Delegate;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.Region.Op;
-import android.graphics.Shader_Delegate;
-import android.util.ArrayMap;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.MathUtils;
-import android.util.PathParser_Delegate;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-import static android.graphics.Canvas.CLIP_SAVE_FLAG;
-import static android.graphics.Canvas.MATRIX_SAVE_FLAG;
-import static android.graphics.Paint.Cap.BUTT;
-import static android.graphics.Paint.Cap.ROUND;
-import static android.graphics.Paint.Cap.SQUARE;
-import static android.graphics.Paint.Join.BEVEL;
-import static android.graphics.Paint.Join.MITER;
-import static android.graphics.Paint.Style;
-
-/**
- * Delegate used to provide new implementation of a select few methods of {@link VectorDrawable}
- * <p>
- * Through the layoutlib_create tool, the original  methods of VectorDrawable have been replaced by
- * calls to methods of the same name in this delegate class.
- */
-@SuppressWarnings("unused")
-public class VectorDrawable_Delegate {
-    private static final String LOGTAG = VectorDrawable_Delegate.class.getSimpleName();
-    private static final boolean DBG_VECTOR_DRAWABLE = false;
-
-    private static final DelegateManager<VNativeObject> sPathManager =
-            new DelegateManager<>(VNativeObject.class);
-
-    private static long addNativeObject(VNativeObject object) {
-        long ptr = sPathManager.addNewDelegate(object);
-        object.setNativePtr(ptr);
-
-        return ptr;
-    }
-
-    /**
-     * Obtains styled attributes from the theme, if available, or unstyled resources if the theme is
-     * null.
-     */
-    private static TypedArray obtainAttributes(
-            Resources res, Theme theme, AttributeSet set, int[] attrs) {
-        if (theme == null) {
-            return res.obtainAttributes(set, attrs);
-        }
-        return theme.obtainStyledAttributes(set, attrs, 0, 0);
-    }
-
-    private static int applyAlpha(int color, float alpha) {
-        int alphaBytes = Color.alpha(color);
-        color &= 0x00FFFFFF;
-        color |= ((int) (alphaBytes * alpha)) << 24;
-        return color;
-    }
-
-    @LayoutlibDelegate
-    static long nCreateTree(long rootGroupPtr) {
-        return addNativeObject(new VPathRenderer_Delegate(rootGroupPtr));
-    }
-
-    @LayoutlibDelegate
-    static long nCreateTreeFromCopy(long rendererToCopyPtr, long rootGroupPtr) {
-        VPathRenderer_Delegate rendererToCopy = VNativeObject.getDelegate(rendererToCopyPtr);
-        return addNativeObject(new VPathRenderer_Delegate(rendererToCopy,
-                rootGroupPtr));
-    }
-
-    @LayoutlibDelegate
-    static void nSetRendererViewportSize(long rendererPtr, float viewportWidth,
-            float viewportHeight) {
-        VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
-        nativePathRenderer.mViewportWidth = viewportWidth;
-        nativePathRenderer.mViewportHeight = viewportHeight;
-    }
-
-    @LayoutlibDelegate
-    static boolean nSetRootAlpha(long rendererPtr, float alpha) {
-        VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
-        nativePathRenderer.setRootAlpha(alpha);
-
-        return true;
-    }
-
-    @LayoutlibDelegate
-    static float nGetRootAlpha(long rendererPtr) {
-        VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
-
-        return nativePathRenderer.getRootAlpha();
-    }
-
-    @LayoutlibDelegate
-    static void nSetAntiAlias(long rendererPtr, boolean aa) {
-        VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
-        nativePathRenderer.setAntiAlias(aa);
-    }
-
-    @LayoutlibDelegate
-    static void nSetAllowCaching(long rendererPtr, boolean allowCaching) {
-        // ignored
-    }
-
-    @LayoutlibDelegate
-    static int nDraw(long rendererPtr, long canvasWrapperPtr,
-            long colorFilterPtr, Rect bounds, boolean needsMirroring, boolean canReuseCache) {
-        VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
-
-        Canvas_Delegate.nSave(canvasWrapperPtr, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
-        Canvas_Delegate.nClipRect(canvasWrapperPtr,
-                bounds.left, bounds.top, bounds.right, bounds.bottom,
-                Region.Op.INTERSECT.nativeInt);
-        Canvas_Delegate.nTranslate(canvasWrapperPtr, bounds.left, bounds.top);
-
-        if (needsMirroring) {
-            Canvas_Delegate.nTranslate(canvasWrapperPtr, bounds.width(), 0);
-            Canvas_Delegate.nScale(canvasWrapperPtr, -1.0f, 1.0f);
-        }
-
-        // At this point, canvas has been translated to the right position.
-        // And we use this bound for the destination rect for the drawBitmap, so
-        // we offset to (0, 0);
-        bounds.offsetTo(0, 0);
-        nativePathRenderer.draw(canvasWrapperPtr, colorFilterPtr, bounds.width(), bounds.height());
-
-        Canvas_Delegate.nRestore(canvasWrapperPtr);
-
-        return bounds.width() * bounds.height();
-    }
-
-    @LayoutlibDelegate
-    static long nCreateFullPath() {
-        return addNativeObject(new VFullPath_Delegate());
-    }
-
-    @LayoutlibDelegate
-    static long nCreateFullPath(long nativeFullPathPtr) {
-        VFullPath_Delegate original = VNativeObject.getDelegate(nativeFullPathPtr);
-        return addNativeObject(new VFullPath_Delegate(original));
-    }
-
-    @LayoutlibDelegate
-    static boolean nGetFullPathProperties(long pathPtr, byte[] propertiesData,
-            int length) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-
-        ByteBuffer properties = ByteBuffer.wrap(propertiesData);
-        properties.order(ByteOrder.nativeOrder());
-
-        properties.putFloat(VFullPath_Delegate.STROKE_WIDTH_INDEX * 4, path.getStrokeWidth());
-        properties.putInt(VFullPath_Delegate.STROKE_COLOR_INDEX * 4, path.getStrokeColor());
-        properties.putFloat(VFullPath_Delegate.STROKE_ALPHA_INDEX * 4, path.getStrokeAlpha());
-        properties.putInt(VFullPath_Delegate.FILL_COLOR_INDEX * 4, path.getFillColor());
-        properties.putFloat(VFullPath_Delegate.FILL_ALPHA_INDEX * 4, path.getStrokeAlpha());
-        properties.putFloat(VFullPath_Delegate.TRIM_PATH_START_INDEX * 4, path.getTrimPathStart());
-        properties.putFloat(VFullPath_Delegate.TRIM_PATH_END_INDEX * 4, path.getTrimPathEnd());
-        properties.putFloat(VFullPath_Delegate.TRIM_PATH_OFFSET_INDEX * 4,
-                path.getTrimPathOffset());
-        properties.putInt(VFullPath_Delegate.STROKE_LINE_CAP_INDEX * 4, path.getStrokeLineCap());
-        properties.putInt(VFullPath_Delegate.STROKE_LINE_JOIN_INDEX * 4, path.getStrokeLineJoin());
-        properties.putFloat(VFullPath_Delegate.STROKE_MITER_LIMIT_INDEX * 4,
-                path.getStrokeMiterlimit());
-        properties.putInt(VFullPath_Delegate.FILL_TYPE_INDEX * 4, path.getFillType());
-
-        return true;
-    }
-
-    @LayoutlibDelegate
-    static void nUpdateFullPathProperties(long pathPtr, float strokeWidth,
-            int strokeColor, float strokeAlpha, int fillColor, float fillAlpha, float trimPathStart,
-            float trimPathEnd, float trimPathOffset, float strokeMiterLimit, int strokeLineCap,
-            int strokeLineJoin, int fillType) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-
-        path.setStrokeWidth(strokeWidth);
-        path.setStrokeColor(strokeColor);
-        path.setStrokeAlpha(strokeAlpha);
-        path.setFillColor(fillColor);
-        path.setFillAlpha(fillAlpha);
-        path.setTrimPathStart(trimPathStart);
-        path.setTrimPathEnd(trimPathEnd);
-        path.setTrimPathOffset(trimPathOffset);
-        path.setStrokeMiterlimit(strokeMiterLimit);
-        path.setStrokeLineCap(strokeLineCap);
-        path.setStrokeLineJoin(strokeLineJoin);
-        path.setFillType(fillType);
-    }
-
-    @LayoutlibDelegate
-    static void nUpdateFullPathFillGradient(long pathPtr, long fillGradientPtr) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-
-        path.setFillGradient(fillGradientPtr);
-    }
-
-    @LayoutlibDelegate
-    static void nUpdateFullPathStrokeGradient(long pathPtr, long strokeGradientPtr) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-
-        path.setStrokeGradient(strokeGradientPtr);
-    }
-
-    @LayoutlibDelegate
-    static long nCreateClipPath() {
-        return addNativeObject(new VClipPath_Delegate());
-    }
-
-    @LayoutlibDelegate
-    static long nCreateClipPath(long clipPathPtr) {
-        VClipPath_Delegate original = VNativeObject.getDelegate(clipPathPtr);
-        return addNativeObject(new VClipPath_Delegate(original));
-    }
-
-    @LayoutlibDelegate
-    static long nCreateGroup() {
-        return addNativeObject(new VGroup_Delegate());
-    }
-
-    @LayoutlibDelegate
-    static long nCreateGroup(long groupPtr) {
-        VGroup_Delegate original = VNativeObject.getDelegate(groupPtr);
-        return addNativeObject(new VGroup_Delegate(original, new ArrayMap<>()));
-    }
-
-    @LayoutlibDelegate
-    static void nSetName(long nodePtr, String name) {
-        VNativeObject group = VNativeObject.getDelegate(nodePtr);
-        group.setName(name);
-    }
-
-    @LayoutlibDelegate
-    static boolean nGetGroupProperties(long groupPtr, float[] propertiesData,
-            int length) {
-        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-
-        FloatBuffer properties = FloatBuffer.wrap(propertiesData);
-
-        properties.put(VGroup_Delegate.ROTATE_INDEX, group.getRotation());
-        properties.put(VGroup_Delegate.PIVOT_X_INDEX, group.getPivotX());
-        properties.put(VGroup_Delegate.PIVOT_Y_INDEX, group.getPivotY());
-        properties.put(VGroup_Delegate.SCALE_X_INDEX, group.getScaleX());
-        properties.put(VGroup_Delegate.SCALE_Y_INDEX, group.getScaleY());
-        properties.put(VGroup_Delegate.TRANSLATE_X_INDEX, group.getTranslateX());
-        properties.put(VGroup_Delegate.TRANSLATE_Y_INDEX, group.getTranslateY());
-
-        return true;
-    }
-    @LayoutlibDelegate
-    static void nUpdateGroupProperties(long groupPtr, float rotate, float pivotX,
-            float pivotY, float scaleX, float scaleY, float translateX, float translateY) {
-        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-
-        group.setRotation(rotate);
-        group.setPivotX(pivotX);
-        group.setPivotY(pivotY);
-        group.setScaleX(scaleX);
-        group.setScaleY(scaleY);
-        group.setTranslateX(translateX);
-        group.setTranslateY(translateY);
-    }
-
-    @LayoutlibDelegate
-    static void nAddChild(long groupPtr, long nodePtr) {
-        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-        group.mChildren.add(VNativeObject.getDelegate(nodePtr));
-    }
-
-    @LayoutlibDelegate
-    static void nSetPathString(long pathPtr, String pathString, int length) {
-        VPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        path.setPathData(PathParser_Delegate.createNodesFromPathData(pathString));
-    }
-
-    /**
-     * The setters and getters below for paths and groups are here temporarily, and will be removed
-     * once the animation in AVD is replaced with RenderNodeAnimator, in which case the animation
-     * will modify these properties in native. By then no JNI hopping would be necessary for VD
-     * during animation, and these setters and getters will be obsolete.
-     */
-    // Setters and getters during animation.
-    @LayoutlibDelegate
-    static float nGetRotation(long groupPtr) {
-        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-        return group.getRotation();
-    }
-
-    @LayoutlibDelegate
-    static void nSetRotation(long groupPtr, float rotation) {
-        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-        group.setRotation(rotation);
-    }
-
-    @LayoutlibDelegate
-    static float nGetPivotX(long groupPtr) {
-        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-        return group.getPivotX();
-    }
-
-    @LayoutlibDelegate
-    static void nSetPivotX(long groupPtr, float pivotX) {
-        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-        group.setPivotX(pivotX);
-    }
-
-    @LayoutlibDelegate
-    static float nGetPivotY(long groupPtr) {
-        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-        return group.getPivotY();
-    }
-
-    @LayoutlibDelegate
-    static void nSetPivotY(long groupPtr, float pivotY) {
-        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-        group.setPivotY(pivotY);
-    }
-
-    @LayoutlibDelegate
-    static float nGetScaleX(long groupPtr) {
-        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-        return group.getScaleX();
-    }
-
-    @LayoutlibDelegate
-    static void nSetScaleX(long groupPtr, float scaleX) {
-        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-        group.setScaleX(scaleX);
-    }
-
-    @LayoutlibDelegate
-    static float nGetScaleY(long groupPtr) {
-        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-        return group.getScaleY();
-    }
-
-    @LayoutlibDelegate
-    static void nSetScaleY(long groupPtr, float scaleY) {
-        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-        group.setScaleY(scaleY);
-    }
-
-    @LayoutlibDelegate
-    static float nGetTranslateX(long groupPtr) {
-        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-        return group.getTranslateX();
-    }
-
-    @LayoutlibDelegate
-    static void nSetTranslateX(long groupPtr, float translateX) {
-        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-        group.setTranslateX(translateX);
-    }
-
-    @LayoutlibDelegate
-    static float nGetTranslateY(long groupPtr) {
-        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-        return group.getTranslateY();
-    }
-
-    @LayoutlibDelegate
-    static void nSetTranslateY(long groupPtr, float translateY) {
-        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-        group.setTranslateY(translateY);
-    }
-
-    @LayoutlibDelegate
-    static void nSetPathData(long pathPtr, long pathDataPtr) {
-        VPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        path.setPathData(PathParser_Delegate.getDelegate(pathDataPtr).getPathDataNodes());
-    }
-
-    @LayoutlibDelegate
-    static float nGetStrokeWidth(long pathPtr) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        return path.getStrokeWidth();
-    }
-
-    @LayoutlibDelegate
-    static void nSetStrokeWidth(long pathPtr, float width) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        path.setStrokeWidth(width);
-    }
-
-    @LayoutlibDelegate
-    static int nGetStrokeColor(long pathPtr) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        return path.getStrokeColor();
-    }
-
-    @LayoutlibDelegate
-    static void nSetStrokeColor(long pathPtr, int strokeColor) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        path.setStrokeColor(strokeColor);
-    }
-
-    @LayoutlibDelegate
-    static float nGetStrokeAlpha(long pathPtr) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        return path.getStrokeAlpha();
-    }
-
-    @LayoutlibDelegate
-    static void nSetStrokeAlpha(long pathPtr, float alpha) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        path.setStrokeAlpha(alpha);
-    }
-
-    @LayoutlibDelegate
-    static int nGetFillColor(long pathPtr) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        return path.getFillColor();
-    }
-
-    @LayoutlibDelegate
-    static void nSetFillColor(long pathPtr, int fillColor) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        path.setFillColor(fillColor);
-    }
-
-    @LayoutlibDelegate
-    static float nGetFillAlpha(long pathPtr) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        return path.getFillAlpha();
-    }
-
-    @LayoutlibDelegate
-    static void nSetFillAlpha(long pathPtr, float fillAlpha) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        path.setFillAlpha(fillAlpha);
-    }
-
-    @LayoutlibDelegate
-    static float nGetTrimPathStart(long pathPtr) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        return path.getTrimPathStart();
-    }
-
-    @LayoutlibDelegate
-    static void nSetTrimPathStart(long pathPtr, float trimPathStart) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        path.setTrimPathStart(trimPathStart);
-    }
-
-    @LayoutlibDelegate
-    static float nGetTrimPathEnd(long pathPtr) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        return path.getTrimPathEnd();
-    }
-
-    @LayoutlibDelegate
-    static void nSetTrimPathEnd(long pathPtr, float trimPathEnd) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        path.setTrimPathEnd(trimPathEnd);
-    }
-
-    @LayoutlibDelegate
-    static float nGetTrimPathOffset(long pathPtr) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        return path.getTrimPathOffset();
-    }
-
-    @LayoutlibDelegate
-    static void nSetTrimPathOffset(long pathPtr, float trimPathOffset) {
-        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-        path.setTrimPathOffset(trimPathOffset);
-    }
-
-    /**
-     * Base class for all the internal Delegates that does two functions:
-     * <ol>
-     *     <li>Serves as base class to store all the delegates in one {@link DelegateManager}
-     *     <li>Provides setName for all the classes. {@link VPathRenderer_Delegate} does actually
-     *     not need it
-     * </ol>
-     */
-    abstract static class VNativeObject {
-        long mNativePtr = 0;
-
-        @NonNull
-        static <T> T getDelegate(long nativePtr) {
-            //noinspection unchecked
-            T vNativeObject = (T) sPathManager.getDelegate(nativePtr);
-
-            assert vNativeObject != null;
-            return vNativeObject;
-        }
-
-        abstract void setName(String name);
-
-        void setNativePtr(long nativePtr) {
-            mNativePtr = nativePtr;
-        }
-
-        /**
-         * Method to explicitly dispose native objects
-         */
-        void dispose() {
-        }
-    }
-
-    private static class VClipPath_Delegate extends VPath_Delegate {
-        private VClipPath_Delegate() {
-            // Empty constructor.
-        }
-
-        private VClipPath_Delegate(VClipPath_Delegate copy) {
-            super(copy);
-        }
-
-        @Override
-        public boolean isClipPath() {
-            return true;
-        }
-    }
-
-    static class VFullPath_Delegate extends VPath_Delegate {
-        // These constants need to be kept in sync with their values in VectorDrawable.VFullPath
-        private static final int STROKE_WIDTH_INDEX = 0;
-        private static final int STROKE_COLOR_INDEX = 1;
-        private static final int STROKE_ALPHA_INDEX = 2;
-        private static final int FILL_COLOR_INDEX = 3;
-        private static final int FILL_ALPHA_INDEX = 4;
-        private static final int TRIM_PATH_START_INDEX = 5;
-        private static final int TRIM_PATH_END_INDEX = 6;
-        private static final int TRIM_PATH_OFFSET_INDEX = 7;
-        private static final int STROKE_LINE_CAP_INDEX = 8;
-        private static final int STROKE_LINE_JOIN_INDEX = 9;
-        private static final int STROKE_MITER_LIMIT_INDEX = 10;
-        private static final int FILL_TYPE_INDEX = 11;
-
-        private static final int LINECAP_BUTT = 0;
-        private static final int LINECAP_ROUND = 1;
-        private static final int LINECAP_SQUARE = 2;
-
-        private static final int LINEJOIN_MITER = 0;
-        private static final int LINEJOIN_ROUND = 1;
-        private static final int LINEJOIN_BEVEL = 2;
-
-        @NonNull
-        public Consumer<Float> getFloatPropertySetter(int propertyIdx) {
-            switch (propertyIdx) {
-                case STROKE_WIDTH_INDEX:
-                    return this::setStrokeWidth;
-                case STROKE_ALPHA_INDEX:
-                    return this::setStrokeAlpha;
-                case FILL_ALPHA_INDEX:
-                    return this::setFillAlpha;
-                case TRIM_PATH_START_INDEX:
-                    return this::setTrimPathStart;
-                case TRIM_PATH_END_INDEX:
-                    return this::setTrimPathEnd;
-                case TRIM_PATH_OFFSET_INDEX:
-                    return this::setTrimPathOffset;
-            }
-
-            assert false : ("Invalid VFullPath_Delegate property index " + propertyIdx);
-            return t -> {};
-        }
-
-        @NonNull
-        public Consumer<Integer> getIntPropertySetter(int propertyIdx) {
-            switch (propertyIdx) {
-                case STROKE_COLOR_INDEX:
-                    return this::setStrokeColor;
-                case FILL_COLOR_INDEX:
-                    return this::setFillColor;
-            }
-
-            assert false : ("Invalid VFullPath_Delegate property index " + propertyIdx);
-            return t -> {};
-        }
-
-        /////////////////////////////////////////////////////
-        // Variables below need to be copied (deep copy if applicable) for mutation.
-
-        int mStrokeColor = Color.TRANSPARENT;
-        float mStrokeWidth = 0;
-
-        int mFillColor = Color.TRANSPARENT;
-        long mStrokeGradient = 0;
-        long mFillGradient = 0;
-        float mStrokeAlpha = 1.0f;
-        float mFillAlpha = 1.0f;
-        float mTrimPathStart = 0;
-        float mTrimPathEnd = 1;
-        float mTrimPathOffset = 0;
-
-        Cap mStrokeLineCap = BUTT;
-        Join mStrokeLineJoin = MITER;
-        float mStrokeMiterlimit = 4;
-
-        int mFillType = 0; // WINDING(0) is the default value. See Path.FillType
-
-        private VFullPath_Delegate() {
-            // Empty constructor.
-        }
-
-        private VFullPath_Delegate(VFullPath_Delegate copy) {
-            super(copy);
-
-            mStrokeColor = copy.mStrokeColor;
-            mStrokeWidth = copy.mStrokeWidth;
-            mStrokeAlpha = copy.mStrokeAlpha;
-            mFillColor = copy.mFillColor;
-            mFillAlpha = copy.mFillAlpha;
-            mTrimPathStart = copy.mTrimPathStart;
-            mTrimPathEnd = copy.mTrimPathEnd;
-            mTrimPathOffset = copy.mTrimPathOffset;
-
-            mStrokeLineCap = copy.mStrokeLineCap;
-            mStrokeLineJoin = copy.mStrokeLineJoin;
-            mStrokeMiterlimit = copy.mStrokeMiterlimit;
-
-            mStrokeGradient = copy.mStrokeGradient;
-            mFillGradient = copy.mFillGradient;
-            mFillType = copy.mFillType;
-        }
-
-        private int getStrokeLineCap() {
-            switch (mStrokeLineCap) {
-                case BUTT:
-                    return LINECAP_BUTT;
-                case ROUND:
-                    return LINECAP_ROUND;
-                case SQUARE:
-                    return LINECAP_SQUARE;
-                default:
-                    assert false;
-            }
-
-            return -1;
-        }
-
-        private void setStrokeLineCap(int cap) {
-            switch (cap) {
-                case LINECAP_BUTT:
-                    mStrokeLineCap = BUTT;
-                    break;
-                case LINECAP_ROUND:
-                    mStrokeLineCap = ROUND;
-                    break;
-                case LINECAP_SQUARE:
-                    mStrokeLineCap = SQUARE;
-                    break;
-                default:
-                    assert false;
-            }
-        }
-
-        private int getStrokeLineJoin() {
-            switch (mStrokeLineJoin) {
-                case MITER:
-                    return LINEJOIN_MITER;
-                case ROUND:
-                    return LINEJOIN_ROUND;
-                case BEVEL:
-                    return LINEJOIN_BEVEL;
-                default:
-                    assert false;
-            }
-
-            return -1;
-        }
-
-        private void setStrokeLineJoin(int join) {
-            switch (join) {
-                case LINEJOIN_BEVEL:
-                    mStrokeLineJoin = BEVEL;
-                    break;
-                case LINEJOIN_MITER:
-                    mStrokeLineJoin = MITER;
-                    break;
-                case LINEJOIN_ROUND:
-                    mStrokeLineJoin = Join.ROUND;
-                    break;
-                default:
-                    assert false;
-            }
-        }
-
-        private int getStrokeColor() {
-            return mStrokeColor;
-        }
-
-        private void setStrokeColor(int strokeColor) {
-            mStrokeColor = strokeColor;
-        }
-
-        private float getStrokeWidth() {
-            return mStrokeWidth;
-        }
-
-        private void setStrokeWidth(float strokeWidth) {
-            mStrokeWidth = strokeWidth;
-        }
-
-        private float getStrokeAlpha() {
-            return mStrokeAlpha;
-        }
-
-        private void setStrokeAlpha(float strokeAlpha) {
-            mStrokeAlpha = strokeAlpha;
-        }
-
-        private int getFillColor() {
-            return mFillColor;
-        }
-
-        private void setFillColor(int fillColor) {
-            mFillColor = fillColor;
-        }
-
-        private float getFillAlpha() {
-            return mFillAlpha;
-        }
-
-        private void setFillAlpha(float fillAlpha) {
-            mFillAlpha = fillAlpha;
-        }
-
-        private float getTrimPathStart() {
-            return mTrimPathStart;
-        }
-
-        private void setTrimPathStart(float trimPathStart) {
-            mTrimPathStart = trimPathStart;
-        }
-
-        private float getTrimPathEnd() {
-            return mTrimPathEnd;
-        }
-
-        private void setTrimPathEnd(float trimPathEnd) {
-            mTrimPathEnd = trimPathEnd;
-        }
-
-        private float getTrimPathOffset() {
-            return mTrimPathOffset;
-        }
-
-        private void setTrimPathOffset(float trimPathOffset) {
-            mTrimPathOffset = trimPathOffset;
-        }
-
-        private void setStrokeMiterlimit(float limit) {
-            mStrokeMiterlimit = limit;
-        }
-
-        private float getStrokeMiterlimit() {
-            return mStrokeMiterlimit;
-        }
-
-        private void setStrokeGradient(long gradientPtr) {
-            mStrokeGradient = gradientPtr;
-        }
-
-        private void setFillGradient(long gradientPtr) {
-            mFillGradient = gradientPtr;
-        }
-
-        private void setFillType(int fillType) {
-            mFillType = fillType;
-        }
-
-        private int getFillType() {
-            return mFillType;
-        }
-    }
-
-    static class VGroup_Delegate extends VNativeObject {
-        // This constants need to be kept in sync with their definitions in VectorDrawable.Group
-        private static final int ROTATE_INDEX = 0;
-        private static final int PIVOT_X_INDEX = 1;
-        private static final int PIVOT_Y_INDEX = 2;
-        private static final int SCALE_X_INDEX = 3;
-        private static final int SCALE_Y_INDEX = 4;
-        private static final int TRANSLATE_X_INDEX = 5;
-        private static final int TRANSLATE_Y_INDEX = 6;
-
-        public Consumer<Float> getPropertySetter(int propertyIdx) {
-            switch (propertyIdx) {
-                case ROTATE_INDEX:
-                    return this::setRotation;
-                case PIVOT_X_INDEX:
-                    return this::setPivotX;
-                case PIVOT_Y_INDEX:
-                    return this::setPivotY;
-                case SCALE_X_INDEX:
-                    return this::setScaleX;
-                case SCALE_Y_INDEX:
-                    return this::setScaleY;
-                case TRANSLATE_X_INDEX:
-                    return this::setTranslateX;
-                case TRANSLATE_Y_INDEX:
-                    return this::setTranslateY;
-            }
-
-            assert false : ("Invalid VGroup_Delegate property index " + propertyIdx);
-            return t -> {};
-        }
-
-        /////////////////////////////////////////////////////
-        // Variables below need to be copied (deep copy if applicable) for mutation.
-        final ArrayList<Object> mChildren = new ArrayList<>();
-        // mStackedMatrix is only used temporarily when drawing, it combines all
-        // the parents' local matrices with the current one.
-        private final Matrix mStackedMatrix = new Matrix();
-        // mLocalMatrix is updated based on the update of transformation information,
-        // either parsed from the XML or by animation.
-        private final Matrix mLocalMatrix = new Matrix();
-        private float mRotate = 0;
-        private float mPivotX = 0;
-        private float mPivotY = 0;
-        private float mScaleX = 1;
-        private float mScaleY = 1;
-        private float mTranslateX = 0;
-        private float mTranslateY = 0;
-        private int mChangingConfigurations;
-        private String mGroupName = null;
-
-        private VGroup_Delegate(VGroup_Delegate copy, ArrayMap<String, Object> targetsMap) {
-            mRotate = copy.mRotate;
-            mPivotX = copy.mPivotX;
-            mPivotY = copy.mPivotY;
-            mScaleX = copy.mScaleX;
-            mScaleY = copy.mScaleY;
-            mTranslateX = copy.mTranslateX;
-            mTranslateY = copy.mTranslateY;
-            mGroupName = copy.mGroupName;
-            mChangingConfigurations = copy.mChangingConfigurations;
-            if (mGroupName != null) {
-                targetsMap.put(mGroupName, this);
-            }
-
-            mLocalMatrix.set(copy.mLocalMatrix);
-        }
-
-        private VGroup_Delegate() {
-        }
-
-        private void updateLocalMatrix() {
-            // The order we apply is the same as the
-            // RenderNode.cpp::applyViewPropertyTransforms().
-            mLocalMatrix.reset();
-            mLocalMatrix.postTranslate(-mPivotX, -mPivotY);
-            mLocalMatrix.postScale(mScaleX, mScaleY);
-            mLocalMatrix.postRotate(mRotate, 0, 0);
-            mLocalMatrix.postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY);
-        }
-
-        /* Setters and Getters, used by animator from AnimatedVectorDrawable. */
-        private float getRotation() {
-            return mRotate;
-        }
-
-        private void setRotation(float rotation) {
-            if (rotation != mRotate) {
-                mRotate = rotation;
-                updateLocalMatrix();
-            }
-        }
-
-        private float getPivotX() {
-            return mPivotX;
-        }
-
-        private void setPivotX(float pivotX) {
-            if (pivotX != mPivotX) {
-                mPivotX = pivotX;
-                updateLocalMatrix();
-            }
-        }
-
-        private float getPivotY() {
-            return mPivotY;
-        }
-
-        private void setPivotY(float pivotY) {
-            if (pivotY != mPivotY) {
-                mPivotY = pivotY;
-                updateLocalMatrix();
-            }
-        }
-
-        private float getScaleX() {
-            return mScaleX;
-        }
-
-        private void setScaleX(float scaleX) {
-            if (scaleX != mScaleX) {
-                mScaleX = scaleX;
-                updateLocalMatrix();
-            }
-        }
-
-        private float getScaleY() {
-            return mScaleY;
-        }
-
-        private void setScaleY(float scaleY) {
-            if (scaleY != mScaleY) {
-                mScaleY = scaleY;
-                updateLocalMatrix();
-            }
-        }
-
-        private float getTranslateX() {
-            return mTranslateX;
-        }
-
-        private void setTranslateX(float translateX) {
-            if (translateX != mTranslateX) {
-                mTranslateX = translateX;
-                updateLocalMatrix();
-            }
-        }
-
-        private float getTranslateY() {
-            return mTranslateY;
-        }
-
-        private void setTranslateY(float translateY) {
-            if (translateY != mTranslateY) {
-                mTranslateY = translateY;
-                updateLocalMatrix();
-            }
-        }
-
-        @Override
-        public void setName(String name) {
-            mGroupName = name;
-        }
-
-        @Override
-        protected void dispose() {
-            mChildren.stream().filter(child -> child instanceof VNativeObject).forEach(child
-                    -> {
-                VNativeObject nativeObject = (VNativeObject) child;
-                if (nativeObject.mNativePtr != 0) {
-                    sPathManager.removeJavaReferenceFor(nativeObject.mNativePtr);
-                    nativeObject.mNativePtr = 0;
-                }
-                nativeObject.dispose();
-            });
-            mChildren.clear();
-        }
-
-        @Override
-        protected void finalize() throws Throwable {
-            super.finalize();
-        }
-    }
-
-    public static class VPath_Delegate extends VNativeObject {
-        protected PathParser_Delegate.PathDataNode[] mNodes = null;
-        String mPathName;
-        int mChangingConfigurations;
-
-        public VPath_Delegate() {
-            // Empty constructor.
-        }
-
-        public VPath_Delegate(VPath_Delegate copy) {
-            mPathName = copy.mPathName;
-            mChangingConfigurations = copy.mChangingConfigurations;
-            mNodes = copy.mNodes != null ? PathParser_Delegate.deepCopyNodes(copy.mNodes) : null;
-        }
-
-        public void toPath(Path path) {
-            path.reset();
-            if (mNodes != null) {
-                PathParser_Delegate.PathDataNode.nodesToPath(mNodes,
-                        Path_Delegate.getDelegate(path.mNativePath));
-            }
-        }
-
-        @Override
-        public void setName(String name) {
-            mPathName = name;
-        }
-
-        public boolean isClipPath() {
-            return false;
-        }
-
-        private void setPathData(PathParser_Delegate.PathDataNode[] nodes) {
-            if (!PathParser_Delegate.canMorph(mNodes, nodes)) {
-                // This should not happen in the middle of animation.
-                mNodes = PathParser_Delegate.deepCopyNodes(nodes);
-            } else {
-                PathParser_Delegate.updateNodes(mNodes, nodes);
-            }
-        }
-
-        @Override
-        void dispose() {
-            mNodes = null;
-        }
-    }
-
-    static class VPathRenderer_Delegate extends VNativeObject {
-        /* Right now the internal data structure is organized as a tree.
-         * Each node can be a group node, or a path.
-         * A group node can have groups or paths as children, but a path node has
-         * no children.
-         * One example can be:
-         *                 Root Group
-         *                /    |     \
-         *           Group    Path    Group
-         *          /     \             |
-         *         Path   Path         Path
-         *
-         */
-        // Variables that only used temporarily inside the draw() call, so there
-        // is no need for deep copying.
-        private final Path mPath;
-        private final Path mRenderPath;
-        private final Matrix mFinalPathMatrix = new Matrix();
-        private final long mRootGroupPtr;
-        private float mViewportWidth = 0;
-        private float mViewportHeight = 0;
-        private float mRootAlpha = 1.0f;
-        private Paint mStrokePaint;
-        private Paint mFillPaint;
-        private PathMeasure mPathMeasure;
-        private boolean mAntiAlias = true;
-
-        private VPathRenderer_Delegate(long rootGroupPtr) {
-            mRootGroupPtr = rootGroupPtr;
-            mPath = new Path();
-            mRenderPath = new Path();
-        }
-
-        private VPathRenderer_Delegate(VPathRenderer_Delegate rendererToCopy,
-                long rootGroupPtr) {
-            this(rootGroupPtr);
-            mViewportWidth = rendererToCopy.mViewportWidth;
-            mViewportHeight = rendererToCopy.mViewportHeight;
-            mRootAlpha = rendererToCopy.mRootAlpha;
-        }
-
-        private float getRootAlpha() {
-            return mRootAlpha;
-        }
-
-        void setRootAlpha(float alpha) {
-            mRootAlpha = alpha;
-        }
-
-        private void drawGroupTree(VGroup_Delegate currentGroup, Matrix currentMatrix,
-                long canvasPtr, int w, int h, long filterPtr) {
-            // Calculate current group's matrix by preConcat the parent's and
-            // and the current one on the top of the stack.
-            // Basically the Mfinal = Mviewport * M0 * M1 * M2;
-            // Mi the local matrix at level i of the group tree.
-            currentGroup.mStackedMatrix.set(currentMatrix);
-            currentGroup.mStackedMatrix.preConcat(currentGroup.mLocalMatrix);
-
-            // Save the current clip information, which is local to this group.
-            Canvas_Delegate.nSave(canvasPtr, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
-            // Draw the group tree in the same order as the XML file.
-            for (int i = 0; i < currentGroup.mChildren.size(); i++) {
-                Object child = currentGroup.mChildren.get(i);
-                if (child instanceof VGroup_Delegate) {
-                    VGroup_Delegate childGroup = (VGroup_Delegate) child;
-                    drawGroupTree(childGroup, currentGroup.mStackedMatrix,
-                            canvasPtr, w, h, filterPtr);
-                } else if (child instanceof VPath_Delegate) {
-                    VPath_Delegate childPath = (VPath_Delegate) child;
-                    drawPath(currentGroup, childPath, canvasPtr, w, h, filterPtr);
-                }
-            }
-            Canvas_Delegate.nRestore(canvasPtr);
-        }
-
-        public void draw(long canvasPtr, long filterPtr, int w, int h) {
-            // Traverse the tree in pre-order to draw.
-            drawGroupTree(VNativeObject.getDelegate(mRootGroupPtr), Matrix.IDENTITY_MATRIX, canvasPtr, w, h, filterPtr);
-        }
-
-        private void drawPath(VGroup_Delegate VGroup, VPath_Delegate VPath, long canvasPtr,
-                int w,
-                int h,
-                long filterPtr) {
-            final float scaleX = w / mViewportWidth;
-            final float scaleY = h / mViewportHeight;
-            final float minScale = Math.min(scaleX, scaleY);
-            final Matrix groupStackedMatrix = VGroup.mStackedMatrix;
-
-            mFinalPathMatrix.set(groupStackedMatrix);
-            mFinalPathMatrix.postScale(scaleX, scaleY);
-
-            final float matrixScale = getMatrixScale(groupStackedMatrix);
-            if (matrixScale == 0) {
-                // When either x or y is scaled to 0, we don't need to draw anything.
-                return;
-            }
-            VPath.toPath(mPath);
-            final Path path = mPath;
-
-            mRenderPath.reset();
-
-            if (VPath.isClipPath()) {
-                mRenderPath.setFillType(FillType.WINDING);
-                mRenderPath.addPath(path, mFinalPathMatrix);
-                Canvas_Delegate.nClipPath(canvasPtr, mRenderPath.mNativePath, Op
-                        .INTERSECT.nativeInt);
-            } else {
-                VFullPath_Delegate fullPath = (VFullPath_Delegate) VPath;
-                if (fullPath.mTrimPathStart != 0.0f || fullPath.mTrimPathEnd != 1.0f) {
-                    float start = (fullPath.mTrimPathStart + fullPath.mTrimPathOffset) % 1.0f;
-                    float end = (fullPath.mTrimPathEnd + fullPath.mTrimPathOffset) % 1.0f;
-
-                    if (mPathMeasure == null) {
-                        mPathMeasure = new PathMeasure();
-                    }
-                    mPathMeasure.setPath(mPath, false);
-
-                    float len = mPathMeasure.getLength();
-                    start = start * len;
-                    end = end * len;
-                    path.reset();
-                    if (start > end) {
-                        mPathMeasure.getSegment(start, len, path, true);
-                        mPathMeasure.getSegment(0f, end, path, true);
-                    } else {
-                        mPathMeasure.getSegment(start, end, path, true);
-                    }
-                    path.rLineTo(0, 0); // fix bug in measure
-                }
-                mRenderPath.addPath(path, mFinalPathMatrix);
-
-                if (fullPath.mFillColor != Color.TRANSPARENT) {
-                    if (mFillPaint == null) {
-                        mFillPaint = new Paint();
-                        mFillPaint.setStyle(Style.FILL);
-                        mFillPaint.setAntiAlias(mAntiAlias);
-                    }
-
-                    final Paint fillPaint = mFillPaint;
-                    fillPaint.setColor(applyAlpha(applyAlpha(fullPath.mFillColor, fullPath
-                      .mFillAlpha), getRootAlpha()));
-                    Paint_Delegate fillPaintDelegate = Paint_Delegate.getDelegate(fillPaint
-                            .getNativeInstance());
-                    // mFillPaint can not be null at this point so we will have a delegate
-                    assert fillPaintDelegate != null;
-                    fillPaintDelegate.setColorFilter(filterPtr);
-
-                    Shader_Delegate shaderDelegate =
-                            Shader_Delegate.getDelegate(fullPath.mFillGradient);
-                    if (shaderDelegate != null) {
-                        // If there is a shader, apply the local transformation to make sure
-                        // the gradient is transformed to match the viewport
-                        shaderDelegate.setLocalMatrix(mFinalPathMatrix.ni());
-                        shaderDelegate.setAlpha(fullPath.mFillAlpha);
-                    }
-
-                    fillPaintDelegate.setShader(fullPath.mFillGradient);
-                    Path_Delegate.nSetFillType(mRenderPath.mNativePath, fullPath.mFillType);
-                    BaseCanvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, fillPaint
-                            .getNativeInstance());
-                    if (shaderDelegate != null) {
-                        // Remove the local matrix
-                        shaderDelegate.setLocalMatrix(0);
-                    }
-                }
-
-                if (fullPath.mStrokeColor != Color.TRANSPARENT) {
-                    if (mStrokePaint == null) {
-                        mStrokePaint = new Paint();
-                        mStrokePaint.setStyle(Style.STROKE);
-                        mStrokePaint.setAntiAlias(mAntiAlias);
-                    }
-
-                    final Paint strokePaint = mStrokePaint;
-                    if (fullPath.mStrokeLineJoin != null) {
-                        strokePaint.setStrokeJoin(fullPath.mStrokeLineJoin);
-                    }
-
-                    if (fullPath.mStrokeLineCap != null) {
-                        strokePaint.setStrokeCap(fullPath.mStrokeLineCap);
-                    }
-
-                    strokePaint.setStrokeMiter(fullPath.mStrokeMiterlimit);
-                    strokePaint.setColor(applyAlpha(applyAlpha(fullPath.mStrokeColor, fullPath
-                      .mStrokeAlpha), getRootAlpha()));
-                    Paint_Delegate strokePaintDelegate = Paint_Delegate.getDelegate(strokePaint
-                            .getNativeInstance());
-                    // mStrokePaint can not be null at this point so we will have a delegate
-                    assert strokePaintDelegate != null;
-                    strokePaintDelegate.setColorFilter(filterPtr);
-                    final float finalStrokeScale = minScale * matrixScale;
-                    strokePaint.setStrokeWidth(fullPath.mStrokeWidth * finalStrokeScale);
-                    Shader_Delegate strokeShaderDelegate =
-                            Shader_Delegate.getDelegate(fullPath.mStrokeGradient);
-                    if (strokeShaderDelegate != null) {
-                        strokeShaderDelegate.setAlpha(fullPath.mStrokeAlpha);
-                    }
-                    strokePaintDelegate.setShader(fullPath.mStrokeGradient);
-                    BaseCanvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, strokePaint
-                            .getNativeInstance());
-                }
-            }
-        }
-
-        private float getMatrixScale(Matrix groupStackedMatrix) {
-            // Given unit vectors A = (0, 1) and B = (1, 0).
-            // After matrix mapping, we got A' and B'. Let theta = the angel b/t A' and B'.
-            // Therefore, the final scale we want is min(|A'| * sin(theta), |B'| * sin(theta)),
-            // which is (|A'| * |B'| * sin(theta)) / max (|A'|, |B'|);
-            // If  max (|A'|, |B'|) = 0, that means either x or y has a scale of 0.
-            //
-            // For non-skew case, which is most of the cases, matrix scale is computing exactly the
-            // scale on x and y axis, and take the minimal of these two.
-            // For skew case, an unit square will mapped to a parallelogram. And this function will
-            // return the minimal height of the 2 bases.
-            float[] unitVectors = new float[]{0, 1, 1, 0};
-            groupStackedMatrix.mapVectors(unitVectors);
-            float scaleX = MathUtils.mag(unitVectors[0], unitVectors[1]);
-            float scaleY = MathUtils.mag(unitVectors[2], unitVectors[3]);
-            float crossProduct = MathUtils.cross(unitVectors[0], unitVectors[1],
-                    unitVectors[2], unitVectors[3]);
-            float maxScale = MathUtils.max(scaleX, scaleY);
-
-            float matrixScale = 0;
-            if (maxScale > 0) {
-                matrixScale = MathUtils.abs(crossProduct) / maxScale;
-            }
-            if (DBG_VECTOR_DRAWABLE) {
-                Log.d(LOGTAG, "Scale x " + scaleX + " y " + scaleY + " final " + matrixScale);
-            }
-            return matrixScale;
-        }
-
-        private void setAntiAlias(boolean aa) {
-            mAntiAlias = aa;
-        }
-
-        @Override
-        public void setName(String name) {
-        }
-
-        @Override
-        protected void finalize() throws Throwable {
-            // The mRootGroupPtr is not explicitly freed by anything in the VectorDrawable so we
-            // need to free it here.
-            VNativeObject nativeObject = sPathManager.getDelegate(mRootGroupPtr);
-            sPathManager.removeJavaReferenceFor(mRootGroupPtr);
-            assert nativeObject != null;
-            nativeObject.dispose();
-
-            super.finalize();
-        }
-    }
-}
diff --git a/bridge/src/android/graphics/fonts/FontFamily_Builder_Delegate.java b/bridge/src/android/graphics/fonts/FontFamily_Builder_Delegate.java
deleted file mode 100644
index 018329d..0000000
--- a/bridge/src/android/graphics/fonts/FontFamily_Builder_Delegate.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2018 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.fonts;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.FontFamily_Delegate.FontInfo;
-import android.graphics.FontFamily_Delegate.FontVariant;
-import android.graphics.Paint;
-
-import java.awt.Font;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.nio.ByteBuffer;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-import static android.graphics.FontFamily_Delegate.computeMatch;
-import static android.graphics.FontFamily_Delegate.deriveFont;
-
-/**
- * Delegate implementing the native methods of android.graphics.fonts.FontFamily$Builder
- * <p>
- * Through the layoutlib_create tool, the original native methods of FontFamily$Builder have been
- * replaced by calls to methods of the same name in this delegate class.
- * <p>
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between it
- * and the original FontFamily$Builder class.
- *
- * @see DelegateManager
- */
-public class FontFamily_Builder_Delegate {
-    private static final DelegateManager<FontFamily_Builder_Delegate> sBuilderManager =
-            new DelegateManager<>(FontFamily_Builder_Delegate.class);
-
-    private static long sFontFamilyFinalizer = -1;
-
-    // Order does not really matter but we use a LinkedHashMap to get reproducible results across
-    // render calls
-    private Map<FontInfo, Font> mFonts = new LinkedHashMap<>();
-    /**
-     * The variant of the Font Family - compact or elegant.
-     * <p/>
-     * 0 is unspecified, 1 is compact and 2 is elegant. This needs to be kept in sync with values in
-     * android.graphics.FontFamily
-     *
-     * @see Paint#setElegantTextHeight(boolean)
-     */
-    private FontVariant mVariant;
-    private boolean mIsCustomFallback;
-
-    @LayoutlibDelegate
-    /*package*/ static long nInitBuilder() {
-        return sBuilderManager.addNewDelegate(new FontFamily_Builder_Delegate());
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nAddFont(long builderPtr, long fontPtr) {
-        FontFamily_Builder_Delegate familyBuilder = sBuilderManager.getDelegate(builderPtr);
-        Font_Builder_Delegate fontBuilder = Font_Builder_Delegate.sBuilderManager.getDelegate(fontPtr);
-        if (familyBuilder == null || fontBuilder == null) {
-            return;
-        }
-        Font font;
-        if (fontBuilder.filePath.equals("")) {
-            font = loadFontBuffer(fontBuilder.mBuffer);
-
-        } else {
-            font = loadFontPath(fontBuilder.filePath);
-        }
-        if (font != null) {
-            familyBuilder.addFont(font, fontBuilder.mWeight, fontBuilder.mItalic);
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nBuild(long builderPtr, String langTags, int variant,
-            boolean isCustomFallback) {
-        FontFamily_Builder_Delegate builder = sBuilderManager.getDelegate(builderPtr);
-        if (builder != null) {
-            assert variant < 3;
-            builder.mVariant = FontVariant.values()[variant];
-            builder.mIsCustomFallback = isCustomFallback;
-        }
-        return builderPtr;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nGetReleaseNativeFamily() {
-        synchronized (Font_Builder_Delegate.class) {
-            if (sFontFamilyFinalizer == -1) {
-                sFontFamilyFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
-                        sBuilderManager::removeJavaReferenceFor);
-            }
-        }
-        return sFontFamilyFinalizer;
-    }
-
-    public static FontFamily_Builder_Delegate getDelegate(long nativeFontFamily) {
-        return sBuilderManager.getDelegate(nativeFontFamily);
-    }
-
-    @Nullable
-    public Font getFont(int desiredWeight, boolean isItalic) {
-        FontInfo desiredStyle = new FontInfo();
-        desiredStyle.mWeight = desiredWeight;
-        desiredStyle.mIsItalic = isItalic;
-
-        Font cachedFont = mFonts.get(desiredStyle);
-        if (cachedFont != null) {
-            return cachedFont;
-        }
-
-        FontInfo bestFont = null;
-
-        if (mFonts.size() == 1) {
-            // No need to compute the match since we only have one candidate
-            bestFont = mFonts.keySet().iterator().next();
-        } else {
-            int bestMatch = Integer.MAX_VALUE;
-
-            for (FontInfo font : mFonts.keySet()) {
-                int match = computeMatch(font, desiredStyle);
-                if (match < bestMatch) {
-                    bestMatch = match;
-                    bestFont = font;
-                    if (bestMatch == 0) {
-                        break;
-                    }
-                }
-            }
-        }
-
-        if (bestFont == null) {
-            return null;
-        }
-
-
-        // Derive the font as required and add it to the list of Fonts.
-        deriveFont(bestFont, desiredStyle);
-        addFont(desiredStyle);
-        return desiredStyle.mFont;
-    }
-
-    public FontVariant getVariant() {
-        return mVariant;
-    }
-
-    // ---- private helper methods ----
-
-    private void addFont(@NonNull Font font, int weight, boolean italic) {
-        FontInfo fontInfo = new FontInfo();
-        fontInfo.mFont = font;
-        fontInfo.mWeight = weight;
-        fontInfo.mIsItalic = italic;
-        addFont(fontInfo);
-    }
-
-    private void addFont(@NonNull FontInfo fontInfo) {
-        mFonts.putIfAbsent(fontInfo, fontInfo.mFont);
-    }
-
-    private static Font loadFontBuffer(@NonNull ByteBuffer buffer) {
-        try {
-            byte[] byteArray = new byte[buffer.limit()];
-            buffer.rewind();
-            buffer.get(byteArray);
-            return Font.createFont(Font.TRUETYPE_FONT, new ByteArrayInputStream(byteArray));
-        } catch (Exception e) {
-            Bridge.getLog().fidelityWarning(ILayoutLog.TAG_BROKEN, "Unable to load font",
-                    e, null, null);
-        }
-
-        return null;
-    }
-
-    private static Font loadFontPath(@NonNull String path) {
-        try {
-            File file = new File(path);
-            return Font.createFont(Font.TRUETYPE_FONT, file);
-        } catch (Exception e) {
-            Bridge.getLog().fidelityWarning(ILayoutLog.TAG_BROKEN, "Unable to load font",
-                    e, null, null);
-        }
-
-        return null;
-    }
-}
diff --git a/bridge/src/android/graphics/fonts/Font_Builder_Delegate.java b/bridge/src/android/graphics/fonts/Font_Builder_Delegate.java
index 0d3ce3f..490659b 100644
--- a/bridge/src/android/graphics/fonts/Font_Builder_Delegate.java
+++ b/bridge/src/android/graphics/fonts/Font_Builder_Delegate.java
@@ -16,8 +16,6 @@
 
 package android.graphics.fonts;
 
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
@@ -31,8 +29,6 @@
 import java.nio.channels.Channels;
 import java.nio.channels.ReadableByteChannel;
 
-import libcore.util.NativeAllocationRegistry_Delegate;
-
 /**
  * Delegate implementing the native methods of android.graphics.fonts.Font$Builder
  * <p>
@@ -46,20 +42,6 @@
  * @see DelegateManager
  */
 public class Font_Builder_Delegate {
-    protected static final DelegateManager<Font_Builder_Delegate> sBuilderManager =
-            new DelegateManager<>(Font_Builder_Delegate.class);
-    private static long sFontFinalizer = -1;
-
-    protected ByteBuffer mBuffer;
-    protected int mWeight;
-    protected boolean mItalic;
-    protected int mTtcIndex;
-    protected String filePath;
-
-    @LayoutlibDelegate
-    /*package*/ static long nInitBuilder() {
-        return sBuilderManager.addNewDelegate(new Font_Builder_Delegate());
-    }
 
     @LayoutlibDelegate
     /*package*/ static ByteBuffer createBuffer(@NonNull AssetManager am, @NonNull String path,
@@ -80,35 +62,4 @@
             return buffer;
         }
     }
-
-    @LayoutlibDelegate
-    /*package*/ static void nAddAxis(long builderPtr, int tag, float value) {
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Font$Builder.nAddAxis is not supported.", null, null, null);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nBuild(long builderPtr, ByteBuffer buffer, String filePath, int weight,
-            boolean italic, int ttcIndex) {
-        Font_Builder_Delegate font = sBuilderManager.getDelegate(builderPtr);
-        if (font != null) {
-            font.mBuffer = buffer;
-            font.mWeight = weight;
-            font.mItalic = italic;
-            font.mTtcIndex = ttcIndex;
-            font.filePath = filePath;
-        }
-        return builderPtr;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nGetReleaseNativeFont() {
-        synchronized (Font_Builder_Delegate.class) {
-            if (sFontFinalizer == -1) {
-                sFontFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
-                        sBuilderManager::removeJavaReferenceFor);
-            }
-        }
-        return sFontFinalizer;
-    }
 }
diff --git a/bridge/src/android/graphics/fonts/SystemFonts_Delegate.java b/bridge/src/android/graphics/fonts/SystemFonts_Delegate.java
deleted file mode 100644
index 3f3e065..0000000
--- a/bridge/src/android/graphics/fonts/SystemFonts_Delegate.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2018 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.fonts;
-
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.text.FontConfig;
-
-import java.io.File;
-import java.util.Map;
-
-import static android.graphics.FontFamily_Delegate.getFontLocation;
-
-/**
- * Delegate implementing the native methods of android.graphics.fonts.SystemFonts
- * <p>
- * Through the layoutlib_create tool, the original native methods of SystemFonts have been
- * replaced by calls to methods of the same name in this delegate class.
- * <p>
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between it
- * and the original SystemFonts class.
- *
- * @see DelegateManager
- */
-public class SystemFonts_Delegate {
-
-    @LayoutlibDelegate
-    /*package*/ static FontConfig getSystemFontConfigInternal(
-            String fontsXml,
-            String systemFontDir,
-            String oemXml,
-            String productFontDir,
-            Map<String, File> updatableFontMap) {
-        Bridge.sIsTypefaceInitialized = true;
-        return SystemFonts.getSystemFontConfigInternal_Original(
-                getFontLocation() + "/standard/fonts.xml", getFontLocation() + "/",
-                null, null, updatableFontMap, 0, 0);
-    }
-}
diff --git a/bridge/src/android/graphics/text/BaseLineBreaker.java b/bridge/src/android/graphics/text/BaseLineBreaker.java
deleted file mode 100644
index 61fa216..0000000
--- a/bridge/src/android/graphics/text/BaseLineBreaker.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2018 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.text;
-
-import android.annotation.NonNull;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-// Based on the native implementation of LineBreaker in
-// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
-public abstract class BaseLineBreaker {
-
-    protected static final int TAB_MASK   = 0x20000000;  // keep in sync with StaticLayout
-
-    protected final @NonNull List<Primitive> mPrimitives;
-    protected final @NonNull
-    LineWidth mLineWidth;
-    protected final @NonNull
-    TabStops mTabStops;
-
-    public BaseLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth,
-            @NonNull TabStops tabStops) {
-        mPrimitives = Collections.unmodifiableList(primitives);
-        mLineWidth = lineWidth;
-        mTabStops = tabStops;
-    }
-
-    public abstract Result computeBreaks();
-
-    public static class Result {
-        List<Integer> mLineBreakOffset = new ArrayList<>();
-        List<Float> mLineWidths = new ArrayList<>();
-        List<Float> mLineAscents = new ArrayList<>();
-        List<Float> mLineDescents = new ArrayList<>();
-        List<Integer> mLineFlags = new ArrayList<>();
-    }
-}
diff --git a/bridge/src/android/graphics/text/GreedyLineBreaker.java b/bridge/src/android/graphics/text/GreedyLineBreaker.java
deleted file mode 100644
index 940e235..0000000
--- a/bridge/src/android/graphics/text/GreedyLineBreaker.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2018 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.text;
-
-import android.annotation.NonNull;
-import android.graphics.text.Primitive.PrimitiveType;
-
-import java.util.List;
-
-import static android.graphics.text.Primitive.PrimitiveType.PENALTY_INFINITY;
-
-// Based on the native implementation of GreedyLineBreaker in
-// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
-public class GreedyLineBreaker extends BaseLineBreaker {
-
-    public GreedyLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth,
-            @NonNull TabStops tabStops) {
-        super(primitives, lineWidth, tabStops);
-    }
-
-    @Override
-    public Result computeBreaks() {
-        int lineNum = 0;
-        float width = 0, printedWidth = 0;
-        boolean breakFound = false, goodBreakFound = false;
-        int breakIndex = 0, goodBreakIndex = 0;
-        float breakWidth = 0, goodBreakWidth = 0;
-        int firstTabIndex = Integer.MAX_VALUE;
-
-        float maxWidth = mLineWidth.getLineWidth(lineNum);
-
-        int numPrimitives = mPrimitives.size();
-        Result result = new Result();
-        // greedily fit as many characters as possible on each line
-        // loop over all primitives, and choose the best break point
-        // (if possible, a break point without splitting a word)
-        // after going over the maximum length
-        for (int i = 0; i < numPrimitives; i++) {
-            Primitive p = mPrimitives.get(i);
-
-            // update the current line width
-            if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) {
-                width += p.width;
-                if (p.type == PrimitiveType.BOX) {
-                    printedWidth = width;
-                }
-            } else if (p.type == PrimitiveType.VARIABLE) {
-                width = mTabStops.width(width);
-                // keep track of first tab character in the region we are examining
-                // so we can determine whether or not a line contains a tab
-                firstTabIndex = Math.min(firstTabIndex, i);
-            }
-
-            // find the best break point for the characters examined so far
-            if (printedWidth > maxWidth) {
-                //noinspection StatementWithEmptyBody
-                if (breakFound || goodBreakFound) {
-                    if (goodBreakFound) {
-                        // a true line break opportunity existed in the characters examined so far,
-                        // so there is no need to split a word
-                        i = goodBreakIndex; // no +1 because of i++
-                        lineNum++;
-                        maxWidth = mLineWidth.getLineWidth(lineNum);
-                        result.mLineBreakOffset.add(mPrimitives.get(goodBreakIndex).location);
-                        result.mLineWidths.add(goodBreakWidth);
-                        result.mLineAscents.add(0f);
-                        result.mLineDescents.add(0f);
-                        result.mLineFlags.add(firstTabIndex < goodBreakIndex ? TAB_MASK : 0);
-                        firstTabIndex = Integer.MAX_VALUE;
-                    } else {
-                        // must split a word because there is no other option
-                        i = breakIndex; // no +1 because of i++
-                        lineNum++;
-                        maxWidth = mLineWidth.getLineWidth(lineNum);
-                        result.mLineBreakOffset.add(mPrimitives.get(breakIndex).location);
-                        result.mLineWidths.add(breakWidth);
-                        result.mLineAscents.add(0f);
-                        result.mLineDescents.add(0f);
-                        result.mLineFlags.add(firstTabIndex < breakIndex ? TAB_MASK : 0);
-                        firstTabIndex = Integer.MAX_VALUE;
-                    }
-                    printedWidth = width = 0;
-                    goodBreakFound = breakFound = false;
-                    goodBreakWidth = breakWidth = 0;
-                    continue;
-                } else {
-                    // no choice, keep going... must make progress by putting at least one
-                    // character on a line, even if part of that character is cut off --
-                    // there is no other option
-                }
-            }
-
-            // update possible break points
-            if (p.type == PrimitiveType.PENALTY &&
-                    p.penalty < PENALTY_INFINITY) {
-                // this does not handle penalties with width
-
-                // handle forced line break
-                if (p.penalty == -PENALTY_INFINITY) {
-                    lineNum++;
-                    maxWidth = mLineWidth.getLineWidth(lineNum);
-                    result.mLineBreakOffset.add(p.location);
-                    result.mLineWidths.add(printedWidth);
-                    result.mLineAscents.add(0f);
-                    result.mLineDescents.add(0f);
-                    result.mLineFlags.add(firstTabIndex < i ? TAB_MASK : 0);
-                    firstTabIndex = Integer.MAX_VALUE;
-                    printedWidth = width = 0;
-                    goodBreakFound = breakFound = false;
-                    goodBreakWidth = breakWidth = 0;
-                    continue;
-                }
-                if (i > breakIndex && (printedWidth <= maxWidth || !breakFound)) {
-                    breakFound = true;
-                    breakIndex = i;
-                    breakWidth = printedWidth;
-                }
-                if (i > goodBreakIndex && printedWidth <= maxWidth) {
-                    goodBreakFound = true;
-                    goodBreakIndex = i;
-                    goodBreakWidth = printedWidth;
-                }
-            } else if (p.type == PrimitiveType.WORD_BREAK) {
-                // only do this if necessary -- we don't want to break words
-                // when possible, but sometimes it is unavoidable
-                if (i > breakIndex && (printedWidth <= maxWidth || !breakFound)) {
-                    breakFound = true;
-                    breakIndex = i;
-                    breakWidth = printedWidth;
-                }
-            }
-        }
-
-        if (breakFound || goodBreakFound) {
-            // output last break if there are more characters to output
-            if (goodBreakFound) {
-                result.mLineBreakOffset.add(mPrimitives.get(goodBreakIndex).location);
-                result.mLineWidths.add(goodBreakWidth);
-                result.mLineAscents.add(0f);
-                result.mLineDescents.add(0f);
-                result.mLineFlags.add(firstTabIndex < goodBreakIndex ? TAB_MASK : 0);
-            } else {
-                result.mLineBreakOffset.add(mPrimitives.get(breakIndex).location);
-                result.mLineWidths.add(breakWidth);
-                result.mLineAscents.add(0f);
-                result.mLineDescents.add(0f);
-                result.mLineFlags.add(firstTabIndex < breakIndex ? TAB_MASK : 0);
-            }
-        }
-        return result;
-    }
-}
diff --git a/bridge/src/android/graphics/text/LineBreaker_Delegate.java b/bridge/src/android/graphics/text/LineBreaker_Delegate.java
deleted file mode 100644
index 92c99cc..0000000
--- a/bridge/src/android/graphics/text/LineBreaker_Delegate.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright (C) 2018 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.text;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.icu.text.BreakIterator;
-import android.text.Layout;
-import android.text.Layout.BreakStrategy;
-import android.text.Layout.HyphenationFrequency;
-import android.graphics.text.Primitive.PrimitiveType;
-
-import java.text.CharacterIterator;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.swing.text.Segment;
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate that provides implementation for native methods in {@link android.text.StaticLayout}
- * <p/>
- * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- */
-public class LineBreaker_Delegate {
-
-    private static final char CHAR_SPACE     = 0x20;
-    private static final char CHAR_TAB       = 0x09;
-    private static final char CHAR_NEWLINE   = 0x0A;
-    private static final char CHAR_ZWSP      = 0x200B;  // Zero width space.
-
-    // ---- Builder delegate manager ----
-    private static final DelegateManager<Builder> sBuilderManager =
-        new DelegateManager<>(Builder.class);
-    private static long sFinalizer = -1;
-
-    // ---- Result delegate manager ----
-    private static final DelegateManager<Result> sResultManager =
-        new DelegateManager<>(Result.class);
-    private static long sResultFinalizer = -1;
-
-    @LayoutlibDelegate
-    /*package*/ static long nInit(
-            @BreakStrategy int breakStrategy,
-            @HyphenationFrequency int hyphenationFrequency,
-            boolean isJustified,
-            @Nullable int[] indents) {
-        Builder builder = new Builder();
-        builder.mBreakStrategy = breakStrategy;
-        return sBuilderManager.addNewDelegate(builder);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nGetReleaseFunc() {
-        synchronized (MeasuredText_Delegate.class) {
-            if (sFinalizer == -1) {
-                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
-                        sBuilderManager::removeJavaReferenceFor);
-            }
-        }
-        return sFinalizer;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nComputeLineBreaks(
-            /* non zero */ long nativePtr,
-
-            // Inputs
-            @NonNull char[] text,
-            long measuredTextPtr,
-            int length,
-            float firstWidth,
-            int firstWidthLineCount,
-            float restWidth,
-            @Nullable float[] variableTabStops,
-            float defaultTabStop,
-            int indentsOffset) {
-        Builder builder = sBuilderManager.getDelegate(nativePtr);
-        if (builder == null) {
-            return 0;
-        }
-
-        builder.mText = text;
-        builder.mWidths = new float[length];
-        builder.mLineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth);
-        builder.mTabStopCalculator = new TabStops(variableTabStops, defaultTabStop);
-
-        MeasuredText_Delegate.computeRuns(measuredTextPtr, builder);
-
-        // compute all possible breakpoints.
-        BreakIterator it = BreakIterator.getLineInstance();
-        it.setText((CharacterIterator) new Segment(builder.mText, 0, length));
-
-        // average word length in english is 5. So, initialize the possible breaks with a guess.
-        List<Integer> breaks = new ArrayList<Integer>((int) Math.ceil(length / 5d));
-        int loc;
-        it.first();
-        while ((loc = it.next()) != BreakIterator.DONE) {
-            breaks.add(loc);
-        }
-
-        List<Primitive> primitives =
-                computePrimitives(builder.mText, builder.mWidths, length, breaks);
-        switch (builder.mBreakStrategy) {
-            case Layout.BREAK_STRATEGY_SIMPLE:
-                builder.mLineBreaker = new GreedyLineBreaker(primitives, builder.mLineWidth,
-                        builder.mTabStopCalculator);
-                break;
-            case Layout.BREAK_STRATEGY_HIGH_QUALITY:
-                // TODO
-//                break;
-            case Layout.BREAK_STRATEGY_BALANCED:
-                builder.mLineBreaker = new OptimizingLineBreaker(primitives, builder.mLineWidth,
-                        builder.mTabStopCalculator);
-                break;
-            default:
-                assert false : "Unknown break strategy: " + builder.mBreakStrategy;
-                builder.mLineBreaker = new GreedyLineBreaker(primitives, builder.mLineWidth,
-                        builder.mTabStopCalculator);
-        }
-        Result result = new Result(builder.mLineBreaker.computeBreaks());
-        return sResultManager.addNewDelegate(result);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetLineCount(long ptr) {
-        Result result = sResultManager.getDelegate(ptr);
-        return result.mResult.mLineBreakOffset.size();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetLineBreakOffset(long ptr, int idx) {
-        Result result = sResultManager.getDelegate(ptr);
-        return result.mResult.mLineBreakOffset.get(idx);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetLineWidth(long ptr, int idx) {
-        Result result = sResultManager.getDelegate(ptr);
-        return result.mResult.mLineWidths.get(idx);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetLineAscent(long ptr, int idx) {
-        Result result = sResultManager.getDelegate(ptr);
-        return result.mResult.mLineAscents.get(idx);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetLineDescent(long ptr, int idx) {
-        Result result = sResultManager.getDelegate(ptr);
-        return result.mResult.mLineDescents.get(idx);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetLineFlag(long ptr, int idx) {
-        Result result = sResultManager.getDelegate(ptr);
-        return result.mResult.mLineFlags.get(idx);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nGetReleaseResultFunc() {
-        synchronized (MeasuredText_Delegate.class) {
-            if (sResultFinalizer == -1) {
-                sResultFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
-                        sBuilderManager::removeJavaReferenceFor);
-            }
-        }
-        return sResultFinalizer;
-    }
-
-    /**
-     * Compute metadata each character - things which help in deciding if it's possible to break
-     * at a point or not.
-     */
-    @NonNull
-    private static List<Primitive> computePrimitives(@NonNull char[] text, @NonNull float[] widths,
-            int length, @NonNull List<Integer> breaks) {
-        // Initialize the list with a guess of the number of primitives:
-        // 2 Primitives per non-whitespace char and approx 5 chars per word (i.e. 83% chars)
-        List<Primitive> primitives = new ArrayList<Primitive>(((int) Math.ceil(length * 1.833)));
-        int breaksSize = breaks.size();
-        int breakIndex = 0;
-        for (int i = 0; i < length; i++) {
-            char c = text[i];
-            if (c == CHAR_SPACE || c == CHAR_ZWSP) {
-                primitives.add(PrimitiveType.GLUE.getNewPrimitive(i, widths[i]));
-            } else if (c == CHAR_TAB) {
-                primitives.add(PrimitiveType.VARIABLE.getNewPrimitive(i));
-            } else if (c != CHAR_NEWLINE) {
-                while (breakIndex < breaksSize && breaks.get(breakIndex) < i) {
-                    breakIndex++;
-                }
-                Primitive p;
-                if (widths[i] != 0) {
-                    if (breakIndex < breaksSize && breaks.get(breakIndex) == i) {
-                        p = PrimitiveType.PENALTY.getNewPrimitive(i, 0, 0);
-                    } else {
-                        p = PrimitiveType.WORD_BREAK.getNewPrimitive(i, 0);
-                    }
-                    primitives.add(p);
-                }
-
-                primitives.add(PrimitiveType.BOX.getNewPrimitive(i, widths[i]));
-            }
-        }
-        // final break at end of everything
-        primitives.add(
-                PrimitiveType.PENALTY.getNewPrimitive(length, 0, -PrimitiveType.PENALTY_INFINITY));
-        return primitives;
-    }
-
-    // TODO: Rename to LineBreakerRef and move everything other than LineBreaker to LineBreaker.
-    /**
-     * Java representation of the native Builder class.
-     */
-    public static class Builder {
-        char[] mText;
-        float[] mWidths;
-        private BaseLineBreaker mLineBreaker;
-        private int mBreakStrategy;
-        private LineWidth mLineWidth;
-        private TabStops mTabStopCalculator;
-    }
-
-    public abstract static class Run {
-        int mStart;
-        int mEnd;
-
-        Run(int start, int end) {
-            mStart = start;
-            mEnd = end;
-        }
-
-        abstract void addTo(Builder builder);
-    }
-
-    public static class Result {
-        final BaseLineBreaker.Result mResult;
-        public Result(BaseLineBreaker.Result result) {
-            mResult = result;
-        }
-    }
-}
diff --git a/bridge/src/android/graphics/text/LineWidth.java b/bridge/src/android/graphics/text/LineWidth.java
deleted file mode 100644
index 809b967..0000000
--- a/bridge/src/android/graphics/text/LineWidth.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2018 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.text;
-
-// Based on the native implementation of LineWidth in
-// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
-public class LineWidth {
-    private final float mFirstWidth;
-    private final int mFirstWidthLineCount;
-    private float mRestWidth;
-
-    public LineWidth(float firstWidth, int firstWidthLineCount, float restWidth) {
-        mFirstWidth = firstWidth;
-        mFirstWidthLineCount = firstWidthLineCount;
-        mRestWidth = restWidth;
-    }
-
-    public float getLineWidth(int line) {
-        return (line < mFirstWidthLineCount) ? mFirstWidth : mRestWidth;
-    }
-}
diff --git a/bridge/src/android/graphics/text/MeasuredText_Builder_Delegate.java b/bridge/src/android/graphics/text/MeasuredText_Builder_Delegate.java
deleted file mode 100644
index f9abbe1..0000000
--- a/bridge/src/android/graphics/text/MeasuredText_Builder_Delegate.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2018 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.text;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.graphics.BidiRenderer;
-import android.graphics.Paint;
-import android.graphics.Paint_Delegate;
-import android.graphics.RectF;
-import android.graphics.text.LineBreaker_Delegate.Builder;
-import android.graphics.text.LineBreaker_Delegate.Run;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-/**
- * Delegate that provides implementation for native methods in
- * {@link android.graphics.text.MeasuredText}
- * <p/>
- * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- */
-public class MeasuredText_Builder_Delegate {
-    // ---- Builder delegate manager ----
-    protected static final DelegateManager<MeasuredText_Builder_Delegate>
-            sBuilderManager =
-            new DelegateManager<>(MeasuredText_Builder_Delegate.class);
-
-    protected final ArrayList<Run> mRuns = new ArrayList<>();
-
-    @LayoutlibDelegate
-    /*package*/ static long nInitBuilder() {
-        return sBuilderManager.addNewDelegate(new MeasuredText_Builder_Delegate());
-    }
-
-    /**
-     * Apply style to make native measured text.
-     *
-     * @param nativeBuilderPtr The native NativeMeasuredParagraph builder pointer.
-     * @param paintPtr The native paint pointer to be applied.
-     * @param start The start offset in the copied buffer.
-     * @param end The end offset in the copied buffer.
-     * @param isRtl True if the text is RTL.
-     */
-    @LayoutlibDelegate
-    /*package*/ static void nAddStyleRun(long nativeBuilderPtr, long paintPtr, int start,
-            int end, boolean isRtl) {
-        MeasuredText_Builder_Delegate builder = sBuilderManager.getDelegate(nativeBuilderPtr);
-        if (builder == null) {
-            return;
-        }
-        builder.mRuns.add(new StyleRun(paintPtr, start, end, isRtl));
-    }
-
-    /**
-     * Apply ReplacementRun to make native measured text.
-     *
-     * @param nativeBuilderPtr The native NativeMeasuredParagraph builder pointer.
-     * @param paintPtr The native paint pointer to be applied.
-     * @param start The start offset in the copied buffer.
-     * @param end The end offset in the copied buffer.
-     * @param width The width of the replacement.
-     */
-    @LayoutlibDelegate
-    /*package*/ static void nAddReplacementRun(long nativeBuilderPtr, long paintPtr, int start,
-            int end, float width) {
-        MeasuredText_Builder_Delegate builder = sBuilderManager.getDelegate(nativeBuilderPtr);
-        if (builder == null) {
-            return;
-        }
-        builder.mRuns.add(new ReplacementRun(start, end, width));
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nBuildMeasuredText(long nativeBuilderPtr, long hintMtPtr,
-            @NonNull char[] text, boolean computeHyphenation, boolean computeLayout) {
-        MeasuredText_Delegate delegate = new MeasuredText_Delegate();
-        delegate.mNativeBuilderPtr = nativeBuilderPtr;
-        return MeasuredText_Delegate.sManager.addNewDelegate(delegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nFreeBuilder(long nativeBuilderPtr) {
-        sBuilderManager.removeJavaReferenceFor(nativeBuilderPtr);
-    }
-
-    private static float measureText(long nativePaint, char[] text, int index, int count,
-            float[] widths, int bidiFlags) {
-        Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint);
-        RectF bounds =
-                new BidiRenderer(null, paint, text).renderText(index, index + count, bidiFlags,
-                        widths, 0, false);
-        return bounds.right - bounds.left;
-    }
-
-    private static class StyleRun extends Run {
-        private final long mNativePaint;
-        private final boolean mIsRtl;
-
-        private StyleRun(long nativePaint, int start, int end, boolean isRtl) {
-            super(start, end);
-            mNativePaint = nativePaint;
-            mIsRtl = isRtl;
-        }
-
-        @Override
-        void addTo(Builder builder) {
-            int bidiFlags = mIsRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR;
-            measureText(mNativePaint, builder.mText, mStart, mEnd - mStart, builder.mWidths,
-                    bidiFlags);
-        }
-    }
-
-    private static class ReplacementRun extends Run {
-        private final float mWidth;
-
-        private ReplacementRun(int start, int end, float width) {
-            super(start, end);
-            mWidth = width;
-        }
-
-        @Override
-        void addTo(Builder builder) {
-            builder.mWidths[mStart] = mWidth;
-            Arrays.fill(builder.mWidths, mStart + 1, mEnd, 0.0f);
-        }
-    }
-}
diff --git a/bridge/src/android/graphics/text/MeasuredText_Delegate.java b/bridge/src/android/graphics/text/MeasuredText_Delegate.java
deleted file mode 100644
index 02e2845..0000000
--- a/bridge/src/android/graphics/text/MeasuredText_Delegate.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2018 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.text;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.Rect;
-import android.graphics.text.LineBreaker_Delegate.Builder;
-import android.graphics.text.LineBreaker_Delegate.Run;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate that provides implementation for native methods in
- * {@link android.graphics.text.MeasuredText}
- * <p/>
- * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- */
-public class MeasuredText_Delegate {
-
-    // ---- Builder delegate manager ----
-    protected static final DelegateManager<MeasuredText_Delegate> sManager =
-            new DelegateManager<>(MeasuredText_Delegate.class);
-    private static long sFinalizer = -1;
-
-    protected long mNativeBuilderPtr;
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetWidth(long nativePtr, int start, int end) {
-        // Ignore as it is not used for the layoutlib implementation
-        return 0.0f;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nGetReleaseFunc() {
-        synchronized (MeasuredText_Delegate.class) {
-            if (sFinalizer == -1) {
-                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
-                        sManager::removeJavaReferenceFor);
-            }
-        }
-        return sFinalizer;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetMemoryUsage(long nativePtr) {
-        // Ignore as it is not used for the layoutlib implementation
-        return 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nGetBounds(long nativePtr, char[] buf, int start, int end, Rect rect) {
-        // Ignore as it is not used for the layoutlib implementation
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static float nGetCharWidthAt(long nativePtr, int offset) {
-        // Ignore as it is not used for the layoutlib implementation
-        return 0.0f;
-    }
-
-    public static void computeRuns(long measuredTextPtr, Builder staticLayoutBuilder) {
-        MeasuredText_Delegate delegate = sManager.getDelegate(measuredTextPtr);
-        if (delegate == null) {
-            return;
-        }
-        MeasuredText_Builder_Delegate builder =
-                MeasuredText_Builder_Delegate.sBuilderManager.getDelegate(delegate.mNativeBuilderPtr);
-        if (builder == null) {
-            return;
-        }
-        for (Run run: builder.mRuns) {
-            run.addTo(staticLayoutBuilder);
-        }
-    }
-}
\ No newline at end of file
diff --git a/bridge/src/android/graphics/text/OptimizingLineBreaker.java b/bridge/src/android/graphics/text/OptimizingLineBreaker.java
deleted file mode 100644
index 95e0920..0000000
--- a/bridge/src/android/graphics/text/OptimizingLineBreaker.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright (C) 2018 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.text;
-
-import android.annotation.NonNull;
-import android.graphics.text.Primitive.PrimitiveType;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.ListIterator;
-
-import static android.graphics.text.Primitive.PrimitiveType.PENALTY_INFINITY;
-
-
-// Based on the native implementation of OptimizingLineBreaker in
-// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
-/**
- * A more complex version of line breaking where we try to prevent the right edge from being too
- * jagged.
- */
-public class OptimizingLineBreaker extends BaseLineBreaker {
-
-    public OptimizingLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth,
-            @NonNull TabStops tabStops) {
-        super(primitives, lineWidth, tabStops);
-    }
-
-    @Override
-    public Result computeBreaks() {
-        Result result = new Result();
-        int numBreaks = mPrimitives.size();
-        assert numBreaks > 0;
-        if (numBreaks == 1) {
-            // This can be true only if it's an empty paragraph.
-            Primitive p = mPrimitives.get(0);
-            assert p.type == PrimitiveType.PENALTY;
-            result.mLineBreakOffset.add(0);
-            result.mLineWidths.add(p.width);
-            result.mLineAscents.add(0f);
-            result.mLineDescents.add(0f);
-            result.mLineFlags.add(0);
-            return result;
-        }
-        Node[] opt = new Node[numBreaks];
-        opt[0] = new Node(-1, 0, 0, 0, false);
-        opt[numBreaks - 1] = new Node(-1, 0, 0, 0, false);
-
-        ArrayList<Integer> active = new ArrayList<Integer>();
-        active.add(0);
-        int lastBreak = 0;
-        for (int i = 0; i < numBreaks; i++) {
-            Primitive p = mPrimitives.get(i);
-            if (p.type == PrimitiveType.PENALTY) {
-                boolean finalBreak = (i + 1 == numBreaks);
-                Node bestBreak = null;
-
-                for (ListIterator<Integer> it = active.listIterator(); it.hasNext();
-                        /* incrementing done in loop */) {
-                    int pos = it.next();
-                    int lines = opt[pos].mPrevCount;
-                    float maxWidth = mLineWidth.getLineWidth(lines);
-                    // we have to compute metrics every time --
-                    // we can't really pre-compute this stuff and just deal with breaks
-                    // because of the way tab characters work, this makes it computationally
-                    // harder, but this way, we can still optimize while treating tab characters
-                    // correctly
-                    LineMetrics lineMetrics = computeMetrics(pos, i);
-                    if (lineMetrics.mPrintedWidth <= maxWidth) {
-                        float demerits = computeDemerits(maxWidth, lineMetrics.mPrintedWidth,
-                                finalBreak, p.penalty) + opt[pos].mDemerits;
-                        if (bestBreak == null || demerits < bestBreak.mDemerits) {
-                            if (bestBreak == null) {
-                                bestBreak = new Node(pos, opt[pos].mPrevCount + 1, demerits,
-                                        lineMetrics.mPrintedWidth, lineMetrics.mHasTabs);
-                            } else {
-                                bestBreak.mPrev = pos;
-                                bestBreak.mPrevCount = opt[pos].mPrevCount + 1;
-                                bestBreak.mDemerits = demerits;
-                                bestBreak.mWidth = lineMetrics.mPrintedWidth;
-                                bestBreak.mHasTabs = lineMetrics.mHasTabs;
-                            }
-                        }
-                    } else {
-                        it.remove();
-                    }
-                }
-                if (p.penalty == -PENALTY_INFINITY) {
-                    active.clear();
-                }
-                if (bestBreak != null) {
-                    opt[i] = bestBreak;
-                    active.add(i);
-                    lastBreak = i;
-                }
-                if (active.isEmpty()) {
-                    // we can't give up!
-                    LineMetrics lineMetrics = new LineMetrics();
-                    int lines = opt[lastBreak].mPrevCount;
-                    float maxWidth = mLineWidth.getLineWidth(lines);
-                    int breakIndex = desperateBreak(lastBreak, numBreaks, maxWidth, lineMetrics);
-                    opt[breakIndex] = new Node(lastBreak, lines + 1, 0 /*doesn't matter*/,
-                            lineMetrics.mWidth, lineMetrics.mHasTabs);
-                    active.add(breakIndex);
-                    lastBreak = breakIndex;
-                    i = breakIndex; // incremented by i++
-                }
-            }
-        }
-
-        int idx = numBreaks - 1;
-        while (opt[idx].mPrev != -1) {
-            result.mLineBreakOffset.add(mPrimitives.get(idx).location);
-            result.mLineWidths.add(opt[idx].mWidth);
-            result.mLineAscents.add(0f);
-            result.mLineDescents.add(0f);
-            result.mLineFlags.add(opt[idx].mHasTabs ? TAB_MASK : 0);
-            idx = opt[idx].mPrev;
-        }
-
-        Collections.reverse(result.mLineBreakOffset);
-        Collections.reverse(result.mLineWidths);
-        Collections.reverse(result.mLineAscents);
-        Collections.reverse(result.mLineDescents);
-        Collections.reverse(result.mLineFlags);
-        return result;
-    }
-
-    @NonNull
-    private LineMetrics computeMetrics(int start, int end) {
-        boolean f = false;
-        float w = 0, pw = 0;
-        for (int i = start; i < end; i++) {
-            Primitive p = mPrimitives.get(i);
-            if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) {
-                w += p.width;
-                if (p.type == PrimitiveType.BOX) {
-                    pw = w;
-                }
-            } else if (p.type == PrimitiveType.VARIABLE) {
-                w = mTabStops.width(w);
-                f = true;
-            }
-        }
-        return new LineMetrics(w, pw, f);
-    }
-
-    private static float computeDemerits(float maxWidth, float width, boolean finalBreak,
-            float penalty) {
-        float deviation = finalBreak ? 0 : maxWidth - width;
-        return (deviation * deviation) + penalty;
-    }
-
-    /**
-     * @return the last break position or -1 if failed.
-     */
-    @SuppressWarnings("ConstantConditions")  // method too complex to be analyzed.
-    private int desperateBreak(int start, int limit, float maxWidth,
-            @NonNull LineMetrics lineMetrics) {
-        float w = 0, pw = 0;
-        boolean breakFound = false;
-        int breakIndex = 0, firstTabIndex = Integer.MAX_VALUE;
-        for (int i = start; i < limit; i++) {
-            Primitive p = mPrimitives.get(i);
-
-            if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) {
-                w += p.width;
-                if (p.type == PrimitiveType.BOX) {
-                    pw = w;
-                }
-            } else if (p.type == PrimitiveType.VARIABLE) {
-                w = mTabStops.width(w);
-                firstTabIndex = Math.min(firstTabIndex, i);
-            }
-
-            if (pw > maxWidth && breakFound) {
-                break;
-            }
-
-            // must make progress
-            if (i > start &&
-                    (p.type == PrimitiveType.PENALTY || p.type == PrimitiveType.WORD_BREAK)) {
-                breakFound = true;
-                breakIndex = i;
-            }
-        }
-
-        if (breakFound) {
-            lineMetrics.mWidth = w;
-            lineMetrics.mPrintedWidth = pw;
-            lineMetrics.mHasTabs = (start <= firstTabIndex && firstTabIndex < breakIndex);
-            return breakIndex;
-        } else {
-            return -1;
-        }
-    }
-
-    private static class LineMetrics {
-        /** Actual width of the line. */
-        float mWidth;
-        /** Width of the line minus trailing whitespace. */
-        float mPrintedWidth;
-        boolean mHasTabs;
-
-        public LineMetrics() {
-        }
-
-        public LineMetrics(float width, float printedWidth, boolean hasTabs) {
-            mWidth = width;
-            mPrintedWidth = printedWidth;
-            mHasTabs = hasTabs;
-        }
-    }
-
-    /**
-     * A struct to store the info about a break.
-     */
-    @SuppressWarnings("SpellCheckingInspection")  // For the word struct.
-    private static class Node {
-        // -1 for the first node.
-        int mPrev;
-        // number of breaks so far.
-        int mPrevCount;
-        float mDemerits;
-        float mWidth;
-        boolean mHasTabs;
-
-        public Node(int prev, int prevCount, float demerits, float width, boolean hasTabs) {
-            mPrev = prev;
-            mPrevCount = prevCount;
-            mDemerits = demerits;
-            mWidth = width;
-            mHasTabs = hasTabs;
-        }
-    }
-}
diff --git a/bridge/src/android/graphics/text/Primitive.java b/bridge/src/android/graphics/text/Primitive.java
deleted file mode 100644
index b8157ec..0000000
--- a/bridge/src/android/graphics/text/Primitive.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2018 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.text;
-
-import android.annotation.NonNull;
-
-// Based on the native implementation of Primitive in
-// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
-public class Primitive {
-    public final @NonNull PrimitiveType type;
-    public final int location;
-    // The following fields don't make sense for all types.
-    // Box and Glue have width only.
-    // Penalty has both width and penalty.
-    // Word_break has penalty only.
-    public final float width;
-    public final float penalty;
-
-    /**
-     * Use {@code PrimitiveType#getNewPrimitive()}
-     */
-    private Primitive(@NonNull PrimitiveType type, int location, float width, float penalty) {
-        this.type = type;
-        this.location = location;
-        this.width = width;
-        this.penalty = penalty;
-    }
-
-    public static enum PrimitiveType {
-        /**
-         * Something with a constant width that is to be typeset - like a character.
-         */
-        BOX,
-        /**
-         * Blank space with fixed width.
-         */
-        GLUE,
-        /**
-         * Aesthetic cost indicating how desirable breaking at this point will be. A penalty of
-         * {@link #PENALTY_INFINITY} means a forced non-break, whereas a penalty of negative
-         * {@code #PENALTY_INFINITY} means a forced break.
-         * <p/>
-         * Currently, it only stores penalty with values 0 or -infinity.
-         */
-        PENALTY,
-        /**
-         * For tabs - variable width space.
-         */
-        VARIABLE,
-        /**
-         * Possible breakpoints within a word. Think of this as a high cost {@link #PENALTY}.
-         */
-        WORD_BREAK;
-
-        public Primitive getNewPrimitive(int location) {
-            assert this == VARIABLE;
-            return new Primitive(this, location, 0f, 0f);
-        }
-
-        public Primitive getNewPrimitive(int location, float value) {
-            assert this == BOX || this == GLUE || this == WORD_BREAK;
-            if (this == BOX || this == GLUE) {
-                return new Primitive(this, location, value, 0f);
-            } else {
-                return new Primitive(this, location, 0f, value);
-            }
-        }
-
-        public Primitive getNewPrimitive(int location, float width, float penalty) {
-            assert this == PENALTY;
-            return new Primitive(this, location, width, penalty);
-        }
-
-        // forced non-break, negative infinity is forced break.
-        public static final float PENALTY_INFINITY = 1e7f;
-    }
-}
-
diff --git a/bridge/src/android/graphics/text/TabStops.java b/bridge/src/android/graphics/text/TabStops.java
deleted file mode 100644
index 6ede24c..0000000
--- a/bridge/src/android/graphics/text/TabStops.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2018 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.text;
-
-import android.annotation.Nullable;
-
-// Based on the native implementation of TabStops in
-// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
-public class TabStops {
-    @Nullable
-    private float[] mStops;
-    private final float mTabWidth;
-
-    public TabStops(@Nullable float[] stops, float defaultTabWidth) {
-        mTabWidth = defaultTabWidth;
-        mStops = stops;
-    }
-
-    public float width(float widthSoFar) {
-        if (mStops != null) {
-            for (float f : mStops) {
-                if (f > widthSoFar) {
-                    return f;
-                }
-            }
-        }
-        // find the next tabStop after widthSoFar.
-        return ((widthSoFar + mTabWidth) / mTabWidth) * mTabWidth;
-    }
-}
diff --git a/bridge/src/android/hardware/display/DisplayManagerGlobal.java b/bridge/src/android/hardware/display/DisplayManagerGlobal.java
new file mode 100644
index 0000000..f58467f
--- /dev/null
+++ b/bridge/src/android/hardware/display/DisplayManagerGlobal.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2021 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.hardware.display;
+
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.ColorSpace;
+import android.graphics.Point;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.media.projection.MediaProjection;
+import android.os.Handler;
+import android.util.Pair;
+import android.view.Display;
+import android.view.DisplayAdjustments;
+import android.view.DisplayInfo;
+import android.view.Surface;
+
+import java.util.List;
+
+public final class DisplayManagerGlobal {
+    public static final int EVENT_DISPLAY_ADDED = 1;
+    public static final int EVENT_DISPLAY_CHANGED = 2;
+    public static final int EVENT_DISPLAY_REMOVED = 3;
+    public static final int EVENT_DISPLAY_BRIGHTNESS_CHANGED = 4;
+
+    private static DisplayManagerGlobal sInstance;
+
+    @VisibleForTesting
+    public DisplayManagerGlobal(IDisplayManager dm) {}
+
+    public static DisplayManagerGlobal getInstance() {
+        synchronized (DisplayManagerGlobal.class) {
+            if (sInstance == null) {
+                sInstance = new DisplayManagerGlobal(null);
+            }
+            return sInstance;
+        }
+    }
+
+    public DisplayInfo getDisplayInfo(int displayId) {
+        return null;
+    }
+
+    public int[] getDisplayIds() {
+        return null;
+    }
+
+    public boolean isUidPresentOnDisplay(int uid, int displayId) {
+        return false;
+    }
+
+    public Display getCompatibleDisplay(int displayId, DisplayAdjustments daj) {
+        return null;
+    }
+
+    public Display getCompatibleDisplay(int displayId, Resources resources) {
+        return null;
+    }
+
+    public Display getRealDisplay(int displayId) {
+        return null;
+    }
+
+    public void registerDisplayListener(@NonNull DisplayListener listener,
+            @Nullable Handler handler, long eventsMask) {}
+
+    public void unregisterDisplayListener(DisplayListener listener) {}
+
+    public void startWifiDisplayScan() {}
+
+    public void stopWifiDisplayScan() {}
+
+    public void connectWifiDisplay(String deviceAddress) {}
+
+    public void pauseWifiDisplay() {}
+
+    public void resumeWifiDisplay() {}
+
+    public void disconnectWifiDisplay() {}
+
+    public void renameWifiDisplay(String deviceAddress, String alias) {}
+
+    public void forgetWifiDisplay(String deviceAddress) {}
+
+    public WifiDisplayStatus getWifiDisplayStatus() {
+        return null;
+    }
+
+    public void setUserDisabledHdrTypes(int[] userDisabledHdrTypes) {}
+
+    public void setAreUserDisabledHdrTypesAllowed(boolean areUserDisabledHdrTypesAllowed) {}
+
+    public boolean areUserDisabledHdrTypesAllowed() {
+        return false;
+    }
+
+    public int[] getUserDisabledHdrTypes() {
+        return null;
+    }
+
+    public void requestColorMode(int displayId, int colorMode) {}
+
+    public VirtualDisplay createVirtualDisplay(@NonNull Context context, MediaProjection projection,
+            @NonNull VirtualDisplayConfig virtualDisplayConfig, VirtualDisplay.Callback callback,
+            Handler handler) {
+        return null;
+    }
+
+    public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {}
+
+    public void resizeVirtualDisplay(IVirtualDisplayCallback token,
+            int width, int height, int densityDpi) {}
+
+    public void releaseVirtualDisplay(IVirtualDisplayCallback token) {}
+
+    void setVirtualDisplayState(IVirtualDisplayCallback token, boolean isOn) {}
+
+    public Point getStableDisplaySize() {
+        return null;
+    }
+
+    public List<BrightnessChangeEvent> getBrightnessEvents(String callingPackage) {
+        return null;
+    }
+
+    public BrightnessInfo getBrightnessInfo(int displayId) {
+        return null;
+    }
+
+    public ColorSpace getPreferredWideGamutColorSpace() {
+        return null;
+    }
+
+    public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId,
+            String packageName) {}
+
+    public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
+        return null;
+    }
+
+    public BrightnessConfiguration getDefaultBrightnessConfiguration() {
+        return null;
+    }
+
+    public boolean isMinimalPostProcessingRequested(int displayId) {
+        return false;
+    }
+
+    public void setTemporaryBrightness(int displayId, float brightness) {}
+
+    public void setBrightness(int displayId, float brightness) {}
+
+    public float getBrightness(int displayId) {
+        return 0.0f;
+    }
+
+    public void setTemporaryAutoBrightnessAdjustment(float adjustment) {}
+
+    public Pair<float[], float[]> getMinimumBrightnessCurve() {
+        return null;
+    }
+
+    public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
+        return null;
+    }
+
+    public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {}
+
+    public boolean shouldAlwaysRespectAppRequestedMode() {
+        return false;
+    }
+
+    public void setRefreshRateSwitchingType(int newValue) {}
+
+    public int getRefreshRateSwitchingType() {
+        return 0;
+    }
+
+    public static final String CACHE_KEY_DISPLAY_INFO_PROPERTY =
+            "cache_key.display_info";
+
+    public static void invalidateLocalDisplayInfoCaches() {}
+
+    public void disableLocalDisplayInfoCaches() {}
+}
diff --git a/bridge/src/android/media/AudioManager.java b/bridge/src/android/media/AudioManager.java
new file mode 100644
index 0000000..a64547c
--- /dev/null
+++ b/bridge/src/android/media/AudioManager.java
@@ -0,0 +1,616 @@
+/*
+ * Copyright (C) 2021 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.media;
+
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothDevice;
+import android.content.ComponentName;
+import android.content.Context;
+import android.media.audiopolicy.AudioPolicy;
+import android.media.audiopolicy.AudioProductStrategy;
+import android.os.Handler;
+import android.view.KeyEvent;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+public class AudioManager {
+    public AudioManager() {}
+
+    public AudioManager(Context context) { }
+
+    public void dispatchMediaKeyEvent(KeyEvent keyEvent) { }
+
+    public void preDispatchKeyEvent(KeyEvent event, int stream) { }
+
+    public boolean isVolumeFixed() {
+        return false;
+    }
+
+    public void adjustStreamVolume(int streamType, int direction, int flags) { }
+
+    public void adjustVolume(int direction, int flags) { }
+
+    public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) { }
+
+    public void setMasterMute(boolean mute, int flags) { }
+
+    public int getRingerMode() {
+        return 0;
+    }
+
+    public int getStreamMaxVolume(int streamType) {
+        return 0;
+    }
+
+    public int getStreamMinVolume(int streamType) {
+        return 0;
+    }
+
+    public int getStreamMinVolumeInt(int streamType) {
+        return 0;
+    }
+
+    public int getStreamVolume(int streamType) {
+        return 0;
+    }
+
+    public float getStreamVolumeDb(int streamType, int index, int deviceType) {
+        return 0;
+    }
+
+    public int getLastAudibleStreamVolume(int streamType) {
+        return 0;
+    }
+
+    public int getUiSoundsStreamType() {
+        return 0;
+    }
+
+    public void setRingerMode(int ringerMode) { }
+
+    public void setStreamVolume(int streamType, int index, int flags) { }
+
+    public void setVolumeIndexForAttributes(AudioAttributes attr, int index, int flags) { }
+
+    public int getVolumeIndexForAttributes(AudioAttributes attr) {
+        return 0;
+    }
+
+    public int getMaxVolumeIndexForAttributes(AudioAttributes attr) {
+        return 0;
+    }
+
+    public int getMinVolumeIndexForAttributes(AudioAttributes attr) {
+        return 0;
+    }
+
+    public void setSupportedSystemUsages(int[] systemUsages) { }
+
+    public int[] getSupportedSystemUsages() {
+        return null;
+    }
+
+    public void setStreamSolo(int streamType, boolean state) { }
+
+    public void setStreamMute(int streamType, boolean state) { }
+
+    public boolean isStreamMute(int streamType) {
+        return false;
+    }
+
+    public boolean isMasterMute() {
+        return false;
+    }
+
+    public void forceVolumeControlStream(int streamType) { }
+
+    public boolean shouldVibrate(int vibrateType) {
+        return false;
+    }
+
+    public int getVibrateSetting(int vibrateType) {
+        return 0;
+    }
+
+    public void setVibrateSetting(int vibrateType, int vibrateSetting) { }
+
+    public void setSpeakerphoneOn(boolean on) { }
+
+    public boolean isSpeakerphoneOn() {
+        return false;
+    }
+
+    public void setAllowedCapturePolicy(int capturePolicy) { }
+
+    public int getAllowedCapturePolicy() {
+        return 0;
+    }
+
+    public boolean setPreferredDeviceForStrategy(AudioProductStrategy strategy, AudioDeviceAttributes device) {
+        return false;
+    }
+
+    public boolean removePreferredDeviceForStrategy(AudioProductStrategy strategy) {
+        return false;
+    }
+
+    public AudioDeviceAttributes getPreferredDeviceForStrategy(AudioProductStrategy strategy) {
+        return null;
+    }
+
+    public boolean setPreferredDevicesForStrategy(AudioProductStrategy strategy, List<AudioDeviceAttributes> devices) {
+        return false;
+    }
+
+    public List<AudioDeviceAttributes> getPreferredDevicesForStrategy(AudioProductStrategy strategy) {
+        return null;
+    }
+
+    public void addOnPreferredDeviceForStrategyChangedListener(Executor executor, AudioManager.OnPreferredDeviceForStrategyChangedListener listener) throws SecurityException { }
+
+    public void removeOnPreferredDeviceForStrategyChangedListener(AudioManager.OnPreferredDeviceForStrategyChangedListener listener) { }
+
+    public void addOnPreferredDevicesForStrategyChangedListener(Executor executor,
+            AudioManager.OnPreferredDevicesForStrategyChangedListener listener) throws SecurityException { }
+
+    public void removeOnPreferredDevicesForStrategyChangedListener(AudioManager.OnPreferredDevicesForStrategyChangedListener listener) { }
+
+    public boolean setPreferredDeviceForCapturePreset(int capturePreset, AudioDeviceAttributes device) {
+        return false;
+    }
+
+    public boolean clearPreferredDevicesForCapturePreset(int capturePreset) {
+        return false;
+    }
+
+    public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int capturePreset) {
+        return null;
+    }
+
+    public void addOnPreferredDevicesForCapturePresetChangedListener(Executor executor,
+            AudioManager.OnPreferredDevicesForCapturePresetChangedListener listener) throws SecurityException { }
+
+    public void removeOnPreferredDevicesForCapturePresetChangedListener(AudioManager.OnPreferredDevicesForCapturePresetChangedListener listener) { }
+
+    public boolean isBluetoothScoAvailableOffCall() {
+        return false;
+    }
+
+    public void startBluetoothSco() { }
+
+    public void startBluetoothScoVirtualCall() { }
+
+    public void stopBluetoothSco() { }
+
+    public void setBluetoothScoOn(boolean on) { }
+
+    public boolean isBluetoothScoOn() {
+        return false;
+    }
+
+    public void setBluetoothA2dpOn(boolean on) { }
+
+    public boolean isBluetoothA2dpOn() {
+        return false;
+    }
+
+    public void setWiredHeadsetOn(boolean on) { }
+
+    public boolean isWiredHeadsetOn() {
+        return false;
+    }
+
+    public void setMicrophoneMute(boolean on) { }
+
+    public void setMicrophoneMuteFromSwitch(boolean on) { }
+
+    public boolean isMicrophoneMute() {
+        return false;
+    }
+
+    public void setMode(int mode) { }
+
+    public int getMode() {
+        return 0;
+    }
+
+    public boolean isCallScreeningModeSupported() {
+        return false;
+    }
+
+    public void setRouting(int mode, int routes, int mask) { }
+
+    public int getRouting(int mode) {
+        return 0;
+    }
+
+    public boolean isMusicActive() {
+        return false;
+    }
+
+    public boolean isMusicActiveRemotely() {
+        return false;
+    }
+
+    public boolean isAudioFocusExclusive() {
+        return false;
+    }
+
+    public int generateAudioSessionId() {
+        return 0;
+    }
+
+    public void setParameter(String key, String value) { }
+
+    public void setParameters(String keyValuePairs) { }
+
+    public String getParameters(String keys) {
+        return null;
+    }
+
+    public void setNavigationRepeatSoundEffectsEnabled(boolean enabled) { }
+
+    public boolean areNavigationRepeatSoundEffectsEnabled() {
+        return false;
+    }
+
+    public void setHomeSoundEffectEnabled(boolean enabled) { }
+
+    public boolean isHomeSoundEffectEnabled() {
+        return false;
+    }
+
+    public void playSoundEffect(int effectType) { }
+
+    public void playSoundEffect(int effectType, int userId) { }
+
+    public void playSoundEffect(int effectType, float volume) { }
+
+    public void loadSoundEffects() { }
+
+    public void unloadSoundEffects() { }
+
+    public void registerAudioFocusRequest(AudioFocusRequest afr) { }
+
+    public void unregisterAudioFocusRequest(AudioManager.OnAudioFocusChangeListener l) { }
+
+    public int requestAudioFocus(AudioManager.OnAudioFocusChangeListener l, int streamType, int durationHint) {
+        return 0;
+    }
+
+    public int requestAudioFocus(AudioFocusRequest focusRequest) {
+        return 0;
+    }
+
+    public int abandonAudioFocusRequest(AudioFocusRequest focusRequest) {
+        return 0;
+    }
+
+    public int requestAudioFocus(AudioManager.OnAudioFocusChangeListener l, AudioAttributes requestAttributes, int durationHint, int flags) throws IllegalArgumentException {
+        return 0;
+    }
+
+    public int requestAudioFocus(AudioManager.OnAudioFocusChangeListener l, AudioAttributes requestAttributes, int durationHint, int flags, AudioPolicy ap) throws IllegalArgumentException {
+        return 0;
+    }
+
+    public int requestAudioFocus(AudioFocusRequest afr, AudioPolicy ap) {
+        return 0;
+    }
+
+    public void requestAudioFocusForCall(int streamType, int durationHint) { }
+
+    public int getFocusRampTimeMs(int focusGain, AudioAttributes attr) {
+        return 0;
+    }
+
+    public void setFocusRequestResult(AudioFocusInfo afi, int requestResult, AudioPolicy ap) { }
+
+    public int dispatchAudioFocusChange(AudioFocusInfo afi, int focusChange, AudioPolicy ap) {
+        return 0;
+    }
+
+    public void abandonAudioFocusForCall() { }
+
+    public int abandonAudioFocus(AudioManager.OnAudioFocusChangeListener l) {
+        return 0;
+    }
+
+    public int abandonAudioFocus(AudioManager.OnAudioFocusChangeListener l, AudioAttributes aa) {
+        return 0;
+    }
+
+    public void registerMediaButtonEventReceiver(ComponentName eventReceiver) { }
+
+    public void registerMediaButtonEventReceiver(PendingIntent eventReceiver) { }
+
+    public void registerMediaButtonIntent(PendingIntent pi, ComponentName eventReceiver) { }
+
+    public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) { }
+
+    public void unregisterMediaButtonEventReceiver(PendingIntent eventReceiver) { }
+
+    public void unregisterMediaButtonIntent(PendingIntent pi) { }
+
+    public void registerRemoteControlClient(RemoteControlClient rcClient) { }
+
+    public void unregisterRemoteControlClient(RemoteControlClient rcClient) { }
+
+    public boolean registerRemoteController(RemoteController rctlr) {
+        return false;
+    }
+
+    public void unregisterRemoteController(RemoteController rctlr) { }
+
+    public int registerAudioPolicy(AudioPolicy policy) {
+        return 0;
+    }
+
+    public void unregisterAudioPolicyAsync(AudioPolicy policy) { }
+
+    public void unregisterAudioPolicy(AudioPolicy policy) { }
+
+    public boolean hasRegisteredDynamicPolicy() {
+        return false;
+    }
+
+    public void registerAudioPlaybackCallback(AudioManager.AudioPlaybackCallback cb,
+            Handler handler) { }
+
+    public void unregisterAudioPlaybackCallback(AudioManager.AudioPlaybackCallback cb) { }
+
+    public List<AudioPlaybackConfiguration> getActivePlaybackConfigurations() {
+        return null;
+    }
+
+    public void registerAudioRecordingCallback(AudioManager.AudioRecordingCallback cb,
+            Handler handler) { }
+
+    public void unregisterAudioRecordingCallback(AudioManager.AudioRecordingCallback cb) { }
+
+    public List<AudioRecordingConfiguration> getActiveRecordingConfigurations() {
+        return null;
+    }
+
+    public void reloadAudioSettings() { }
+
+    public void avrcpSupportsAbsoluteVolume(String address, boolean support) { }
+
+    public boolean isSilentMode() {
+        return false;
+    }
+
+    public int getDevicesForStream(int streamType) {
+        return 0;
+    }
+
+    public List<AudioDeviceAttributes> getDevicesForAttributes(AudioAttributes attributes) {
+        return null;
+    }
+
+    public void setDeviceVolumeBehavior(AudioDeviceAttributes device, int deviceVolumeBehavior) { }
+
+    public int getDeviceVolumeBehavior(AudioDeviceAttributes device) {
+        return 0;
+    }
+
+    public void setWiredDeviceConnectionState(int type, int state, String address, String name) { }
+
+    public void setBluetoothHearingAidDeviceConnectionState(BluetoothDevice device, int state,
+            boolean suppressNoisyIntent, int musicDevice) { }
+
+    public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice device,
+            int state, int profile, boolean suppressNoisyIntent, int a2dpVolume) { }
+
+    public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device) { }
+
+    public IRingtonePlayer getRingtonePlayer() {
+        return null;
+    }
+
+    public String getProperty(String key) {
+        return null;
+    }
+
+    public boolean setAdditionalOutputDeviceDelay(AudioDeviceInfo device, long delayMillis) {
+        return false;
+    }
+
+    public long getAdditionalOutputDeviceDelay(AudioDeviceInfo device) {
+        return 0;
+    }
+
+    public long getMaxAdditionalOutputDeviceDelay(AudioDeviceInfo device) {
+        return 0;
+    }
+
+    public int getOutputLatency(int streamType) {
+        return 0;
+    }
+
+    public void setVolumeController(IVolumeController controller) { }
+
+    public void notifyVolumeControllerVisible(IVolumeController controller, boolean visible) { }
+
+    public boolean isStreamAffectedByRingerMode(int streamType) {
+        return false;
+    }
+
+    public boolean isStreamAffectedByMute(int streamType) {
+        return false;
+    }
+
+    public void disableSafeMediaVolume() { }
+
+    public void setRingerModeInternal(int ringerMode) { }
+
+    public int getRingerModeInternal() {
+        return 0;
+    }
+
+    public void setVolumePolicy(VolumePolicy policy) { }
+
+    public int setHdmiSystemAudioSupported(boolean on) {
+        return 0;
+    }
+
+    public boolean isHdmiSystemAudioSupported() {
+        return false;
+    }
+
+    public void registerAudioPortUpdateListener(AudioManager.OnAudioPortUpdateListener l) { }
+
+    public void unregisterAudioPortUpdateListener(AudioManager.OnAudioPortUpdateListener l) { }
+
+    public AudioDeviceInfo[] getDevices(int flags) {
+        return null;
+    }
+
+    public void registerAudioDeviceCallback(AudioDeviceCallback callback, Handler handler) { }
+
+    public void unregisterAudioDeviceCallback(AudioDeviceCallback callback) { }
+
+    public List<MicrophoneInfo> getMicrophones() throws IOException {
+        return null;
+    }
+
+    public List<BluetoothCodecConfig> getHwOffloadEncodingFormatsSupportedForA2DP() {
+        return null;
+    }
+
+    public void setAudioServerStateCallback(Executor executor,
+            AudioManager.AudioServerStateCallback stateCallback) { }
+
+    public void clearAudioServerStateCallback() { }
+
+    public boolean isAudioServerRunning() {
+        return false;
+    }
+
+    public Map<Integer, Boolean> getSurroundFormats() {
+        return null;
+    }
+
+    public boolean setSurroundFormatEnabled(int audioFormat, boolean enabled) {
+        return false;
+    }
+
+    public Map<Integer, Boolean> getReportedSurroundFormats() {
+        return null;
+    }
+
+    public void registerVolumeGroupCallback(Executor executor,
+            AudioManager.VolumeGroupCallback callback) { }
+
+    public void unregisterVolumeGroupCallback(AudioManager.VolumeGroupCallback callback) { }
+
+    public void adjustSuggestedStreamVolumeForUid(int suggestedStreamType, int direction,
+            int flags, String packageName, int uid, int pid, int targetSdkVersion) { }
+
+    public void adjustStreamVolumeForUid(int streamType, int direction, int flags,
+            String packageName, int uid, int pid, int targetSdkVersion) { }
+
+    public void setStreamVolumeForUid(int streamType, int index, int flags, String packageName,
+            int uid, int pid, int targetSdkVersion) { }
+
+    public void setMultiAudioFocusEnabled(boolean enabled) { }
+
+    public int getAudioHwSyncForSession(int sessionId) {
+        return 0;
+    }
+
+    public boolean setDeviceForCommunication(AudioDeviceInfo device) {
+        return false;
+    }
+
+    public void clearDeviceForCommunication() { }
+
+    public AudioDeviceInfo getDeviceForCommunication() {
+        return null;
+    }
+
+    public void addOnCommunicationDeviceChangedListener(Executor executor, AudioManager.OnCommunicationDeviceChangedListener listener) { }
+
+    public void removeOnCommunicationDeviceChangedListener(AudioManager.OnCommunicationDeviceChangedListener listener) { }
+
+    public interface OnCommunicationDeviceChangedListener {
+        void onCommunicationDeviceChanged(AudioDeviceInfo var1);
+    }
+
+    public abstract static class VolumeGroupCallback {
+        public VolumeGroupCallback() {
+        }
+
+        public void onAudioVolumeGroupChanged(int group, int flags) {
+        }
+    }
+
+    public abstract static class AudioServerStateCallback {
+        public AudioServerStateCallback() {
+        }
+
+        public void onAudioServerDown() {
+        }
+
+        public void onAudioServerUp() {
+        }
+    }
+
+    public interface OnAudioPortUpdateListener {
+        void onAudioPortListUpdate(AudioPort[] var1);
+
+        void onAudioPatchListUpdate(AudioPatch[] var1);
+
+        void onServiceDied();
+    }
+
+    public abstract static class AudioRecordingCallback {
+        public AudioRecordingCallback() {
+        }
+
+        public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {
+        }
+    }
+
+    public abstract static class AudioPlaybackCallback {
+        public AudioPlaybackCallback() {
+        }
+
+        public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) {
+        }
+    }
+
+    public interface OnAudioFocusChangeListener {
+        void onAudioFocusChange(int var1);
+    }
+
+    public interface OnPreferredDevicesForCapturePresetChangedListener {
+        void onPreferredDevicesForCapturePresetChanged(int var1, List<AudioDeviceAttributes> var2);
+    }
+
+    public interface OnPreferredDevicesForStrategyChangedListener {
+        void onPreferredDevicesForStrategyChanged(AudioProductStrategy var1, List<AudioDeviceAttributes> var2);
+    }
+
+    public interface OnPreferredDeviceForStrategyChangedListener {
+        void onPreferredDeviceForStrategyChanged(AudioProductStrategy var1, AudioDeviceAttributes var2);
+    }
+}
diff --git a/bridge/src/android/os/HandlerThread_Delegate.java b/bridge/src/android/os/HandlerThread_Delegate.java
index afbe97c..18faa5d 100644
--- a/bridge/src/android/os/HandlerThread_Delegate.java
+++ b/bridge/src/android/os/HandlerThread_Delegate.java
@@ -35,7 +35,7 @@
  */
 public class HandlerThread_Delegate {
 
-    private static Map<BridgeContext, List<HandlerThread>> sThreads =
+    private static final Map<BridgeContext, List<HandlerThread>> sThreads =
             new HashMap<BridgeContext, List<HandlerThread>>();
 
     public static void cleanUp(BridgeContext context) {
diff --git a/bridge/src/android/os/Handler_Delegate.java b/bridge/src/android/os/Handler_Delegate.java
index 2152c8a..d8688af 100644
--- a/bridge/src/android/os/Handler_Delegate.java
+++ b/bridge/src/android/os/Handler_Delegate.java
@@ -16,8 +16,14 @@
 
 package android.os;
 
+import com.android.ide.common.rendering.api.ILayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.util.HandlerMessageQueue;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import com.android.tools.layoutlib.annotations.NotNull;
 
+import static com.android.layoutlib.bridge.impl.RenderAction.getCurrentContext;
 
 /**
  * Delegate overriding selected methods of android.os.Handler
@@ -28,20 +34,69 @@
  *
  */
 public class Handler_Delegate {
-
-    // -------- Delegate methods
-
     @LayoutlibDelegate
     /*package*/ static boolean sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
         // get the callback
         IHandlerCallback callback = sCallbacks.get();
         if (callback != null) {
             callback.sendMessageAtTime(handler, msg, uptimeMillis);
+        } else {
+            if (msg.callback != null) {
+                BridgeContext context = getCurrentContext();
+                if (context == null) {
+                    return true;
+                }
+                context.getSessionInteractiveData()
+                        .getHandlerMessageQueue()
+                        .add(handler, uptimeMillis, msg.callback);
+            }
         }
         return true;
     }
 
+    /**
+     * Current implementation of Compose uses {@link Handler#postAtFrontOfQueue} to execute state
+     * updates. We can not intercept postAtFrontOfQueue Compose calls, however we can intecept
+     * internal Handler calls. Since postAtFrontOfQueue is just a wrapper of
+     * sendMessageAtFrontOfQueue we re-define sendMessageAtFrontOfQueue here to catch Compose calls
+     * (we are only interested in them) and execute them.
+     * TODO(b/137794558): Clean/rework this when Compose reworks Handler usage.
+     */
+    @LayoutlibDelegate
+    /*package*/ static boolean sendMessageAtFrontOfQueue(Handler handler, Message msg) {
+        // We will also catch calls from the Choreographer that have no callback.
+        if (msg.callback != null) {
+            BridgeContext context = getCurrentContext();
+            if (context == null) {
+                return true;
+            }
+            context.getSessionInteractiveData()
+                    .getHandlerMessageQueue()
+                    .add(handler, 0, msg.callback);
+        }
+
+        return true;
+    }
+
     // -------- Delegate implementation
+    /**
+     * Executed all the collected callbacks
+     *
+     * @return if there are more callbacks to execute
+     */
+    public static boolean executeCallbacks() {
+        BridgeContext context = getCurrentContext();
+        if (context == null) {
+            return false;
+        }
+        HandlerMessageQueue queue = context.getSessionInteractiveData().getHandlerMessageQueue();
+        long uptimeMillis = SystemClock_Delegate.uptimeMillis();
+        Runnable r;
+        while ((r = queue.extractFirst(uptimeMillis)) != null) {
+            executeSafely(r);
+        }
+        return queue.isNotEmpty();
+    }
 
     public interface IHandlerCallback {
         void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis);
@@ -54,4 +109,18 @@
         sCallbacks.set(callback);
     }
 
+    /**
+     * The runnables we are executing are mostly library/user code and we have no guarantee that it
+     * is safe to execute them. Thus, we have to wrap each executing in try/catch block to isolate
+     * dangerous executions.
+     * @param r a runnable to be executed
+     */
+    private static void executeSafely(@NotNull Runnable r) {
+        try {
+            r.run();
+        } catch (Throwable t) {
+            Bridge.getLog().error(ILayoutLog.TAG_BROKEN, "Failed executing Handler callback", t,
+                    null, null);
+        }
+    }
 }
diff --git a/bridge/src/android/os/SystemClock_Delegate.java b/bridge/src/android/os/SystemClock_Delegate.java
index b6a85f8..21c2fca 100644
--- a/bridge/src/android/os/SystemClock_Delegate.java
+++ b/bridge/src/android/os/SystemClock_Delegate.java
@@ -16,9 +16,9 @@
 
 package android.os;
 
+import com.android.internal.lang.System_Delegate;
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-import com.android.tools.layoutlib.java.System_Delegate;
 
 /**
  * Delegate implementing the native methods of android.os.SystemClock
@@ -39,7 +39,7 @@
      * @return milliseconds of non-sleep uptime since boot.
      */
     @LayoutlibDelegate
-    /*package*/ static long uptimeMillis() {
+    public static long uptimeMillis() {
         return System_Delegate.currentTimeMillis() - System_Delegate.bootTimeMillis();
     }
 
diff --git a/bridge/src/android/os/SystemProperties_Delegate.java b/bridge/src/android/os/SystemProperties_Delegate.java
deleted file mode 100644
index b5c8294..0000000
--- a/bridge/src/android/os/SystemProperties_Delegate.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.util.Map;
-
-/**
- * Delegate implementing the native methods of android.os.SystemProperties
- *
- * Through the layoutlib_create tool, the original native methods of SystemProperties have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
- * around to map int to instance of the delegate.
- */
-public class SystemProperties_Delegate {
-
-    @LayoutlibDelegate
-    /*package*/ static String native_get(String key, String def) {
-        Map<String, String> properties = Bridge.getPlatformProperties();
-        String value = properties.get(key);
-        if (value != null) {
-            return value;
-        }
-
-        return def;
-    }
-    @LayoutlibDelegate
-    /*package*/ static int native_get_int(String key, int def) {
-        Map<String, String> properties = Bridge.getPlatformProperties();
-        String value = properties.get(key);
-        if (value != null) {
-            return Integer.decode(value);
-        }
-
-        return def;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long native_get_long(String key, long def) {
-        Map<String, String> properties = Bridge.getPlatformProperties();
-        String value = properties.get(key);
-        if (value != null) {
-            return Long.decode(value);
-        }
-
-        return def;
-    }
-
-    /**
-     * Values 'n', 'no', '0', 'false' or 'off' are considered false.
-     * Values 'y', 'yes', '1', 'true' or 'on' are considered true.
-     */
-    @LayoutlibDelegate
-    /*package*/ static boolean native_get_boolean(String key, boolean def) {
-        Map<String, String> properties = Bridge.getPlatformProperties();
-        String value = properties.get(key);
-
-        if ("n".equals(value) || "no".equals(value) || "0".equals(value) || "false".equals(value)
-                || "off".equals(value)) {
-            return false;
-        }
-        //noinspection SimplifiableIfStatement
-        if ("y".equals(value) || "yes".equals(value) || "1".equals(value) || "true".equals(value)
-                || "on".equals(value)) {
-            return true;
-        }
-
-        return def;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void native_set(String key, String def) {
-        Map<String, String> properties = Bridge.getPlatformProperties();
-        properties.put(key, def);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void native_add_change_callback() {
-        // pass.
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void native_report_sysprop_change() {
-        // pass.
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static String native_get(long handle) {
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Layoutlib does not support SystemProperties Handle", null, null, null);
-        return null;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int native_get_int(long handle, int def) {
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Layoutlib does not support SystemProperties Handle", null, null, null);
-        return def;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long native_get_long(long handle, long def) {
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Layoutlib does not support SystemProperties Handle", null, null, null);
-        return def;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean native_get_boolean(long handle, boolean def) {
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Layoutlib does not support SystemProperties Handle", null, null, null);
-        return def;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long native_find(String name) {
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "Layoutlib does not support SystemProperties Handle", null, null, null);
-        return 0;
-    }
-}
diff --git a/bridge/src/android/preference/BridgePreferenceInflater.java b/bridge/src/android/preference/BridgePreferenceInflater.java
index 3665d86..c17313b 100644
--- a/bridge/src/android/preference/BridgePreferenceInflater.java
+++ b/bridge/src/android/preference/BridgePreferenceInflater.java
@@ -16,6 +16,7 @@
 
 package android.preference;
 
+import com.android.ide.common.rendering.api.ILayoutLog;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
 
@@ -23,6 +24,8 @@
 import android.util.AttributeSet;
 import android.view.InflateException;
 
+import static com.android.layoutlib.bridge.Bridge.getLog;
+
 public class BridgePreferenceInflater extends PreferenceInflater {
 
     public BridgePreferenceInflater(Context context, PreferenceManager preferenceManager) {
@@ -53,6 +56,11 @@
                     "androidx.preference".equals(prefix)) &&
                     "SwitchPreferenceCompat".equals(name)) {
                 preference = super.createItem("SwitchPreference", prefix, attrs);
+            } else {
+                // Log the error and rethrow the exception as returning null would later result in
+                // a NullPointerException without a good error message for the user.
+                getLog().error(ILayoutLog.TAG_INFLATE, exception.getMessage(), null, null);
+                throw exception;
             }
         }
 
diff --git a/bridge/src/android/util/Log_Delegate.java b/bridge/src/android/util/Log_Delegate.java
deleted file mode 100644
index 7f432ab..0000000
--- a/bridge/src/android/util/Log_Delegate.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.util;
-
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-class Log_Delegate {
-    // to replicate prefix visible when using 'adb logcat'
-    private static char priorityChar(int priority) {
-        switch (priority) {
-            case Log.VERBOSE:
-                return 'V';
-            case Log.DEBUG:
-                return 'D';
-            case Log.INFO:
-                return 'I';
-            case Log.WARN:
-                return 'W';
-            case Log.ERROR:
-                return 'E';
-            case Log.ASSERT:
-                return 'A';
-            default:
-                return '?';
-        }
-    }
-
-    @LayoutlibDelegate
-    static int println_native(int bufID, int priority, String tag, String msgs) {
-        String prefix = priorityChar(priority) + "/" + tag + ": ";
-        for (String msg: msgs.split("\n")) {
-            System.out.println(prefix + msg);
-        }
-        return 0;
-    }
-
-}
diff --git a/bridge/src/android/util/PathParser_Delegate.java b/bridge/src/android/util/PathParser_Delegate.java
deleted file mode 100644
index 46e94a7..0000000
--- a/bridge/src/android/util/PathParser_Delegate.java
+++ /dev/null
@@ -1,846 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.util;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.graphics.Path_Delegate;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * Delegate that provides implementation for native methods in {@link android.util.PathParser}
- * <p/>
- * Through the layoutlib_create tool, selected methods of PathParser have been replaced by calls to
- * methods of the same name in this delegate class.
- *
- * Most of the code has been taken from the implementation in
- * {@code tools/base/sdk-common/src/main/java/com/android/ide/common/vectordrawable/PathParser.java}
- * revision be6fe89a3b686db5a75e7e692a148699973957f3
- */
-public class PathParser_Delegate {
-
-    private static final Logger LOGGER = Logger.getLogger("PathParser");
-
-    // ---- Builder delegate manager ----
-    private static final DelegateManager<PathParser_Delegate> sManager =
-            new DelegateManager<PathParser_Delegate>(PathParser_Delegate.class);
-
-    // ---- delegate data ----
-    @NonNull
-    private PathDataNode[] mPathDataNodes;
-
-    public static PathParser_Delegate getDelegate(long nativePtr) {
-        return sManager.getDelegate(nativePtr);
-    }
-
-    private PathParser_Delegate(@NonNull PathDataNode[] nodes) {
-        mPathDataNodes = nodes;
-    }
-
-    public PathDataNode[] getPathDataNodes() {
-        return mPathDataNodes;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nParseStringForPath(long pathPtr, @NonNull String pathString, int
-            stringLength) {
-        Path_Delegate path_delegate = Path_Delegate.getDelegate(pathPtr);
-        if (path_delegate == null) {
-            return;
-        }
-        assert pathString.length() == stringLength;
-        PathDataNode.nodesToPath(createNodesFromPathData(pathString), path_delegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nCreatePathFromPathData(long outPathPtr, long pathData) {
-        Path_Delegate path_delegate = Path_Delegate.getDelegate(outPathPtr);
-        PathParser_Delegate source = sManager.getDelegate(outPathPtr);
-        if (source == null || path_delegate == null) {
-            return;
-        }
-        PathDataNode.nodesToPath(source.mPathDataNodes, path_delegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nCreateEmptyPathData() {
-        PathParser_Delegate newDelegate = new PathParser_Delegate(new PathDataNode[0]);
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nCreatePathData(long nativePtr) {
-        PathParser_Delegate source = sManager.getDelegate(nativePtr);
-        if (source == null) {
-            return 0;
-        }
-        PathParser_Delegate dest = new PathParser_Delegate(deepCopyNodes(source.mPathDataNodes));
-        return sManager.addNewDelegate(dest);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nCreatePathDataFromString(@NonNull String pathString,
-            int stringLength) {
-        assert pathString.length() == stringLength : "Inconsistent path string length.";
-        PathDataNode[] nodes = createNodesFromPathData(pathString);
-        PathParser_Delegate delegate = new PathParser_Delegate(nodes);
-        return sManager.addNewDelegate(delegate);
-
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nInterpolatePathData(long outDataPtr, long fromDataPtr,
-            long toDataPtr, float fraction) {
-        PathParser_Delegate out = sManager.getDelegate(outDataPtr);
-        PathParser_Delegate from = sManager.getDelegate(fromDataPtr);
-        PathParser_Delegate to = sManager.getDelegate(toDataPtr);
-        if (out == null || from == null || to == null) {
-            return false;
-        }
-        int length = from.mPathDataNodes.length;
-        if (length != to.mPathDataNodes.length) {
-            Bridge.getLog().error(ILayoutLog.TAG_BROKEN,
-                    "Cannot interpolate path data with different lengths (from " + length + " to " +
-                            to.mPathDataNodes.length + ").", null, null);
-            return false;
-        }
-        if (out.mPathDataNodes.length != length) {
-            out.mPathDataNodes = new PathDataNode[length];
-        }
-        for (int i = 0; i < length; i++) {
-            if (out.mPathDataNodes[i] == null) {
-                out.mPathDataNodes[i] = new PathDataNode(from.mPathDataNodes[i]);
-            }
-            out.mPathDataNodes[i].interpolatePathDataNode(from.mPathDataNodes[i],
-                        to.mPathDataNodes[i], fraction);
-        }
-        return true;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nFinalize(long nativePtr) {
-        sManager.removeJavaReferenceFor(nativePtr);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean nCanMorph(long fromDataPtr, long toDataPtr) {
-        PathParser_Delegate fromPath = PathParser_Delegate.getDelegate(fromDataPtr);
-        PathParser_Delegate toPath = PathParser_Delegate.getDelegate(toDataPtr);
-        if (fromPath == null || toPath == null || fromPath.getPathDataNodes() == null || toPath
-                .getPathDataNodes() == null) {
-            return true;
-        }
-        return PathParser_Delegate.canMorph(fromPath.getPathDataNodes(), toPath.getPathDataNodes());
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetPathData(long outDataPtr, long fromDataPtr) {
-        PathParser_Delegate out = sManager.getDelegate(outDataPtr);
-        PathParser_Delegate from = sManager.getDelegate(fromDataPtr);
-        if (from == null || out == null) {
-            return;
-        }
-        out.mPathDataNodes = deepCopyNodes(from.mPathDataNodes);
-    }
-
-    /**
-     * @param pathData The string representing a path, the same as "d" string in svg file.
-     *
-     * @return an array of the PathDataNode.
-     */
-    @NonNull
-    public static PathDataNode[] createNodesFromPathData(@NonNull String pathData) {
-        int start = 0;
-        int end = 1;
-
-        ArrayList<PathDataNode> list = new ArrayList<PathDataNode>();
-        while (end < pathData.length()) {
-            end = nextStart(pathData, end);
-            String s = pathData.substring(start, end).trim();
-            if (s.length() > 0) {
-                float[] val = getFloats(s);
-                addNode(list, s.charAt(0), val);
-            }
-
-            start = end;
-            end++;
-        }
-        if ((end - start) == 1 && start < pathData.length()) {
-            addNode(list, pathData.charAt(start), new float[0]);
-        }
-        return list.toArray(new PathDataNode[list.size()]);
-    }
-
-    /**
-     * @param source The array of PathDataNode to be duplicated.
-     *
-     * @return a deep copy of the <code>source</code>.
-     */
-    @NonNull
-    public static PathDataNode[] deepCopyNodes(@NonNull PathDataNode[] source) {
-        PathDataNode[] copy = new PathDataNode[source.length];
-        for (int i = 0; i < source.length; i++) {
-            copy[i] = new PathDataNode(source[i]);
-        }
-        return copy;
-    }
-
-    /**
-     * @param nodesFrom The source path represented in an array of PathDataNode
-     * @param nodesTo The target path represented in an array of PathDataNode
-     * @return whether the <code>nodesFrom</code> can morph into <code>nodesTo</code>
-     */
-    public static boolean canMorph(PathDataNode[] nodesFrom, PathDataNode[] nodesTo) {
-        if (nodesFrom == null || nodesTo == null) {
-            return false;
-        }
-
-        if (nodesFrom.length != nodesTo.length) {
-            return false;
-        }
-
-        for (int i = 0; i < nodesFrom.length; i ++) {
-            if (nodesFrom[i].mType != nodesTo[i].mType
-                    || nodesFrom[i].mParams.length != nodesTo[i].mParams.length) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Update the target's data to match the source.
-     * Before calling this, make sure canMorph(target, source) is true.
-     *
-     * @param target The target path represented in an array of PathDataNode
-     * @param source The source path represented in an array of PathDataNode
-     */
-    public static void updateNodes(PathDataNode[] target, PathDataNode[] source) {
-        for (int i = 0; i < source.length; i ++) {
-            target[i].mType = source[i].mType;
-            for (int j = 0; j < source[i].mParams.length; j ++) {
-                target[i].mParams[j] = source[i].mParams[j];
-            }
-        }
-    }
-
-    private static int nextStart(@NonNull String s, int end) {
-        char c;
-
-        while (end < s.length()) {
-            c = s.charAt(end);
-            // Note that 'e' or 'E' are not valid path commands, but could be
-            // used for floating point numbers' scientific notation.
-            // Therefore, when searching for next command, we should ignore 'e'
-            // and 'E'.
-            if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0))
-                    && c != 'e' && c != 'E') {
-                return end;
-            }
-            end++;
-        }
-        return end;
-    }
-
-    /**
-     * Calculate the position of the next comma or space or negative sign
-     *
-     * @param s the string to search
-     * @param start the position to start searching
-     * @param result the result of the extraction, including the position of the the starting
-     * position of next number, whether it is ending with a '-'.
-     */
-    private static void extract(@NonNull String s, int start, @NonNull ExtractFloatResult result) {
-        // Now looking for ' ', ',', '.' or '-' from the start.
-        int currentIndex = start;
-        boolean foundSeparator = false;
-        result.mEndWithNegOrDot = false;
-        boolean secondDot = false;
-        boolean isExponential = false;
-        for (; currentIndex < s.length(); currentIndex++) {
-            boolean isPrevExponential = isExponential;
-            isExponential = false;
-            char currentChar = s.charAt(currentIndex);
-            switch (currentChar) {
-                case ' ':
-                case ',':
-                case '\t':
-                case '\n':
-                    foundSeparator = true;
-                    break;
-                case '-':
-                    // The negative sign following a 'e' or 'E' is not a separator.
-                    if (currentIndex != start && !isPrevExponential) {
-                        foundSeparator = true;
-                        result.mEndWithNegOrDot = true;
-                    }
-                    break;
-                case '.':
-                    if (!secondDot) {
-                        secondDot = true;
-                    } else {
-                        // This is the second dot, and it is considered as a separator.
-                        foundSeparator = true;
-                        result.mEndWithNegOrDot = true;
-                    }
-                    break;
-                case 'e':
-                case 'E':
-                    isExponential = true;
-                    break;
-            }
-            if (foundSeparator) {
-                break;
-            }
-        }
-        // When there is nothing found, then we put the end position to the end
-        // of the string.
-        result.mEndPosition = currentIndex;
-    }
-
-    /**
-     * Parse the floats in the string. This is an optimized version of
-     * parseFloat(s.split(",|\\s"));
-     *
-     * @param s the string containing a command and list of floats
-     *
-     * @return array of floats
-     */
-    @NonNull
-    private static float[] getFloats(@NonNull String s) {
-        if (s.charAt(0) == 'z' || s.charAt(0) == 'Z') {
-            return new float[0];
-        }
-        try {
-            float[] results = new float[s.length()];
-            int count = 0;
-            int startPosition = 1;
-            int endPosition;
-
-            ExtractFloatResult result = new ExtractFloatResult();
-            int totalLength = s.length();
-
-            // The startPosition should always be the first character of the
-            // current number, and endPosition is the character after the current
-            // number.
-            while (startPosition < totalLength) {
-                extract(s, startPosition, result);
-                endPosition = result.mEndPosition;
-
-                if (startPosition < endPosition) {
-                    results[count++] = Float.parseFloat(
-                            s.substring(startPosition, endPosition));
-                }
-
-                if (result.mEndWithNegOrDot) {
-                    // Keep the '-' or '.' sign with next number.
-                    startPosition = endPosition;
-                } else {
-                    startPosition = endPosition + 1;
-                }
-            }
-            return Arrays.copyOf(results, count);
-        } catch (NumberFormatException e) {
-            assert false : "error in parsing \"" + s + "\"" + e;
-            return new float[0];
-        }
-    }
-
-
-    private static void addNode(@NonNull ArrayList<PathDataNode> list, char cmd,
-            @NonNull float[] val) {
-        list.add(new PathDataNode(cmd, val));
-    }
-
-    private static class ExtractFloatResult {
-        // We need to return the position of the next separator and whether the
-        // next float starts with a '-' or a '.'.
-        private int mEndPosition;
-        private boolean mEndWithNegOrDot;
-    }
-
-    /**
-     * Each PathDataNode represents one command in the "d" attribute of the svg file. An array of
-     * PathDataNode can represent the whole "d" attribute.
-     */
-    public static class PathDataNode {
-        private char mType;
-        @NonNull
-        private float[] mParams;
-
-        private PathDataNode(char type, @NonNull float[] params) {
-            mType = type;
-            mParams = params;
-        }
-
-        public char getType() {
-            return mType;
-        }
-
-        @NonNull
-        public float[] getParams() {
-            return mParams;
-        }
-
-        private PathDataNode(@NonNull PathDataNode n) {
-            mType = n.mType;
-            mParams = Arrays.copyOf(n.mParams, n.mParams.length);
-        }
-
-        /**
-         * Convert an array of PathDataNode to Path. Reset the passed path as needed before
-         * calling this method.
-         *
-         * @param node The source array of PathDataNode.
-         * @param path The target Path object.
-         */
-        public static void nodesToPath(@NonNull PathDataNode[] node, @NonNull Path_Delegate path) {
-            float[] current = new float[6];
-            char previousCommand = 'm';
-            //noinspection ForLoopReplaceableByForEach
-            for (int i = 0; i < node.length; i++) {
-                addCommand(path, current, previousCommand, node[i].mType, node[i].mParams);
-                previousCommand = node[i].mType;
-            }
-        }
-
-        /**
-         * The current PathDataNode will be interpolated between the <code>nodeFrom</code> and
-         * <code>nodeTo</code> according to the <code>fraction</code>.
-         *
-         * @param nodeFrom The start value as a PathDataNode.
-         * @param nodeTo The end value as a PathDataNode
-         * @param fraction The fraction to interpolate.
-         */
-        private void interpolatePathDataNode(@NonNull PathDataNode nodeFrom,
-                @NonNull PathDataNode nodeTo, float fraction) {
-            for (int i = 0; i < nodeFrom.mParams.length; i++) {
-                mParams[i] = nodeFrom.mParams[i] * (1 - fraction)
-                        + nodeTo.mParams[i] * fraction;
-            }
-        }
-
-        @SuppressWarnings("PointlessArithmeticExpression")
-        private static void addCommand(@NonNull Path_Delegate path, float[] current,
-                char previousCmd, char cmd, @NonNull float[] val) {
-
-            int incr = 2;
-            float currentX = current[0];
-            float currentY = current[1];
-            float ctrlPointX = current[2];
-            float ctrlPointY = current[3];
-            float currentSegmentStartX = current[4];
-            float currentSegmentStartY = current[5];
-            float reflectiveCtrlPointX;
-            float reflectiveCtrlPointY;
-
-            switch (cmd) {
-                case 'z':
-                case 'Z':
-                    path.close();
-                    // Path is closed here, but we need to move the pen to the
-                    // closed position. So we cache the segment's starting position,
-                    // and restore it here.
-                    currentX = currentSegmentStartX;
-                    currentY = currentSegmentStartY;
-                    ctrlPointX = currentSegmentStartX;
-                    ctrlPointY = currentSegmentStartY;
-                    path.moveTo(currentX, currentY);
-                    break;
-                case 'm':
-                case 'M':
-                case 'l':
-                case 'L':
-                case 't':
-                case 'T':
-                    incr = 2;
-                    break;
-                case 'h':
-                case 'H':
-                case 'v':
-                case 'V':
-                    incr = 1;
-                    break;
-                case 'c':
-                case 'C':
-                    incr = 6;
-                    break;
-                case 's':
-                case 'S':
-                case 'q':
-                case 'Q':
-                    incr = 4;
-                    break;
-                case 'a':
-                case 'A':
-                    incr = 7;
-                    break;
-            }
-
-            for (int k = 0; k < val.length; k += incr) {
-                switch (cmd) {
-                    case 'm': // moveto - Start a new sub-path (relative)
-                        currentX += val[k + 0];
-                        currentY += val[k + 1];
-
-                        if (k > 0) {
-                            // According to the spec, if a moveto is followed by multiple
-                            // pairs of coordinates, the subsequent pairs are treated as
-                            // implicit lineto commands.
-                            path.rLineTo(val[k + 0], val[k + 1]);
-                        } else {
-                            path.rMoveTo(val[k + 0], val[k + 1]);
-                            currentSegmentStartX = currentX;
-                            currentSegmentStartY = currentY;
-                        }
-                        break;
-                    case 'M': // moveto - Start a new sub-path
-                        currentX = val[k + 0];
-                        currentY = val[k + 1];
-
-                        if (k > 0) {
-                            // According to the spec, if a moveto is followed by multiple
-                            // pairs of coordinates, the subsequent pairs are treated as
-                            // implicit lineto commands.
-                            path.lineTo(val[k + 0], val[k + 1]);
-                        } else {
-                            path.moveTo(val[k + 0], val[k + 1]);
-                            currentSegmentStartX = currentX;
-                            currentSegmentStartY = currentY;
-                        }
-                        break;
-                    case 'l': // lineto - Draw a line from the current point (relative)
-                        path.rLineTo(val[k + 0], val[k + 1]);
-                        currentX += val[k + 0];
-                        currentY += val[k + 1];
-                        break;
-                    case 'L': // lineto - Draw a line from the current point
-                        path.lineTo(val[k + 0], val[k + 1]);
-                        currentX = val[k + 0];
-                        currentY = val[k + 1];
-                        break;
-                    case 'h': // horizontal lineto - Draws a horizontal line (relative)
-                        path.rLineTo(val[k + 0], 0);
-                        currentX += val[k + 0];
-                        break;
-                    case 'H': // horizontal lineto - Draws a horizontal line
-                        path.lineTo(val[k + 0], currentY);
-                        currentX = val[k + 0];
-                        break;
-                    case 'v': // vertical lineto - Draws a vertical line from the current point (r)
-                        path.rLineTo(0, val[k + 0]);
-                        currentY += val[k + 0];
-                        break;
-                    case 'V': // vertical lineto - Draws a vertical line from the current point
-                        path.lineTo(currentX, val[k + 0]);
-                        currentY = val[k + 0];
-                        break;
-                    case 'c': // curveto - Draws a cubic Bézier curve (relative)
-                        path.rCubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
-                                val[k + 4], val[k + 5]);
-
-                        ctrlPointX = currentX + val[k + 2];
-                        ctrlPointY = currentY + val[k + 3];
-                        currentX += val[k + 4];
-                        currentY += val[k + 5];
-
-                        break;
-                    case 'C': // curveto - Draws a cubic Bézier curve
-                        path.cubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
-                                val[k + 4], val[k + 5]);
-                        currentX = val[k + 4];
-                        currentY = val[k + 5];
-                        ctrlPointX = val[k + 2];
-                        ctrlPointY = val[k + 3];
-                        break;
-                    case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp)
-                        reflectiveCtrlPointX = 0;
-                        reflectiveCtrlPointY = 0;
-                        if (previousCmd == 'c' || previousCmd == 's'
-                                || previousCmd == 'C' || previousCmd == 'S') {
-                            reflectiveCtrlPointX = currentX - ctrlPointX;
-                            reflectiveCtrlPointY = currentY - ctrlPointY;
-                        }
-                        path.rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
-                                val[k + 0], val[k + 1],
-                                val[k + 2], val[k + 3]);
-
-                        ctrlPointX = currentX + val[k + 0];
-                        ctrlPointY = currentY + val[k + 1];
-                        currentX += val[k + 2];
-                        currentY += val[k + 3];
-                        break;
-                    case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp)
-                        reflectiveCtrlPointX = currentX;
-                        reflectiveCtrlPointY = currentY;
-                        if (previousCmd == 'c' || previousCmd == 's'
-                                || previousCmd == 'C' || previousCmd == 'S') {
-                            reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
-                            reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
-                        }
-                        path.cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
-                                val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
-                        ctrlPointX = val[k + 0];
-                        ctrlPointY = val[k + 1];
-                        currentX = val[k + 2];
-                        currentY = val[k + 3];
-                        break;
-                    case 'q': // Draws a quadratic Bézier (relative)
-                        path.rQuadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
-                        ctrlPointX = currentX + val[k + 0];
-                        ctrlPointY = currentY + val[k + 1];
-                        currentX += val[k + 2];
-                        currentY += val[k + 3];
-                        break;
-                    case 'Q': // Draws a quadratic Bézier
-                        path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
-                        ctrlPointX = val[k + 0];
-                        ctrlPointY = val[k + 1];
-                        currentX = val[k + 2];
-                        currentY = val[k + 3];
-                        break;
-                    case 't': // Draws a quadratic Bézier curve(reflective control point)(relative)
-                        reflectiveCtrlPointX = 0;
-                        reflectiveCtrlPointY = 0;
-                        if (previousCmd == 'q' || previousCmd == 't'
-                                || previousCmd == 'Q' || previousCmd == 'T') {
-                            reflectiveCtrlPointX = currentX - ctrlPointX;
-                            reflectiveCtrlPointY = currentY - ctrlPointY;
-                        }
-                        path.rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
-                                val[k + 0], val[k + 1]);
-                        ctrlPointX = currentX + reflectiveCtrlPointX;
-                        ctrlPointY = currentY + reflectiveCtrlPointY;
-                        currentX += val[k + 0];
-                        currentY += val[k + 1];
-                        break;
-                    case 'T': // Draws a quadratic Bézier curve (reflective control point)
-                        reflectiveCtrlPointX = currentX;
-                        reflectiveCtrlPointY = currentY;
-                        if (previousCmd == 'q' || previousCmd == 't'
-                                || previousCmd == 'Q' || previousCmd == 'T') {
-                            reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
-                            reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
-                        }
-                        path.quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
-                                val[k + 0], val[k + 1]);
-                        ctrlPointX = reflectiveCtrlPointX;
-                        ctrlPointY = reflectiveCtrlPointY;
-                        currentX = val[k + 0];
-                        currentY = val[k + 1];
-                        break;
-                    case 'a': // Draws an elliptical arc
-                        // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
-                        drawArc(path,
-                                currentX,
-                                currentY,
-                                val[k + 5] + currentX,
-                                val[k + 6] + currentY,
-                                val[k + 0],
-                                val[k + 1],
-                                val[k + 2],
-                                val[k + 3] != 0,
-                                val[k + 4] != 0);
-                        currentX += val[k + 5];
-                        currentY += val[k + 6];
-                        ctrlPointX = currentX;
-                        ctrlPointY = currentY;
-                        break;
-                    case 'A': // Draws an elliptical arc
-                        drawArc(path,
-                                currentX,
-                                currentY,
-                                val[k + 5],
-                                val[k + 6],
-                                val[k + 0],
-                                val[k + 1],
-                                val[k + 2],
-                                val[k + 3] != 0,
-                                val[k + 4] != 0);
-                        currentX = val[k + 5];
-                        currentY = val[k + 6];
-                        ctrlPointX = currentX;
-                        ctrlPointY = currentY;
-                        break;
-                }
-                previousCmd = cmd;
-            }
-            current[0] = currentX;
-            current[1] = currentY;
-            current[2] = ctrlPointX;
-            current[3] = ctrlPointY;
-            current[4] = currentSegmentStartX;
-            current[5] = currentSegmentStartY;
-        }
-
-        private static void drawArc(@NonNull Path_Delegate p, float x0, float y0, float x1,
-                float y1, float a, float b, float theta, boolean isMoreThanHalf,
-                boolean isPositiveArc) {
-
-            LOGGER.log(Level.FINE, "(" + x0 + "," + y0 + ")-(" + x1 + "," + y1
-                    + ") {" + a + " " + b + "}");
-        /* Convert rotation angle from degrees to radians */
-            double thetaD = theta * Math.PI / 180.0f;
-        /* Pre-compute rotation matrix entries */
-            double cosTheta = Math.cos(thetaD);
-            double sinTheta = Math.sin(thetaD);
-        /* Transform (x0, y0) and (x1, y1) into unit space */
-        /* using (inverse) rotation, followed by (inverse) scale */
-            double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
-            double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
-            double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
-            double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
-            LOGGER.log(Level.FINE, "unit space (" + x0p + "," + y0p + ")-(" + x1p
-                    + "," + y1p + ")");
-        /* Compute differences and averages */
-            double dx = x0p - x1p;
-            double dy = y0p - y1p;
-            double xm = (x0p + x1p) / 2;
-            double ym = (y0p + y1p) / 2;
-        /* Solve for intersecting unit circles */
-            double dsq = dx * dx + dy * dy;
-            if (dsq == 0.0) {
-                LOGGER.log(Level.FINE, " Points are coincident");
-                return; /* Points are coincident */
-            }
-            double disc = 1.0 / dsq - 1.0 / 4.0;
-            if (disc < 0.0) {
-                LOGGER.log(Level.FINE, "Points are too far apart " + dsq);
-                float adjust = (float) (Math.sqrt(dsq) / 1.99999);
-                drawArc(p, x0, y0, x1, y1, a * adjust, b * adjust, theta,
-                        isMoreThanHalf, isPositiveArc);
-                return; /* Points are too far apart */
-            }
-            double s = Math.sqrt(disc);
-            double sdx = s * dx;
-            double sdy = s * dy;
-            double cx;
-            double cy;
-            if (isMoreThanHalf == isPositiveArc) {
-                cx = xm - sdy;
-                cy = ym + sdx;
-            } else {
-                cx = xm + sdy;
-                cy = ym - sdx;
-            }
-
-            double eta0 = Math.atan2((y0p - cy), (x0p - cx));
-            LOGGER.log(Level.FINE, "eta0 = Math.atan2( " + (y0p - cy) + " , "
-                    + (x0p - cx) + ") = " + Math.toDegrees(eta0));
-
-            double eta1 = Math.atan2((y1p - cy), (x1p - cx));
-            LOGGER.log(Level.FINE, "eta1 = Math.atan2( " + (y1p - cy) + " , "
-                    + (x1p - cx) + ") = " + Math.toDegrees(eta1));
-            double sweep = (eta1 - eta0);
-            if (isPositiveArc != (sweep >= 0)) {
-                if (sweep > 0) {
-                    sweep -= 2 * Math.PI;
-                } else {
-                    sweep += 2 * Math.PI;
-                }
-            }
-
-            cx *= a;
-            cy *= b;
-            double tcx = cx;
-            cx = cx * cosTheta - cy * sinTheta;
-            cy = tcx * sinTheta + cy * cosTheta;
-            LOGGER.log(
-                    Level.FINE,
-                    "cx, cy, a, b, x0, y0, thetaD, eta0, sweep = " + cx + " , "
-                            + cy + " , " + a + " , " + b + " , " + x0 + " , " + y0
-                            + " , " + Math.toDegrees(thetaD) + " , "
-                            + Math.toDegrees(eta0) + " , " + Math.toDegrees(sweep));
-
-            arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
-        }
-
-        /**
-         * Converts an arc to cubic Bezier segments and records them in p.
-         *
-         * @param p The target for the cubic Bezier segments
-         * @param cx The x coordinate center of the ellipse
-         * @param cy The y coordinate center of the ellipse
-         * @param a The radius of the ellipse in the horizontal direction
-         * @param b The radius of the ellipse in the vertical direction
-         * @param e1x E(eta1) x coordinate of the starting point of the arc
-         * @param e1y E(eta2) y coordinate of the starting point of the arc
-         * @param theta The angle that the ellipse bounding rectangle makes with the horizontal
-         * plane
-         * @param start The start angle of the arc on the ellipse
-         * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
-         */
-        private static void arcToBezier(@NonNull Path_Delegate p, double cx, double cy, double a,
-                double b, double e1x, double e1y, double theta, double start,
-                double sweep) {
-            // Taken from equations at:
-            // http://spaceroots.org/documents/ellipse/node8.html
-            // and http://www.spaceroots.org/documents/ellipse/node22.html
-            // Maximum of 45 degrees per cubic Bezier segment
-            int numSegments = (int) Math.ceil(Math.abs(sweep * 4 / Math.PI));
-
-
-            double eta1 = start;
-            double cosTheta = Math.cos(theta);
-            double sinTheta = Math.sin(theta);
-            double cosEta1 = Math.cos(eta1);
-            double sinEta1 = Math.sin(eta1);
-            double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
-            double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
-
-            double anglePerSegment = sweep / numSegments;
-            for (int i = 0; i < numSegments; i++) {
-                double eta2 = eta1 + anglePerSegment;
-                double sinEta2 = Math.sin(eta2);
-                double cosEta2 = Math.cos(eta2);
-                double e2x = cx + (a * cosTheta * cosEta2)
-                        - (b * sinTheta * sinEta2);
-                double e2y = cy + (a * sinTheta * cosEta2)
-                        + (b * cosTheta * sinEta2);
-                double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
-                double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
-                double tanDiff2 = Math.tan((eta2 - eta1) / 2);
-                double alpha = Math.sin(eta2 - eta1)
-                        * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
-                double q1x = e1x + alpha * ep1x;
-                double q1y = e1y + alpha * ep1y;
-                double q2x = e2x - alpha * ep2x;
-                double q2y = e2y - alpha * ep2y;
-
-                p.cubicTo((float) q1x,
-                        (float) q1y,
-                        (float) q2x,
-                        (float) q2y,
-                        (float) e2x,
-                        (float) e2y);
-                eta1 = eta2;
-                e1x = e2x;
-                e1y = e2y;
-                ep1x = ep2x;
-                ep1y = ep2y;
-            }
-        }
-    }
-}
diff --git a/bridge/src/android/util/imagepool/Bucket.java b/bridge/src/android/util/imagepool/Bucket.java
deleted file mode 100644
index c562243..0000000
--- a/bridge/src/android/util/imagepool/Bucket.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2018 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.imagepool;
-
-import com.android.tools.layoutlib.annotations.Nullable;
-import com.android.tools.layoutlib.annotations.VisibleForTesting;
-
-import android.util.imagepool.ImagePool.Image.Orientation;
-
-import java.awt.image.BufferedImage;
-import java.lang.ref.SoftReference;
-import java.util.LinkedList;
-import java.util.Queue;
-
-/**
- * Data model for image pool. Bucket contains the list of same sized buffered image in soft ref.
- */
-/* private package */ class Bucket {
-
-    @VisibleForTesting final Queue<SoftReference<BufferedImage>> mBufferedImageRef = new LinkedList<>();
-
-    public boolean isEmpty() {
-        return mBufferedImageRef.isEmpty();
-    }
-
-    @Nullable
-    public BufferedImage remove() {
-        if (mBufferedImageRef.isEmpty()) {
-            return null;
-        }
-
-        SoftReference<BufferedImage> reference = mBufferedImageRef.remove();
-        return reference == null ? null : reference.get();
-    }
-
-    public void offer(BufferedImage img) {
-        mBufferedImageRef.offer(new SoftReference<>(img));
-    }
-
-    public void clear() {
-        mBufferedImageRef.clear();
-    }
-
-    static class BucketCreationMetaData {
-        public final int mWidth;
-        public final int mHeight;
-        public final int mType;
-        public final int mNumberOfCopies;
-        public final Orientation mOrientation;
-        public final long mMaxCacheSize;
-
-        BucketCreationMetaData(int width, int height, int type, int numberOfCopies,
-                Orientation orientation, long maxCacheSize) {
-            mWidth = width;
-            mHeight = height;
-            mType = type;
-            mNumberOfCopies = numberOfCopies;
-            mOrientation = orientation;
-            mMaxCacheSize = maxCacheSize;
-        }
-    }
-}
\ No newline at end of file
diff --git a/bridge/src/android/util/imagepool/ImageImpl.java b/bridge/src/android/util/imagepool/ImageImpl.java
deleted file mode 100644
index 42a6e73..0000000
--- a/bridge/src/android/util/imagepool/ImageImpl.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2018 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.imagepool;
-
-import com.android.tools.layoutlib.annotations.NotNull;
-import com.android.tools.layoutlib.annotations.Nullable;
-import com.android.tools.layoutlib.annotations.VisibleForTesting;
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
-import java.awt.image.ImageObserver;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-/**
- * Representation of buffered image. When used with ImagePool, it provides 2 features
- * (last one not yet impl'd):
- *
- * <ul>
- *   <li>Automatic recycle of BufferedImage through use of reference counter</li>
- *   <li>Able to re-use the image for similar size of buffered image</li>
- *   <li>TODO: Able to re-use the iamge for different orientation (not yet impl'd)</li>
- * </ul>
- */
-/* private package */ class ImageImpl implements ImagePool.Image {
-
-    private final ReadWriteLock mLock = new ReentrantReadWriteLock();
-
-    private final int mWidth;
-    private final int mHeight;
-    private final Orientation mOrientation;
-
-    @VisibleForTesting final BufferedImage mImg;
-
-    ImageImpl(
-            int width,
-            int height,
-            @NotNull BufferedImage img,
-            Orientation orientation) {
-        mImg = img;
-        mWidth = width;
-        mHeight = height;
-        mOrientation = orientation;
-    }
-
-    @Override
-    public int getWidth() {
-        return mWidth;
-    }
-
-    @Override
-    public int getHeight() {
-        return mHeight;
-    }
-
-    @Override
-    public void setRGB(int x, int y, int width, int height, int[] colors, int offset, int stride) {
-        mLock.readLock().lock();
-        try {
-            // TODO: Apply orientation.
-            mImg.setRGB(x, y, width, height, colors, offset, stride);
-        } finally {
-            mLock.readLock().unlock();
-        }
-    }
-
-    @Override
-    public void drawImage(Graphics2D graphics, int x, int y, @Nullable ImageObserver o) {
-        mLock.readLock().lock();
-        try {
-            // TODO: Apply orientation.
-            graphics.drawImage(mImg, x, y, mWidth, mHeight, o);
-        } finally {
-            mLock.readLock().unlock();
-        }
-    }
-}
\ No newline at end of file
diff --git a/bridge/src/android/util/imagepool/ImagePool.java b/bridge/src/android/util/imagepool/ImagePool.java
deleted file mode 100644
index baec65d..0000000
--- a/bridge/src/android/util/imagepool/ImagePool.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2018 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.imagepool;
-
-import com.android.tools.layoutlib.annotations.NotNull;
-
-import java.awt.Graphics2D;
-import java.awt.Image;
-import java.awt.image.BufferedImage;
-import java.awt.image.ImageObserver;
-import java.util.function.Consumer;
-
-/**
- * Simplified version of image pool that exists in Studio.
- *
- * Lacks:
- * - PhantomReference and FinalizableReference to recognize the death of references automatically.
- *   (Meaning devs need to be more deligent in dispose.)
- * Has:
- * + Debugger that allows us to better trace where image is being leaked in stack.
- */
-public interface ImagePool {
-
-    /**
-     * Returns a new image of width w and height h.
-     */
-    @NotNull
-    Image acquire(final int w, final int h, final int type);
-
-    /**
-     * Disposes the image pool, releasing all the references to the buffered images.
-     */
-    void dispose();
-
-    /**
-     * Interface that represents a buffered image. Using this wrapper allows us ot better track
-     * memory usages around BufferedImage. When all of it's references are removed, it will
-     * automatically be pooled back into the image pool for re-use.
-     */
-    interface Image {
-
-        /**
-         * Same as {@link BufferedImage#setRGB(int, int, int, int, int[], int, int)}
-         */
-        void setRGB(int x, int y, int width, int height, int[] colors, int offset, int stride);
-
-        /**
-         * Same as {@link Graphics2D#drawImage(java.awt.Image, int, int, ImageObserver)}
-         */
-        void drawImage(Graphics2D graphics, int x, int y, ImageObserver o);
-
-        /**
-         * Image orientation. It's not used at the moment. To be used later.
-         */
-        enum Orientation {
-            NONE,
-            CW_90
-        }
-
-        int getWidth();
-        int getHeight();
-    }
-
-    /**
-     * Policy for how to set up the memory pool.
-     */
-    class ImagePoolPolicy {
-
-        public final int[] mBucketSizes;
-        public final int[] mNumberOfCopies;
-        public final long mBucketMaxCacheSize;
-
-        /**
-         * @param bucketPixelSizes - list of pixel sizes to bucket (categorize) images. The list
-         * must be sorted (low to high).
-         * @param numberOfCopies - Allows users to create multiple copies of the bucket. It is
-         * recommended to create more copies for smaller images to avoid fragmentation in memory.
-         * It must match the size of bucketPixelSizes.
-         * @param bucketMaxCacheByteSize - Maximum cache byte sizes image pool is allowed to hold onto
-         * in memory.
-         */
-        public ImagePoolPolicy(
-                int[] bucketPixelSizes, int[] numberOfCopies, long bucketMaxCacheByteSize) {
-            assert bucketPixelSizes.length == numberOfCopies.length;
-            mBucketSizes = bucketPixelSizes;
-            mNumberOfCopies = numberOfCopies;
-            mBucketMaxCacheSize = bucketMaxCacheByteSize;
-        }
-    }
-}
diff --git a/bridge/src/android/util/imagepool/ImagePoolHelper.java b/bridge/src/android/util/imagepool/ImagePoolHelper.java
deleted file mode 100644
index 292fc59..0000000
--- a/bridge/src/android/util/imagepool/ImagePoolHelper.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2018 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.imagepool;
-
-import com.android.tools.layoutlib.annotations.Nullable;
-
-import android.util.imagepool.Bucket.BucketCreationMetaData;
-import android.util.imagepool.ImagePool.Image.Orientation;
-import android.util.imagepool.ImagePool.ImagePoolPolicy;
-
-import java.awt.image.BufferedImage;
-import java.util.Map;
-
-/* private package */ class ImagePoolHelper {
-
-    @Nullable
-    public static BucketCreationMetaData getBucketCreationMetaData(int w, int h, int type, ImagePoolPolicy poolPolicy, ImagePoolStats stats) {
-        // Find the bucket sizes for both dimensions
-        int widthBucket = -1;
-        int heightBucket = -1;
-        int index = 0;
-
-        for (int bucketMinSize : poolPolicy.mBucketSizes) {
-            if (widthBucket == -1 && w <= bucketMinSize) {
-                widthBucket = bucketMinSize;
-
-                if (heightBucket != -1) {
-                    break;
-                }
-            }
-            if (heightBucket == -1 && h <= bucketMinSize) {
-                heightBucket = bucketMinSize;
-
-                if (widthBucket != -1) {
-                    break;
-                }
-            }
-            ++index;
-        }
-
-        stats.recordBucketRequest(w, h);
-
-        if (index >= poolPolicy.mNumberOfCopies.length) {
-            return null;
-        }
-
-        // TODO: Apply orientation
-//        if (widthBucket < heightBucket) {
-//            return new BucketCreationMetaData(heightBucket, widthBucket, type, poolPolicy.mNumberOfCopies[index],
-//                    Orientation.CW_90, poolPolicy.mBucketMaxCacheSize);
-//        }
-        return new BucketCreationMetaData(widthBucket, heightBucket, type, poolPolicy.mNumberOfCopies[index],
-                Orientation.NONE, poolPolicy.mBucketMaxCacheSize);
-    }
-
-    @Nullable
-    public static BufferedImage getBufferedImage(
-            Bucket bucket, BucketCreationMetaData metaData, ImagePoolStats stats) {
-
-        // strongref is just for gc.
-        BufferedImage strongRef = populateBucket(bucket, metaData, stats);
-
-        // pool is too small to create the requested buffer.
-        if (bucket.isEmpty()) {
-            assert strongRef == null;
-            return null;
-        }
-
-        // Go through the bucket of soft references to find the first buffer that's not null.
-        // Even if gc is triggered here, strongref should survive.
-        BufferedImage img = bucket.remove();
-        while (img == null && !bucket.isEmpty()) {
-            img = bucket.remove();
-        }
-
-        if (img == null && bucket.isEmpty()) {
-            // Whole buffer was null. Recurse.
-            return getBufferedImage(bucket, metaData, stats);
-        }
-        return img;
-    }
-
-    /**
-     * Populate the bucket in greedy manner to avoid fragmentation.
-     * Behaviour is controlled by {@link ImagePoolPolicy}.
-     * Returns one strong referenced buffer to avoid getting results gc'd. Null if pool is not large
-     * enough.
-     */
-    @Nullable
-    private static BufferedImage populateBucket(
-            Bucket bucket, BucketCreationMetaData metaData, ImagePoolStats stats) {
-        if (!bucket.isEmpty()) {
-            // If not empty no need to populate.
-            return null;
-        }
-
-        BufferedImage strongRef = null;
-        for (int i = 0; i < metaData.mNumberOfCopies; i++) {
-            if (!stats.fitsMaxCacheSize(
-                    metaData.mWidth, metaData.mHeight, metaData.mMaxCacheSize)) {
-                break;
-            }
-            strongRef =new BufferedImage(metaData.mWidth, metaData.mHeight,
-                    metaData.mType);
-            bucket.offer(strongRef);
-            stats.recordBucketCreation(metaData.mWidth, metaData.mHeight);
-        }
-        return strongRef;
-    }
-
-    private static String toKey(int w, int h, int type) {
-        return new StringBuilder()
-                .append(w)
-                .append('x')
-                .append(h)
-                .append(':')
-                .append(type)
-                .toString();
-    }
-
-    public static Bucket getBucket(Map<String, Bucket> map, BucketCreationMetaData metaData, ImagePoolPolicy mPolicy) {
-        String key = toKey(metaData.mWidth, metaData.mHeight, metaData.mType);
-        Bucket bucket = map.get(key);
-        if (bucket == null) {
-            bucket = new Bucket();
-            map.put(key, bucket);
-        }
-        return bucket;
-    }
-}
\ No newline at end of file
diff --git a/bridge/src/android/util/imagepool/ImagePoolImpl.java b/bridge/src/android/util/imagepool/ImagePoolImpl.java
deleted file mode 100644
index 3da706e..0000000
--- a/bridge/src/android/util/imagepool/ImagePoolImpl.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2018 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.imagepool;
-
-import com.android.tools.layoutlib.annotations.Nullable;
-import com.android.tools.layoutlib.annotations.VisibleForTesting;
-
-import android.util.imagepool.Bucket.BucketCreationMetaData;
-import android.util.imagepool.ImagePool.Image.Orientation;
-
-import java.awt.image.BufferedImage;
-import java.awt.image.DataBufferInt;
-import java.lang.ref.Reference;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.function.Consumer;
-
-import com.google.common.base.FinalizablePhantomReference;
-import com.google.common.base.FinalizableReferenceQueue;
-
-class ImagePoolImpl implements ImagePool {
-
-    private final ReentrantReadWriteLock mReentrantLock = new ReentrantReadWriteLock();
-    private final ImagePoolPolicy mPolicy;
-    @VisibleForTesting final Map<String, Bucket> mPool = new HashMap<>();
-    @VisibleForTesting final ImagePoolStats mImagePoolStats = new ImagePoolStatsProdImpl();
-    private final FinalizableReferenceQueue mFinalizableReferenceQueue = new FinalizableReferenceQueue();
-    private final Set<Reference<?>> mReferences = new HashSet<>();
-
-    public ImagePoolImpl(ImagePoolPolicy policy) {
-        mPolicy = policy;
-        mImagePoolStats.start();
-    }
-
-    @Override
-    public Image acquire(int w, int h, int type) {
-        return acquire(w, h, type, null);
-    }
-
-    /* package private */ Image acquire(int w, int h, int type,
-            @Nullable Consumer<BufferedImage> freedCallback) {
-        mReentrantLock.writeLock().lock();
-        try {
-            BucketCreationMetaData metaData =
-                    ImagePoolHelper.getBucketCreationMetaData(w, h, type, mPolicy, mImagePoolStats);
-            if (metaData == null) {
-                return defaultImageImpl(w, h, type, freedCallback);
-            }
-
-            final Bucket existingBucket = ImagePoolHelper.getBucket(mPool, metaData, mPolicy);
-            final BufferedImage img =
-                    ImagePoolHelper.getBufferedImage(existingBucket, metaData, mImagePoolStats);
-            if (img == null) {
-                return defaultImageImpl(w, h, type, freedCallback);
-            }
-
-            // Clear the image. - is this necessary?
-            if (img.getRaster().getDataBuffer().getDataType() == java.awt.image.DataBuffer.TYPE_INT) {
-                Arrays.fill(((DataBufferInt)img.getRaster().getDataBuffer()).getData(), 0);
-            }
-
-            return prepareImage(
-                    new ImageImpl(w, h, img, metaData.mOrientation),
-                    true,
-                    img,
-                    existingBucket,
-                    freedCallback);
-        } finally {
-            mReentrantLock.writeLock().unlock();
-        }
-    }
-
-    /**
-     * Add statistics as well as dispose behaviour before returning image.
-     */
-    private Image prepareImage(
-            Image image,
-            boolean offerBackToBucket,
-            @Nullable BufferedImage img,
-            @Nullable Bucket existingBucket,
-            @Nullable Consumer<BufferedImage> freedCallback) {
-        final Integer imageHash = image.hashCode();
-        mImagePoolStats.acquiredImage(imageHash);
-        FinalizablePhantomReference<Image> reference =
-                new FinalizablePhantomReference<ImagePool.Image>(image, mFinalizableReferenceQueue) {
-                    @Override
-                    public void finalizeReferent() {
-                        // This method might be called twice if the user has manually called the free() method. The second call will have no effect.
-                        if (mReferences.remove(this)) {
-                            mImagePoolStats.disposeImage(imageHash);
-                            if (offerBackToBucket) {
-                                if (!mImagePoolStats.fitsMaxCacheSize(img.getWidth(), img.getHeight(),
-                                        mPolicy.mBucketMaxCacheSize)) {
-                                    mImagePoolStats.tooBigForCache();
-                                    // Adding this back would go over the max cache size we set for ourselves. Release it.
-                                    return;
-                                }
-
-                                // else stat does not change.
-                                existingBucket.offer(img);
-                            }
-                            if (freedCallback != null) {
-                                freedCallback.accept(img);
-                            }
-                        }
-                    }
-                };
-        mReferences.add(reference);
-        return image;
-    }
-
-    /**
-     * Default Image Impl to be used when the pool is not big enough.
-     */
-    private Image defaultImageImpl(int w, int h, int type,
-            @Nullable Consumer<BufferedImage> freedCallback) {
-        BufferedImage bufferedImage = new BufferedImage(w, h, type);
-        mImagePoolStats.tooBigForCache();
-        mImagePoolStats.recordAllocOutsidePool(w, h);
-        return prepareImage(new ImageImpl(w, h, bufferedImage, Orientation.NONE),
-                false,  null, null, freedCallback);
-    }
-
-    @Override
-    public void dispose() {
-        mReentrantLock.writeLock().lock();
-        try {
-            for (Bucket bucket : mPool.values()) {
-                bucket.clear();
-            }
-            mImagePoolStats.clear();
-        } finally {
-            mReentrantLock.writeLock().unlock();
-        }
-    }
-
-    /* package private */ void printStat() {
-        System.out.println(mImagePoolStats.getStatistic());
-    }
-}
\ No newline at end of file
diff --git a/bridge/src/android/util/imagepool/ImagePoolProvider.java b/bridge/src/android/util/imagepool/ImagePoolProvider.java
deleted file mode 100644
index dbdd849..0000000
--- a/bridge/src/android/util/imagepool/ImagePoolProvider.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2018 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.imagepool;
-
-import com.android.tools.layoutlib.annotations.NotNull;
-
-import android.util.imagepool.ImagePool.ImagePoolPolicy;
-
-public class ImagePoolProvider {
-
-    private static ImagePool sInstance;
-
-    @NotNull
-    public static synchronized ImagePool get() {
-        if (sInstance == null) {
-            // Idea is to create more
-            ImagePoolPolicy policy = new ImagePoolPolicy(
-                    new int[]{100, 200, 400, 600, 800, 1000, 1600, 3200},
-                    new int[]{  3,   3,   2,   2,   2,    1,    1,    1},
-                    10_000_000L); // 10 MB
-
-            sInstance = new ImagePoolImpl(policy);
-        }
-        return sInstance;
-    }
-}
\ No newline at end of file
diff --git a/bridge/src/android/util/imagepool/ImagePoolStats.java b/bridge/src/android/util/imagepool/ImagePoolStats.java
deleted file mode 100644
index 17dab14..0000000
--- a/bridge/src/android/util/imagepool/ImagePoolStats.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2018 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.imagepool;
-
-/**
- * Keeps track of statistics. Some are purely for debugging purposes in which case it's wrapped
- * around DEBUG check.
- */
-interface ImagePoolStats {
-
-    void recordBucketCreation(int widthBucket, int heightBucket);
-
-    boolean fitsMaxCacheSize(int width, int height, long maxCacheSize);
-
-    void clear();
-
-    void recordBucketRequest(int w, int h);
-
-    void recordAllocOutsidePool(int width, int height);
-
-    void tooBigForCache();
-
-    void acquiredImage(Integer imageHash);
-
-    void disposeImage(Integer imageHash);
-
-    void start();
-
-    String getStatistic();
-}
diff --git a/bridge/src/android/util/imagepool/ImagePoolStatsDebugImpl.java b/bridge/src/android/util/imagepool/ImagePoolStatsDebugImpl.java
deleted file mode 100644
index de0f757..0000000
--- a/bridge/src/android/util/imagepool/ImagePoolStatsDebugImpl.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2018 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.imagepool;
-
-import java.lang.management.GarbageCollectorMXBean;
-import java.lang.management.ManagementFactory;
-import java.util.HashMap;
-import java.util.Map;
-
-import com.google.common.collect.HashMultiset;
-import com.google.common.collect.Multiset;
-
-/**
- * Useful impl for debugging reproable error.
- */
-public class ImagePoolStatsDebugImpl extends ImagePoolStatsProdImpl {
-
-    private static String PACKAGE_NAME = ImagePoolStats.class.getPackage().getName();
-
-    // Used for deugging purposes only.
-    private final Map<Integer, String> mCallStack = new HashMap<>();
-    private long mRequestedTotalBytes = 0;
-    private long mAllocatedOutsidePoolBytes = 0;
-
-    // Used for gc-related stats.
-    private long mPreviousGcCollection = 0;
-    private long mPreviousGcTime = 0;
-
-    /** Used for policy */
-    @Override
-    public void recordBucketCreation(int widthBucket, int heightBucket) {
-        super.recordBucketCreation(widthBucket, heightBucket);
-    }
-
-    @Override
-    public boolean fitsMaxCacheSize(int width, int height, long maxCacheSize) {
-        return super.fitsMaxCacheSize(width, height, maxCacheSize);
-    }
-
-    @Override
-    public void clear() {
-        super.clear();
-
-        mRequestedTotalBytes = 0;
-        mAllocatedOutsidePoolBytes = 0;
-        mTooBigForPoolCount = 0;
-        mCallStack.clear();
-    }
-
-    @Override
-    public void tooBigForCache() {
-        super.tooBigForCache();
-    }
-
-    /** Used for Debugging only */
-    @Override
-    public void recordBucketRequest(int w, int h) {
-        mRequestedTotalBytes += (w * h * ESTIMATED_PIXEL_BYTES);
-    }
-
-    @Override
-    public void recordAllocOutsidePool(int width, int height) {
-        mAllocatedOutsidePoolBytes += (width * height * ESTIMATED_PIXEL_BYTES);
-    }
-
-    @Override
-    public void acquiredImage(Integer imageHash) {
-        for (int i = 1; i < Thread.currentThread().getStackTrace().length; i++) {
-            StackTraceElement element = Thread.currentThread().getStackTrace()[i];
-            String str = element.toString();
-
-            if (!str.contains(PACKAGE_NAME)) {
-                mCallStack.put(imageHash, str);
-                break;
-            }
-        }
-    }
-
-    @Override
-    public void disposeImage(Integer imageHash) {
-        mCallStack.remove(imageHash);
-    }
-
-    @Override
-    public void start() {
-        long totalGarbageCollections = 0;
-        long garbageCollectionTime = 0;
-        for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
-            long count = gc.getCollectionCount();
-            if (count >= 0) {
-                totalGarbageCollections += count;
-            }
-            long time = gc.getCollectionTime();
-            if (time >= 0) {
-                garbageCollectionTime += time;
-            }
-        }
-        mPreviousGcCollection = totalGarbageCollections;
-        mPreviousGcTime = garbageCollectionTime;
-    }
-
-    private String calculateGcStatAndReturn() {
-        long totalGarbageCollections = 0;
-        long garbageCollectionTime = 0;
-        for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
-            long count = gc.getCollectionCount();
-            if (count > 0) {
-                totalGarbageCollections += count;
-            }
-            long time = gc.getCollectionTime();
-            if(time > 0) {
-                garbageCollectionTime += time;
-            }
-        }
-        totalGarbageCollections -= mPreviousGcCollection;
-        garbageCollectionTime -= mPreviousGcTime;
-
-        StringBuilder builder = new StringBuilder();
-        builder.append("Total Garbage Collections: ");
-        builder.append(totalGarbageCollections);
-        builder.append("\n");
-
-        builder.append("Total Garbage Collection Time (ms): ");
-        builder.append(garbageCollectionTime);
-        builder.append("\n");
-
-        return builder.toString();
-    }
-
-    @Override
-    public String getStatistic() {
-        StringBuilder builder = new StringBuilder();
-
-        builder.append(calculateGcStatAndReturn());
-        builder.append("Memory\n");
-        builder.append(" requested total         : ");
-        builder.append(mRequestedTotalBytes / 1_000_000);
-        builder.append(" MB\n");
-        builder.append(" allocated (in pool)     : ");
-        builder.append(mAllocateTotalBytes / 1_000_000);
-        builder.append(" MB\n");
-        builder.append(" allocated (out of pool) : ");
-        builder.append(mAllocatedOutsidePoolBytes / 1_000_000);
-        builder.append(" MB\n");
-
-        double percent = (1.0 - (double) mRequestedTotalBytes / (mAllocateTotalBytes +
-                mAllocatedOutsidePoolBytes));
-        if (percent < 0.0) {
-            builder.append(" saved : ");
-            builder.append(-1.0 * percent);
-            builder.append("%\n");
-        } else {
-            builder.append(" wasting : ");
-            builder.append(percent);
-            builder.append("%\n");
-        }
-
-        builder.append("Undispose images\n");
-        Multiset<String> countSet = HashMultiset.create();
-        for (String callsite : mCallStack.values()) {
-            countSet.add(callsite);
-        }
-
-        for (Multiset.Entry<String> entry : countSet.entrySet()) {
-            builder.append(" - ");
-            builder.append(entry.getElement());
-            builder.append(" - missed dispose : ");
-            builder.append(entry.getCount());
-            builder.append(" times\n");
-        }
-
-        builder.append("Number of times requested image didn't fit the pool : ");
-        builder.append(mTooBigForPoolCount);
-        builder.append("\n");
-
-        return builder.toString();
-    }
-}
diff --git a/bridge/src/android/util/imagepool/ImagePoolStatsProdImpl.java b/bridge/src/android/util/imagepool/ImagePoolStatsProdImpl.java
deleted file mode 100644
index e6c6abe..0000000
--- a/bridge/src/android/util/imagepool/ImagePoolStatsProdImpl.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2018 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.imagepool;
-
-import com.android.tools.layoutlib.annotations.NotNull;
-import com.android.tools.layoutlib.annotations.VisibleForTesting;
-
-class ImagePoolStatsProdImpl implements ImagePoolStats {
-
-    static int ESTIMATED_PIXEL_BYTES = 4;
-
-    // Used for determining how many buckets can be created.
-    @VisibleForTesting long mAllocateTotalBytes = 0;
-    @VisibleForTesting int mTooBigForPoolCount = 0;
-
-    /** Used for policy */
-    @Override
-    public void recordBucketCreation(int widthBucket, int heightBucket) {
-        mAllocateTotalBytes += (widthBucket * heightBucket * ESTIMATED_PIXEL_BYTES);
-    }
-
-    @Override
-    public boolean fitsMaxCacheSize(int width, int height, long maxCacheSize) {
-        long newTotal = mAllocateTotalBytes + (width * height * ESTIMATED_PIXEL_BYTES);
-        return newTotal <= maxCacheSize;
-    }
-
-    @Override
-    public void tooBigForCache() {
-        mTooBigForPoolCount++;
-    }
-
-    @Override
-    public void clear() {
-        mAllocateTotalBytes = 0;
-    }
-
-    @Override
-    public void recordBucketRequest(int w, int h) { }
-
-    @Override
-    public void recordAllocOutsidePool(int width, int height) { }
-
-    @Override
-    public void acquiredImage(@NotNull Integer imageHash) { }
-
-    @Override
-    public void disposeImage(@NotNull Integer imageHash) { }
-
-    @Override
-    public void start() { }
-
-    @Override
-    public String getStatistic() { return ""; }
-}
diff --git a/bridge/src/android/view/AttachInfo_Accessor.java b/bridge/src/android/view/AttachInfo_Accessor.java
index 60c13c0..5f7588e 100644
--- a/bridge/src/android/view/AttachInfo_Accessor.java
+++ b/bridge/src/android/view/AttachInfo_Accessor.java
@@ -17,28 +17,44 @@
 package android.view;
 
 import android.content.Context;
+import android.graphics.HardwareRenderer;
+import android.graphics.RenderNode;
 import android.os.Handler;
 import android.view.View.AttachInfo;
 
-import com.android.layoutlib.bridge.util.ReflectionUtils;
+import com.android.layoutlib.common.util.ReflectionUtils;
 
 /**
  * Class allowing access to package-protected methods/fields.
  */
 public class AttachInfo_Accessor {
 
-    public static void setAttachInfo(View view) {
+    public static void setAttachInfo(ViewGroup view, HardwareRenderer renderer) {
         Context context = view.getContext();
-        WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
+        WindowManagerImpl wm = (WindowManagerImpl)context.getSystemService(Context.WINDOW_SERVICE);
+        wm.setBaseRootView(view);
         Display display = wm.getDefaultDisplay();
         ViewRootImpl root = new ViewRootImpl(context, display);
+        root.mAttachInfo.mThreadedRenderer = new ThreadedRenderer(context, false,
+                "delegate-renderer") {
+            @Override
+            public void registerAnimatingRenderNode(RenderNode animator) {
+                if (renderer != null) {
+                    renderer.registerAnimatingRenderNode(animator);
+                } else {
+                    super.registerAnimatingRenderNode(animator);
+                }
+            }
+        };
         AttachInfo info = new AttachInfo(ReflectionUtils.createProxy(IWindowSession.class),
                 ReflectionUtils.createProxy(IWindow.class), display, root, new Handler(), null,
                 context);
         info.mHasWindowFocus = true;
         info.mWindowVisibility = View.VISIBLE;
         info.mInTouchMode = false; // this is so that we can display selections.
-        info.mHardwareAccelerated = false;
+        info.mHardwareAccelerated = true;
+        // We do not use this one at all, it is only needed to satisfy null checks in View
+        info.mThreadedRenderer = new ThreadedRenderer(context, false, "layoutlib-renderer");
         view.dispatchAttachedToWindow(info, 0);
     }
 
@@ -46,9 +62,21 @@
         view.mAttachInfo.mTreeObserver.dispatchOnPreDraw();
     }
 
-    public static void detachFromWindow(View view) {
+    public static void detachFromWindow(final View view) {
         if (view != null) {
+            final View.AttachInfo attachInfo = view.mAttachInfo;
             view.dispatchDetachedFromWindow();
+            if (attachInfo != null) {
+                final ThreadedRenderer threadedRenderer = attachInfo.mThreadedRenderer;
+                if(threadedRenderer != null) {
+                    threadedRenderer.destroy();
+                }
+                ThreadedRenderer rootRenderer =
+                        attachInfo.mViewRootImpl.mAttachInfo.mThreadedRenderer;
+                if (rootRenderer != null) {
+                    rootRenderer.destroy();
+                }
+            }
         }
     }
 
diff --git a/bridge/src/android/view/BridgeInflater.java b/bridge/src/android/view/BridgeInflater.java
index dfb46f2..842bade 100644
--- a/bridge/src/android/view/BridgeInflater.java
+++ b/bridge/src/android/view/BridgeInflater.java
@@ -16,7 +16,7 @@
 
 package android.view;
 
-import com.android.SdkConstants;
+import com.android.ide.common.rendering.api.AndroidConstants;
 import com.android.ide.common.rendering.api.ILayoutLog;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.MergeCookie;
@@ -31,7 +31,7 @@
 import com.android.layoutlib.bridge.android.support.DrawerLayoutUtil;
 import com.android.layoutlib.bridge.android.support.RecyclerViewUtil;
 import com.android.layoutlib.bridge.impl.ParserFactory;
-import com.android.layoutlib.bridge.util.ReflectionUtils;
+import com.android.layoutlib.common.util.ReflectionUtils;
 import com.android.tools.layoutlib.annotations.NotNull;
 import com.android.tools.layoutlib.annotations.Nullable;
 
@@ -495,7 +495,7 @@
             // By default, ViewStub will be set to GONE and won't be inflate. If the XML has the
             // tools:visibility attribute we'll workaround that behavior.
             String visibility = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI,
-                    SdkConstants.ATTR_VISIBILITY);
+                    AndroidConstants.ATTR_VISIBILITY);
 
             boolean isVisible = "visible".equals(visibility);
             if (isVisible || "invisible".equals(visibility)) {
diff --git a/bridge/src/android/view/Choreographer_Delegate.java b/bridge/src/android/view/Choreographer_Delegate.java
index 3a8839f..0b6a9e1 100644
--- a/bridge/src/android/view/Choreographer_Delegate.java
+++ b/bridge/src/android/view/Choreographer_Delegate.java
@@ -15,13 +15,18 @@
  */
 package android.view;
 
+import com.android.ide.common.rendering.api.ILayoutLog;
+import com.android.internal.lang.System_Delegate;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.impl.RenderAction;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import com.android.tools.layoutlib.annotations.Nullable;
 
-import android.animation.AnimationHandler;
-import android.util.TimeUtils;
-import android.view.animation.AnimationUtils;
+import java.lang.StackWalker.StackFrame;
+import java.util.Optional;
 
-import java.util.concurrent.atomic.AtomicReference;
+import static com.android.layoutlib.bridge.impl.RenderAction.getCurrentContext;
 
 /**
  * Delegate used to provide new implementation of a select few methods of {@link Choreographer}
@@ -31,65 +36,80 @@
  *
  */
 public class Choreographer_Delegate {
-    private static final AtomicReference<Choreographer> mInstance = new AtomicReference<Choreographer>();
-
-    private static final int MS_16 = 16000000;
-
-    @LayoutlibDelegate
-    public static Choreographer getInstance() {
-        if (mInstance.get() == null) {
-            mInstance.compareAndSet(null, Choreographer.getInstance_Original());
-        }
-
-        return mInstance.get();
-    }
-
     @LayoutlibDelegate
     public static float getRefreshRate() {
         return 60.f;
     }
 
     @LayoutlibDelegate
-    static void scheduleVsyncLocked(Choreographer thisChoreographer) {
-        // do nothing
-    }
-
-    public static void doFrame(long frameTimeNanos) {
-        Choreographer thisChoreographer = Choreographer.getInstance();
-
-        AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
-
-        try {
-            thisChoreographer.mLastFrameTimeNanos = frameTimeNanos - thisChoreographer.getFrameIntervalNanos();
-            thisChoreographer.mFrameInfo.markInputHandlingStart();
-            thisChoreographer.doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos, MS_16);
-
-            thisChoreographer.mFrameInfo.markAnimationsStart();
-            thisChoreographer.doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos, MS_16);
-
-            thisChoreographer.mFrameInfo.markPerformTraversalsStart();
-            thisChoreographer.doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos, MS_16);
-
-            thisChoreographer.doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos, MS_16);
-        } finally {
-            AnimationUtils.unlockAnimationClock();
+    public static void postCallbackDelayedInternal(
+            Choreographer thiz, int callbackType, Object action, Object token, long delayMillis) {
+        BridgeContext context = getCurrentContext();
+        if (context == null) {
+            if (!Thread.currentThread().getName().equals("kotlinx.coroutines.DefaultExecutor")) {
+                return;
+            }
+            ClassLoader moduleClassLoader = findCallingClassLoader();
+            if (moduleClassLoader == null) {
+                return;
+            }
+            context = RenderAction.findContextFor(moduleClassLoader);
+            if (context == null) {
+                return;
+            }
         }
+        if (callbackType != Choreographer.CALLBACK_ANIMATION) {
+            // Ignore non-animation callbacks
+            return;
+        }
+        if (action == null) {
+            Bridge.getLog().error(ILayoutLog.TAG_BROKEN,
+                    "Callback with null action", (Object) null, null);
+        }
+        context.getSessionInteractiveData().getChoreographerCallbacks().add(action, delayMillis);
     }
 
-    public static void clearFrames() {
-        Choreographer thisChoreographer = Choreographer.getInstance();
-
-        thisChoreographer.removeCallbacks(Choreographer.CALLBACK_INPUT, null, null);
-        thisChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, null, null);
-        thisChoreographer.removeCallbacks(Choreographer.CALLBACK_TRAVERSAL, null, null);
-        thisChoreographer.removeCallbacks(Choreographer.CALLBACK_COMMIT, null, null);
-
-        // Release animation handler instance since it holds references to the callbacks
-        AnimationHandler.sAnimatorHandler.set(null);
+    @LayoutlibDelegate
+    public static void removeCallbacksInternal(
+            Choreographer thiz, int callbackType, Object action, Object token) {
+        BridgeContext context = getCurrentContext();
+        if (context == null) {
+            return;
+        }
+        if (callbackType != Choreographer.CALLBACK_ANIMATION) {
+            // Ignore non-animation callbacks
+            return;
+        }
+        if (action == null) {
+            Bridge.getLog().error(ILayoutLog.TAG_BROKEN,
+                    "Callback with null action", (Object) null, null);
+        }
+        context.getSessionInteractiveData().getChoreographerCallbacks().remove(action);
     }
 
-    public static void dispose() {
-        clearFrames();
-        Choreographer.releaseInstance();
+    @LayoutlibDelegate
+    public static long getFrameTimeNanos(Choreographer thiz) {
+        return System.nanoTime();
+    }
+
+    /**
+     * With this method we are trying to find a child ClassLoader that calls this method. We assume
+     * that the child ClassLoader is the first ClassLoader in the callstack that is different from
+     * the current one.
+     */
+    @Nullable
+    private static ClassLoader findCallingClassLoader() {
+        final ClassLoader current = Choreographer_Delegate.class.getClassLoader();
+        StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
+        try {
+            return walker.walk(stackFrameStream -> {
+                Optional<StackFrame> stackFrame = stackFrameStream
+                        .filter(sf -> sf.getDeclaringClass().getClassLoader() != current)
+                        .findFirst();
+                return stackFrame.map(f -> f.getDeclaringClass().getClassLoader()).orElse(null);
+            });
+        } catch (Throwable ex) {
+            return null;
+        }
     }
 }
diff --git a/bridge/src/android/view/DisplayEventReceiver_VsyncEventData_Accessor.java b/bridge/src/android/view/DisplayEventReceiver_VsyncEventData_Accessor.java
new file mode 100644
index 0000000..a72ea79
--- /dev/null
+++ b/bridge/src/android/view/DisplayEventReceiver_VsyncEventData_Accessor.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import android.view.DisplayEventReceiver.VsyncEventData;
+
+public class DisplayEventReceiver_VsyncEventData_Accessor {
+    private static VsyncEventData sVsyncEventData;
+
+    @NotNull
+    public static VsyncEventData getVsyncEventDataInstance() {
+        if (sVsyncEventData == null) {
+            sVsyncEventData = new VsyncEventData();
+        }
+        return sVsyncEventData;
+    }
+}
diff --git a/bridge/src/android/view/RectShadowPainter.java b/bridge/src/android/view/RectShadowPainter.java
deleted file mode 100644
index 88771a7..0000000
--- a/bridge/src/android/view/RectShadowPainter.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import com.android.layoutlib.bridge.impl.GcSnapshot;
-import com.android.layoutlib.bridge.impl.ResourceHelper;
-
-import android.graphics.BaseCanvas_Delegate;
-import android.graphics.Canvas;
-import android.graphics.Canvas_Delegate;
-import android.graphics.Color;
-import android.graphics.LinearGradient;
-import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.Paint.Style;
-import android.graphics.Path;
-import android.graphics.Path.FillType;
-import android.graphics.RadialGradient;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region.Op;
-import android.graphics.Shader.TileMode;
-
-import java.awt.Rectangle;
-
-/**
- * Paints shadow for rounded rectangles. Inspiration from CardView. Couldn't use that directly,
- * since it modifies the size of the content, that we can't do.
- */
-public class RectShadowPainter {
-
-
-    private static final int START_COLOR = ResourceHelper.getColor("#37000000");
-    private static final int END_COLOR = ResourceHelper.getColor("#03000000");
-    private static final float PERPENDICULAR_ANGLE = 90f;
-
-    public static void paintShadow(Outline viewOutline, float elevation, Canvas canvas,
-            float alpha) {
-        Rect outline = new Rect();
-        if (!viewOutline.getRect(outline)) {
-            assert false : "Outline is not a rect shadow";
-            return;
-        }
-
-        // TODO replacing the algorithm here to create better shadow
-
-        float shadowSize = elevationToShadow(elevation);
-        int saved = modifyCanvas(canvas, shadowSize);
-        if (saved == -1) {
-            return;
-        }
-
-        float radius = viewOutline.getRadius();
-        if (radius <= 0) {
-            // We can not paint a shadow with radius 0
-            return;
-        }
-
-        try {
-            Paint cornerPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
-            cornerPaint.setStyle(Style.FILL);
-            Paint edgePaint = new Paint(cornerPaint);
-            edgePaint.setAntiAlias(false);
-            float outerArcRadius = radius + shadowSize;
-            int[] colors = {START_COLOR, START_COLOR, END_COLOR};
-            if (alpha != 1f) {
-                // Correct colors using the given component alpha
-                for (int i = 0; i < colors.length; i++) {
-                    colors[i] = Color.argb((int) (Color.alpha(colors[i]) * alpha), Color.red(colors[i]),
-                            Color.green(colors[i]), Color.blue(colors[i]));
-                }
-            }
-            cornerPaint.setShader(new RadialGradient(0, 0, outerArcRadius, colors,
-                    new float[]{0f, radius / outerArcRadius, 1f}, TileMode.CLAMP));
-            edgePaint.setShader(new LinearGradient(0, 0, -shadowSize, 0, colors[0], colors[2],
-                    TileMode.CLAMP));
-            Path path = new Path();
-            path.setFillType(FillType.EVEN_ODD);
-            // A rectangle bounding the complete shadow.
-            RectF shadowRect = new RectF(outline);
-            shadowRect.inset(-shadowSize, -shadowSize);
-            // A rectangle with edges corresponding to the straight edges of the outline.
-            RectF inset = new RectF(outline);
-            inset.inset(radius, radius);
-            // A rectangle used to represent the edge shadow.
-            RectF edgeShadowRect = new RectF();
-
-
-            // left and right sides.
-            edgeShadowRect.set(-shadowSize, 0f, 0f, inset.height());
-            // Left shadow
-            sideShadow(canvas, edgePaint, edgeShadowRect, outline.left, inset.top, 0);
-            // Right shadow
-            sideShadow(canvas, edgePaint, edgeShadowRect, outline.right, inset.bottom, 2);
-            // Top shadow
-            edgeShadowRect.set(-shadowSize, 0, 0, inset.width());
-            sideShadow(canvas, edgePaint, edgeShadowRect, inset.right, outline.top, 1);
-            // bottom shadow. This needs an inset so that blank doesn't appear when the content is
-            // moved up.
-            edgeShadowRect.set(-shadowSize, 0, shadowSize / 2f, inset.width());
-            edgePaint.setShader(new LinearGradient(edgeShadowRect.right, 0, edgeShadowRect.left, 0,
-                    colors, new float[]{0f, 1 / 3f, 1f}, TileMode.CLAMP));
-            sideShadow(canvas, edgePaint, edgeShadowRect, inset.left, outline.bottom, 3);
-
-            // Draw corners.
-            drawCorner(canvas, cornerPaint, path, inset.right, inset.bottom, outerArcRadius, 0);
-            drawCorner(canvas, cornerPaint, path, inset.left, inset.bottom, outerArcRadius, 1);
-            drawCorner(canvas, cornerPaint, path, inset.left, inset.top, outerArcRadius, 2);
-            drawCorner(canvas, cornerPaint, path, inset.right, inset.top, outerArcRadius, 3);
-        } finally {
-            canvas.restoreToCount(saved);
-        }
-    }
-
-    private static float elevationToShadow(float elevation) {
-        // The factor is chosen by eyeballing the shadow size on device and preview.
-        return elevation * 0.5f;
-    }
-
-    /**
-     * Translate canvas by half of shadow size up, so that it appears that light is coming
-     * slightly from above. Also, remove clipping, so that shadow is not clipped.
-     */
-    private static int modifyCanvas(Canvas canvas, float shadowSize) {
-        Rect clipBounds = canvas.getClipBounds();
-        if (clipBounds.isEmpty()) {
-            return -1;
-        }
-        int saved = canvas.save();
-        // Usually canvas has been translated to the top left corner of the view when this is
-        // called. So, setting a clip rect at 0,0 will clip the top left part of the shadow.
-        // Thus, we just expand in each direction by width and height of the canvas, while staying
-        // inside the original drawing region.
-        GcSnapshot snapshot = Canvas_Delegate.getDelegate(canvas).getSnapshot();
-        Rectangle originalClip = snapshot.getOriginalClip();
-        if (originalClip != null) {
-            canvas.clipRect(originalClip.x, originalClip.y, originalClip.x + originalClip.width,
-              originalClip.y + originalClip.height, Op.REPLACE);
-            canvas.clipRect(-canvas.getWidth(), -canvas.getHeight(), canvas.getWidth(),
-              canvas.getHeight(), Op.INTERSECT);
-        }
-        canvas.translate(0, shadowSize / 2f);
-        return saved;
-    }
-
-    private static void sideShadow(Canvas canvas, Paint edgePaint,
-            RectF edgeShadowRect, float dx, float dy, int rotations) {
-        if (isRectEmpty(edgeShadowRect)) {
-            return;
-        }
-        int saved = canvas.save();
-        canvas.translate(dx, dy);
-        canvas.rotate(rotations * PERPENDICULAR_ANGLE);
-        canvas.drawRect(edgeShadowRect, edgePaint);
-        canvas.restoreToCount(saved);
-    }
-
-    /**
-     * @param canvas Canvas to draw the rectangle on.
-     * @param paint Paint to use when drawing the corner.
-     * @param path A path to reuse. Prevents allocating memory for each path.
-     * @param x Center of circle, which this corner is a part of.
-     * @param y Center of circle, which this corner is a part of.
-     * @param radius radius of the arc
-     * @param rotations number of quarter rotations before starting to paint the arc.
-     */
-    private static void drawCorner(Canvas canvas, Paint paint, Path path, float x, float y,
-            float radius, int rotations) {
-        int saved = canvas.save();
-        canvas.translate(x, y);
-        path.reset();
-        path.arcTo(-radius, -radius, radius, radius, rotations * PERPENDICULAR_ANGLE,
-                PERPENDICULAR_ANGLE, false);
-        path.lineTo(0, 0);
-        path.close();
-        canvas.drawPath(path, paint);
-        canvas.restoreToCount(saved);
-    }
-
-    /**
-     * Differs from {@link RectF#isEmpty()} as this first converts the rect to int and then checks.
-     * <p/>
-     * This is required because {@link BaseCanvas_Delegate#native_drawRect(long, float, float,
-     * float,
-     * float, long)} casts the co-ordinates to int and we want to ensure that it doesn't end up
-     * drawing empty rectangles, which results in IllegalArgumentException.
-     */
-    private static boolean isRectEmpty(RectF rect) {
-        return (int) rect.left >= (int) rect.right || (int) rect.top >= (int) rect.bottom;
-    }
-}
diff --git a/bridge/src/android/view/ShadowPainter.java b/bridge/src/android/view/ShadowPainter.java
deleted file mode 100644
index 788c6c3..0000000
--- a/bridge/src/android/view/ShadowPainter.java
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * Copyright (C) 2014 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 android.annotation.NonNull;
-
-import java.awt.Graphics2D;
-import java.awt.Image;
-import java.awt.image.BufferedImage;
-import java.awt.image.DataBufferInt;
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.imageio.ImageIO;
-
-public class ShadowPainter {
-
-    /**
-     * Adds a drop shadow to a semi-transparent image (of an arbitrary shape) and returns it as a
-     * new image. This method attempts to mimic the same visual characteristics as the rectangular
-     * shadow painting methods in this class, {@link #createRectangularDropShadow(java.awt.image.BufferedImage)}
-     * and {@link #createSmallRectangularDropShadow(java.awt.image.BufferedImage)}.
-     * <p/>
-     * If shadowSize is less or equals to 1, no shadow will be painted and the source image will be
-     * returned instead.
-     *
-     * @param source the source image
-     * @param shadowSize the size of the shadow, normally {@link #SHADOW_SIZE or {@link
-     * #SMALL_SHADOW_SIZE}}
-     * @param alpha alpha value to apply to the shadow
-     *
-     * @return an image with the shadow painted in or the source image if shadowSize <= 1
-     */
-    @NonNull
-    public static BufferedImage createDropShadow(BufferedImage source, int shadowSize, float
-            alpha) {
-        shadowSize /= 2; // make shadow size have the same meaning as in the other shadow paint methods in this class
-
-        return createDropShadow(source, shadowSize, 0.7f * alpha, 0);
-    }
-
-    /**
-     * Creates a drop shadow of a given image and returns a new image which shows the input image on
-     * top of its drop shadow.
-     * <p/>
-     * <b>NOTE: If the shape is rectangular and opaque, consider using {@link
-     * #drawRectangleShadow(Graphics2D, int, int, int, int)} instead.</b>
-     *
-     * @param source the source image to be shadowed
-     * @param shadowSize the size of the shadow in pixels
-     * @param shadowOpacity the opacity of the shadow, with 0=transparent and 1=opaque
-     * @param shadowRgb the RGB int to use for the shadow color
-     *
-     * @return a new image with the source image on top of its shadow when shadowSize > 0 or the
-     * source image otherwise
-     */
-    @SuppressWarnings({"SuspiciousNameCombination", "UnnecessaryLocalVariable"})  // Imported code
-    public static BufferedImage createDropShadow(BufferedImage source, int shadowSize,
-            float shadowOpacity, int shadowRgb) {
-        if (shadowSize <= 0) {
-            return source;
-        }
-
-        // This code is based on
-        //      http://www.jroller.com/gfx/entry/non_rectangular_shadow
-
-        BufferedImage image;
-        int width = source.getWidth();
-        int height = source.getHeight();
-        image = new BufferedImage(width + SHADOW_SIZE, height + SHADOW_SIZE,
-                BufferedImage.TYPE_INT_ARGB);
-
-        Graphics2D g2 = image.createGraphics();
-        g2.drawImage(image, shadowSize, shadowSize, null);
-
-        int dstWidth = image.getWidth();
-        int dstHeight = image.getHeight();
-
-        int left = (shadowSize - 1) >> 1;
-        int right = shadowSize - left;
-        int xStart = left;
-        int xStop = dstWidth - right;
-        int yStart = left;
-        int yStop = dstHeight - right;
-
-        shadowRgb &= 0x00FFFFFF;
-
-        int[] aHistory = new int[shadowSize];
-        int historyIdx;
-
-        int aSum;
-
-        int[] dataBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
-        int lastPixelOffset = right * dstWidth;
-        float sumDivider = shadowOpacity / shadowSize;
-
-        // horizontal pass
-        for (int y = 0, bufferOffset = 0; y < dstHeight; y++, bufferOffset = y * dstWidth) {
-            aSum = 0;
-            historyIdx = 0;
-            for (int x = 0; x < shadowSize; x++, bufferOffset++) {
-                int a = dataBuffer[bufferOffset] >>> 24;
-                aHistory[x] = a;
-                aSum += a;
-            }
-
-            bufferOffset -= right;
-
-            for (int x = xStart; x < xStop; x++, bufferOffset++) {
-                int a = (int) (aSum * sumDivider);
-                dataBuffer[bufferOffset] = a << 24 | shadowRgb;
-
-                // subtract the oldest pixel from the sum
-                aSum -= aHistory[historyIdx];
-
-                // get the latest pixel
-                a = dataBuffer[bufferOffset + right] >>> 24;
-                aHistory[historyIdx] = a;
-                aSum += a;
-
-                if (++historyIdx >= shadowSize) {
-                    historyIdx -= shadowSize;
-                }
-            }
-        }
-        // vertical pass
-        for (int x = 0, bufferOffset = 0; x < dstWidth; x++, bufferOffset = x) {
-            aSum = 0;
-            historyIdx = 0;
-            for (int y = 0; y < shadowSize; y++, bufferOffset += dstWidth) {
-                int a = dataBuffer[bufferOffset] >>> 24;
-                aHistory[y] = a;
-                aSum += a;
-            }
-
-            bufferOffset -= lastPixelOffset;
-
-            for (int y = yStart; y < yStop; y++, bufferOffset += dstWidth) {
-                int a = (int) (aSum * sumDivider);
-                dataBuffer[bufferOffset] = a << 24 | shadowRgb;
-
-                // subtract the oldest pixel from the sum
-                aSum -= aHistory[historyIdx];
-
-                // get the latest pixel
-                a = dataBuffer[bufferOffset + lastPixelOffset] >>> 24;
-                aHistory[historyIdx] = a;
-                aSum += a;
-
-                if (++historyIdx >= shadowSize) {
-                    historyIdx -= shadowSize;
-                }
-            }
-        }
-
-        g2.drawImage(source, null, 0, 0);
-        g2.dispose();
-
-        return image;
-    }
-
-    /**
-     * Draws a rectangular drop shadow (of size {@link #SHADOW_SIZE} by {@link #SHADOW_SIZE} around
-     * the given source and returns a new image with both combined
-     *
-     * @param source the source image
-     *
-     * @return the source image with a drop shadow on the bottom and right
-     */
-    @SuppressWarnings("UnusedDeclaration")
-    public static BufferedImage createRectangularDropShadow(BufferedImage source) {
-        int type = source.getType();
-        if (type == BufferedImage.TYPE_CUSTOM) {
-            type = BufferedImage.TYPE_INT_ARGB;
-        }
-
-        int width = source.getWidth();
-        int height = source.getHeight();
-        BufferedImage image;
-        image = new BufferedImage(width + SHADOW_SIZE, height + SHADOW_SIZE, type);
-        Graphics2D g = image.createGraphics();
-        g.drawImage(source, 0, 0, null);
-        drawRectangleShadow(image, 0, 0, width, height);
-        g.dispose();
-
-        return image;
-    }
-
-    /**
-     * Draws a small rectangular drop shadow (of size {@link #SMALL_SHADOW_SIZE} by {@link
-     * #SMALL_SHADOW_SIZE} around the given source and returns a new image with both combined
-     *
-     * @param source the source image
-     *
-     * @return the source image with a drop shadow on the bottom and right
-     */
-    @SuppressWarnings("UnusedDeclaration")
-    public static BufferedImage createSmallRectangularDropShadow(BufferedImage source) {
-        int type = source.getType();
-        if (type == BufferedImage.TYPE_CUSTOM) {
-            type = BufferedImage.TYPE_INT_ARGB;
-        }
-
-        int width = source.getWidth();
-        int height = source.getHeight();
-
-        BufferedImage image;
-        image = new BufferedImage(width + SMALL_SHADOW_SIZE, height + SMALL_SHADOW_SIZE, type);
-
-        Graphics2D g = image.createGraphics();
-        g.drawImage(source, 0, 0, null);
-        drawSmallRectangleShadow(image, 0, 0, width, height);
-        g.dispose();
-
-        return image;
-    }
-
-    /**
-     * Draws a drop shadow for the given rectangle into the given context. It will not draw anything
-     * if the rectangle is smaller than a minimum determined by the assets used to draw the shadow
-     * graphics. The size of the shadow is {@link #SHADOW_SIZE}.
-     *
-     * @param image the image to draw the shadow into
-     * @param x the left coordinate of the left hand side of the rectangle
-     * @param y the top coordinate of the top of the rectangle
-     * @param width the width of the rectangle
-     * @param height the height of the rectangle
-     */
-    public static void drawRectangleShadow(BufferedImage image,
-            int x, int y, int width, int height) {
-        Graphics2D gc = image.createGraphics();
-        try {
-            drawRectangleShadow(gc, x, y, width, height);
-        } finally {
-            gc.dispose();
-        }
-    }
-
-    /**
-     * Draws a small drop shadow for the given rectangle into the given context. It will not draw
-     * anything if the rectangle is smaller than a minimum determined by the assets used to draw the
-     * shadow graphics. The size of the shadow is {@link #SMALL_SHADOW_SIZE}.
-     *
-     * @param image the image to draw the shadow into
-     * @param x the left coordinate of the left hand side of the rectangle
-     * @param y the top coordinate of the top of the rectangle
-     * @param width the width of the rectangle
-     * @param height the height of the rectangle
-     */
-    public static void drawSmallRectangleShadow(BufferedImage image,
-            int x, int y, int width, int height) {
-        Graphics2D gc = image.createGraphics();
-        try {
-            drawSmallRectangleShadow(gc, x, y, width, height);
-        } finally {
-            gc.dispose();
-        }
-    }
-
-    /**
-     * The width and height of the drop shadow painted by
-     * {@link #drawRectangleShadow(Graphics2D, int, int, int, int)}
-     */
-    public static final int SHADOW_SIZE = 20; // DO NOT EDIT. This corresponds to bitmap graphics
-
-    /**
-     * The width and height of the drop shadow painted by
-     * {@link #drawSmallRectangleShadow(Graphics2D, int, int, int, int)}
-     */
-    public static final int SMALL_SHADOW_SIZE = 10; // DO NOT EDIT. Corresponds to bitmap graphics
-
-    /**
-     * Draws a drop shadow for the given rectangle into the given context. It will not draw anything
-     * if the rectangle is smaller than a minimum determined by the assets used to draw the shadow
-     * graphics.
-     *
-     * @param gc the graphics context to draw into
-     * @param x the left coordinate of the left hand side of the rectangle
-     * @param y the top coordinate of the top of the rectangle
-     * @param width the width of the rectangle
-     * @param height the height of the rectangle
-     */
-    public static void drawRectangleShadow(Graphics2D gc, int x, int y, int width, int height) {
-        assert ShadowBottomLeft != null;
-        assert ShadowBottomRight.getWidth(null) == SHADOW_SIZE;
-        assert ShadowBottomRight.getHeight(null) == SHADOW_SIZE;
-
-        int blWidth = ShadowBottomLeft.getWidth(null);
-        int trHeight = ShadowTopRight.getHeight(null);
-        if (width < blWidth) {
-            return;
-        }
-        if (height < trHeight) {
-            return;
-        }
-
-        gc.drawImage(ShadowBottomLeft, x - ShadowBottomLeft.getWidth(null), y + height, null);
-        gc.drawImage(ShadowBottomRight, x + width, y + height, null);
-        gc.drawImage(ShadowTopRight, x + width, y, null);
-        gc.drawImage(ShadowTopLeft, x - ShadowTopLeft.getWidth(null), y, null);
-        gc.drawImage(ShadowBottom,
-                x, y + height, x + width, y + height + ShadowBottom.getHeight(null),
-                0, 0, ShadowBottom.getWidth(null), ShadowBottom.getHeight(null), null);
-        gc.drawImage(ShadowRight,
-                x + width, y + ShadowTopRight.getHeight(null), x + width + ShadowRight.getWidth(null), y + height,
-                0, 0, ShadowRight.getWidth(null), ShadowRight.getHeight(null), null);
-        gc.drawImage(ShadowLeft,
-                x - ShadowLeft.getWidth(null), y + ShadowTopLeft.getHeight(null), x, y + height,
-                0, 0, ShadowLeft.getWidth(null), ShadowLeft.getHeight(null), null);
-    }
-
-    /**
-     * Draws a small drop shadow for the given rectangle into the given context. It will not draw
-     * anything if the rectangle is smaller than a minimum determined by the assets used to draw the
-     * shadow graphics.
-     * <p/>
-     *
-     * @param gc the graphics context to draw into
-     * @param x the left coordinate of the left hand side of the rectangle
-     * @param y the top coordinate of the top of the rectangle
-     * @param width the width of the rectangle
-     * @param height the height of the rectangle
-     */
-    public static void drawSmallRectangleShadow(Graphics2D gc, int x, int y, int width,
-            int height) {
-        assert Shadow2BottomLeft != null;
-        assert Shadow2TopRight != null;
-        assert Shadow2BottomRight.getWidth(null) == SMALL_SHADOW_SIZE;
-        assert Shadow2BottomRight.getHeight(null) == SMALL_SHADOW_SIZE;
-
-        int blWidth = Shadow2BottomLeft.getWidth(null);
-        int trHeight = Shadow2TopRight.getHeight(null);
-        if (width < blWidth) {
-            return;
-        }
-        if (height < trHeight) {
-            return;
-        }
-
-        gc.drawImage(Shadow2BottomLeft, x - Shadow2BottomLeft.getWidth(null), y + height, null);
-        gc.drawImage(Shadow2BottomRight, x + width, y + height, null);
-        gc.drawImage(Shadow2TopRight, x + width, y, null);
-        gc.drawImage(Shadow2TopLeft, x - Shadow2TopLeft.getWidth(null), y, null);
-        gc.drawImage(Shadow2Bottom,
-                x, y + height, x + width, y + height + Shadow2Bottom.getHeight(null),
-                0, 0, Shadow2Bottom.getWidth(null), Shadow2Bottom.getHeight(null), null);
-        gc.drawImage(Shadow2Right,
-                x + width, y + Shadow2TopRight.getHeight(null), x + width + Shadow2Right.getWidth(null), y + height,
-                0, 0, Shadow2Right.getWidth(null), Shadow2Right.getHeight(null), null);
-        gc.drawImage(Shadow2Left,
-                x - Shadow2Left.getWidth(null), y + Shadow2TopLeft.getHeight(null), x, y + height,
-                0, 0, Shadow2Left.getWidth(null), Shadow2Left.getHeight(null), null);
-    }
-
-    private static Image loadIcon(String name) {
-        InputStream inputStream = ShadowPainter.class.getResourceAsStream(name);
-        if (inputStream == null) {
-            throw new RuntimeException("Unable to load image for shadow: " + name);
-        }
-        try {
-            return ImageIO.read(inputStream);
-        } catch (IOException e) {
-            throw new RuntimeException("Unable to load image for shadow:" + name, e);
-        } finally {
-            try {
-                inputStream.close();
-            } catch (IOException e) {
-                // ignore.
-            }
-        }
-    }
-
-    // Shadow graphics. This was generated by creating a drop shadow in
-    // Gimp, using the parameters x offset=10, y offset=10, blur radius=10,
-    // (for the small drop shadows x offset=10, y offset=10, blur radius=10)
-    // color=black, and opacity=51. These values attempt to make a shadow
-    // that is legible both for dark and light themes, on top of the
-    // canvas background (rgb(150,150,150). Darker shadows would tend to
-    // blend into the foreground for a dark holo screen, and lighter shadows
-    // would be hard to spot on the canvas background. If you make adjustments,
-    // make sure to check the shadow with both dark and light themes.
-    //
-    // After making the graphics, I cut out the top right, bottom left
-    // and bottom right corners as 20x20 images, and these are reproduced by
-    // painting them in the corresponding places in the target graphics context.
-    // I then grabbed a single horizontal gradient line from the middle of the
-    // right edge,and a single vertical gradient line from the bottom. These
-    // are then painted scaled/stretched in the target to fill the gaps between
-    // the three corner images.
-    //
-    // Filenames: bl=bottom left, b=bottom, br=bottom right, r=right, tr=top right
-
-    // Normal Drop Shadow
-    private static final Image ShadowBottom = loadIcon("/icons/shadow-b.png");
-    private static final Image ShadowBottomLeft = loadIcon("/icons/shadow-bl.png");
-    private static final Image ShadowBottomRight = loadIcon("/icons/shadow-br.png");
-    private static final Image ShadowRight = loadIcon("/icons/shadow-r.png");
-    private static final Image ShadowTopRight = loadIcon("/icons/shadow-tr.png");
-    private static final Image ShadowTopLeft = loadIcon("/icons/shadow-tl.png");
-    private static final Image ShadowLeft = loadIcon("/icons/shadow-l.png");
-
-    // Small Drop Shadow
-    private static final Image Shadow2Bottom = loadIcon("/icons/shadow2-b.png");
-    private static final Image Shadow2BottomLeft = loadIcon("/icons/shadow2-bl.png");
-    private static final Image Shadow2BottomRight = loadIcon("/icons/shadow2-br.png");
-    private static final Image Shadow2Right = loadIcon("/icons/shadow2-r.png");
-    private static final Image Shadow2TopRight = loadIcon("/icons/shadow2-tr.png");
-    private static final Image Shadow2TopLeft = loadIcon("/icons/shadow2-tl.png");
-    private static final Image Shadow2Left = loadIcon("/icons/shadow2-l.png");
-}
diff --git a/bridge/src/android/view/TextureView_Delegate.java b/bridge/src/android/view/TextureView_Delegate.java
new file mode 100644
index 0000000..0a2abe1
--- /dev/null
+++ b/bridge/src/android/view/TextureView_Delegate.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.graphics.TextureLayer;
+
+public class TextureView_Delegate {
+    @LayoutlibDelegate
+    static TextureLayer getTextureLayer(TextureView thisTextureView) {
+        /*
+         * Currently layoutlib does not support TextureLayers (no OpenGL)
+         */
+        return null;
+    }
+}
diff --git a/bridge/src/android/view/ViewGroup_Delegate.java b/bridge/src/android/view/ViewGroup_Delegate.java
deleted file mode 100644
index e71af61..0000000
--- a/bridge/src/android/view/ViewGroup_Delegate.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import com.android.layoutlib.bridge.android.BridgeContext;
-import com.android.resources.Density;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap_Delegate;
-import android.graphics.Canvas;
-import android.graphics.Outline;
-import android.graphics.Path_Delegate;
-import android.graphics.Rect;
-import android.view.animation.Transformation;
-import android.view.shadow.HighQualityShadowPainter;
-
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
-
-/**
- * Delegate used to provide new implementation of a select few methods of {@link ViewGroup}
- * <p/>
- * Through the layoutlib_create tool, the original  methods of ViewGroup have been replaced by calls
- * to methods of the same name in this delegate class.
- */
-public class ViewGroup_Delegate {
-
-    /**
-     * Overrides the original drawChild call in ViewGroup to draw the shadow.
-     */
-    @LayoutlibDelegate
-    /*package*/ static boolean drawChild(ViewGroup thisVG, Canvas canvas, View child,
-            long drawingTime) {
-        if (child.getZ() > thisVG.getZ()) {
-            // The background's bounds are set lazily. Make sure they are set correctly so that
-            // the outline obtained is correct.
-            child.setBackgroundBounds();
-            ViewOutlineProvider outlineProvider = child.getOutlineProvider();
-            if (outlineProvider != null) {
-                Outline outline = child.mAttachInfo.mTmpOutline;
-                outlineProvider.getOutline(child, outline);
-                if (outline.mPath != null || (outline.mRect != null && !outline.mRect.isEmpty())) {
-                    int restoreTo = transformCanvas(thisVG, canvas, child);
-                    drawShadow(thisVG, canvas, child, outline);
-                    canvas.restoreToCount(restoreTo);
-                }
-            }
-        }
-        return thisVG.drawChild_Original(canvas, child, drawingTime);
-    }
-
-    private static void drawShadow(ViewGroup parent, Canvas canvas, View child,
-            Outline outline) {
-        boolean highQualityShadow = false;
-        boolean enableShadow = true;
-        float elevation = getElevation(child, parent);
-        Context bridgeContext = parent.getContext();
-        if (bridgeContext instanceof BridgeContext) {
-            highQualityShadow = ((BridgeContext) bridgeContext).isHighQualityShadows();
-            enableShadow = ((BridgeContext) bridgeContext).isShadowsEnabled();
-        }
-
-        if (!enableShadow) {
-            return;
-        }
-
-        if(outline.mMode == Outline.MODE_ROUND_RECT && outline.mRect != null) {
-            if (highQualityShadow) {
-                float densityDpi = bridgeContext.getResources().getDisplayMetrics().densityDpi;
-                HighQualityShadowPainter.paintRectShadow(
-                        parent, outline, elevation, canvas, child.getAlpha(), densityDpi);
-            } else {
-                RectShadowPainter.paintShadow(outline, elevation, canvas, child.getAlpha());
-            }
-            return;
-        }
-
-        BufferedImage shadow = null;
-        if (outline.mPath != null) {
-            shadow = getPathShadow(outline, canvas, elevation, child.getAlpha());
-        }
-        if (shadow == null) {
-            return;
-        }
-        Bitmap bitmap = Bitmap_Delegate.createBitmap(shadow, false,
-                Density.getEnum(canvas.getDensity()));
-        canvas.save();
-        Rect clipBounds = canvas.getClipBounds();
-        Rect newBounds = new Rect(clipBounds);
-        newBounds.inset((int)-elevation, (int)-elevation);
-        canvas.clipRectUnion(newBounds);
-        canvas.drawBitmap(bitmap, 0, 0, null);
-        canvas.restore();
-    }
-
-    private static float getElevation(View child, ViewGroup parent) {
-        return child.getZ() - parent.getZ();
-    }
-
-    private static BufferedImage getPathShadow(Outline outline, Canvas canvas, float elevation,
-            float alpha) {
-        Rect clipBounds = canvas.getClipBounds();
-        if (clipBounds.isEmpty()) {
-          return null;
-        }
-        BufferedImage image = new BufferedImage(clipBounds.width(), clipBounds.height(),
-                BufferedImage.TYPE_INT_ARGB);
-        Graphics2D graphics = image.createGraphics();
-        graphics.draw(Path_Delegate.getDelegate(outline.mPath.mNativePath).getJavaShape());
-        graphics.dispose();
-        return ShadowPainter.createDropShadow(image, (int) elevation, alpha);
-    }
-
-    // Copied from android.view.View#draw(Canvas, ViewGroup, long) and removed code paths
-    // which were never taken. Ideally, we should hook up the shadow code in the same method so
-    // that we don't have to transform the canvas twice.
-    private static int transformCanvas(ViewGroup thisVG, Canvas canvas, View child) {
-        final int restoreTo = canvas.save();
-        final boolean childHasIdentityMatrix = child.hasIdentityMatrix();
-        int flags = thisVG.mGroupFlags;
-        Transformation transformToApply = null;
-        boolean concatMatrix = false;
-        if ((flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
-            final Transformation t = thisVG.getChildTransformation();
-            final boolean hasTransform = thisVG.getChildStaticTransformation(child, t);
-            if (hasTransform) {
-                final int transformType = t.getTransformationType();
-                transformToApply = transformType != Transformation.TYPE_IDENTITY ? t : null;
-                concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
-            }
-        }
-        concatMatrix |= childHasIdentityMatrix;
-
-        canvas.translate(child.mLeft, child.mTop);
-        float alpha = child.getAlpha() * child.getTransitionAlpha();
-
-        if (transformToApply != null || alpha < 1 || !childHasIdentityMatrix) {
-            if (transformToApply != null || !childHasIdentityMatrix) {
-
-                if (transformToApply != null) {
-                    if (concatMatrix) {
-                        canvas.concat(transformToApply.getMatrix());
-                    }
-                }
-                if (!childHasIdentityMatrix) {
-                    canvas.concat(child.getMatrix());
-                }
-
-            }
-        }
-        return restoreTo;
-    }
-}
diff --git a/bridge/src/android/view/WindowManagerImpl.java b/bridge/src/android/view/WindowManagerImpl.java
index 36b7165..dabeda3 100644
--- a/bridge/src/android/view/WindowManagerImpl.java
+++ b/bridge/src/android/view/WindowManagerImpl.java
@@ -16,12 +16,14 @@
 package android.view;
 
 import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import android.app.ResourcesManager;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -29,8 +31,10 @@
 import android.os.RemoteException;
 import android.util.DisplayMetrics;
 import android.view.Display.Mode;
+import android.widget.FrameLayout;
 
 import com.android.ide.common.rendering.api.ILayoutLog;
+import com.android.internal.R;
 import com.android.layoutlib.bridge.Bridge;
 
 public class WindowManagerImpl implements WindowManager {
@@ -38,6 +42,15 @@
     private final Context mContext;
     private final DisplayMetrics mMetrics;
     private final Display mDisplay;
+    /**
+     * Root view of the base window, new windows will be added on top of this.
+     */
+    private ViewGroup mBaseRootView;
+    /**
+     * Root view of the current window at the top of the display,
+     * null if there is only the base window present.
+     */
+    private ViewGroup mCurrentRootView;
 
     public WindowManagerImpl(Context context, DisplayMetrics metrics) {
         mContext = context;
@@ -55,15 +68,12 @@
     }
 
     public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
-        Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "The preview does not support multiple windows.",
-                null, null, null);
         return this;
     }
 
     public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
         Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
-                "The preview does not support multiple windows.",
+                "The preview does not fully support multiple windows.",
                 null, null, null);
         return this;
     }
@@ -86,12 +96,78 @@
 
     @Override
     public void addView(View arg0, android.view.ViewGroup.LayoutParams arg1) {
-        // pass
+        if (mBaseRootView == null) {
+            return;
+        }
+        if (mCurrentRootView == null) {
+            FrameLayout layout = new FrameLayout(mContext) {
+                @Override
+                public boolean dispatchTouchEvent(MotionEvent ev) {
+                    View baseRootParent = (View)mBaseRootView.getParent();
+                    if (baseRootParent != null) {
+                        ev.offsetLocation(-baseRootParent.getX(), -baseRootParent.getY());
+                    }
+                    return super.dispatchTouchEvent(ev);
+                }
+
+                @Override
+                protected void measureChildWithMargins(View child, int parentWidthMeasureSpec,
+                        int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
+                    // This reproduces ViewRootImpl#measureHierarchy as this FrameLayout should
+                    // be treated as a ViewRoot.
+                    ViewGroup.LayoutParams lp = child.getLayoutParams();
+                    int parentWidth = MeasureSpec.getSize(parentWidthMeasureSpec);
+                    int parentHeight = MeasureSpec.getSize(parentHeightMeasureSpec);
+                    int childWidthMeasureSpec = 0;
+                    int childHeightMeasureSpec = ViewRootImpl.getRootMeasureSpec(parentHeight,
+                            lp.height, 0);
+                    if (lp.width == WRAP_CONTENT) {
+                        int baseSize =
+                                mContext.getResources().getDimensionPixelSize(R.dimen.config_prefDialogWidth);
+                        if (baseSize != 0 && baseSize < parentWidth) {
+                            childWidthMeasureSpec = ViewRootImpl.getRootMeasureSpec(baseSize,
+                                    lp.width, 0);
+                        }
+                    }
+                    if (childWidthMeasureSpec == 0) {
+                        childWidthMeasureSpec = ViewRootImpl.getRootMeasureSpec(parentWidth,
+                                lp.width, 0);
+                    }
+                    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+                }
+            };
+            // The window root view should not handle touch events.
+            // Events need to be dispatched to the base view inside the window,
+            // with coordinates shifted accordingly.
+            layout.setOnTouchListener((v, event) -> {
+                event.offsetLocation(-arg0.getX(), -arg0.getY());
+                return arg0.dispatchTouchEvent(event);
+            });
+            mBaseRootView.addView(layout, new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+                    LayoutParams.MATCH_PARENT));
+            mCurrentRootView = layout;
+        }
+
+        FrameLayout.LayoutParams frameLayoutParams = new FrameLayout.LayoutParams(arg1);
+        if (arg1 instanceof WindowManager.LayoutParams) {
+            LayoutParams params = (LayoutParams) arg1;
+            frameLayoutParams.gravity = params.gravity;
+            if ((params.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) {
+                mCurrentRootView.setBackgroundColor(Color.argb(params.dimAmount, 0, 0, 0));
+            }
+        }
+        mCurrentRootView.addView(arg0, frameLayoutParams);
     }
 
     @Override
     public void removeView(View arg0) {
-        // pass
+        if (mCurrentRootView != null) {
+            mCurrentRootView.removeView(arg0);
+            if (mBaseRootView != null && mCurrentRootView.getChildCount() == 0) {
+                mBaseRootView.removeView(mCurrentRootView);
+                mCurrentRootView = null;
+            }
+        }
     }
 
     @Override
@@ -102,7 +178,7 @@
 
     @Override
     public void removeViewImmediate(View arg0) {
-        // pass
+        removeView(arg0);
     }
 
     @Override
@@ -171,4 +247,33 @@
         }
         return null;
     }
+
+    // ---- Extra methods for layoutlib ----
+
+    public void setBaseRootView(ViewGroup baseRootView) {
+        // If used within Compose Preview, use the ComposeViewAdapter as the root
+        // so that the preview attributes are handled correctly.
+        ViewGroup composableRoot = findComposableRoot(baseRootView);
+        mBaseRootView = composableRoot != null ? composableRoot : baseRootView;
+    }
+
+    private ViewGroup findComposableRoot(ViewGroup baseRootView) {
+        if (baseRootView.getClass().getName().endsWith("ComposeViewAdapter")) {
+            return baseRootView;
+        }
+        for (int i = 0; i < baseRootView.getChildCount(); i++) {
+            View child = baseRootView.getChildAt(i);
+            if (child instanceof ViewGroup) {
+                ViewGroup composableRoot = findComposableRoot((ViewGroup)child);
+                if (composableRoot != null) {
+                    return composableRoot;
+                }
+            }
+        }
+        return null;
+    }
+
+    public ViewGroup getCurrentRootView() {
+        return mCurrentRootView;
+    }
 }
diff --git a/bridge/src/android/view/math/Math3DHelper.java b/bridge/src/android/view/math/Math3DHelper.java
deleted file mode 100644
index 35a1ee1..0000000
--- a/bridge/src/android/view/math/Math3DHelper.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2018 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.math;
-
-public class Math3DHelper {
-
-    private Math3DHelper() { }
-
-    public final static int min(int x1, int x2, int x3) {
-        return (x1 > x2) ? ((x2 > x3) ? x3 : x2) : ((x1 > x3) ? x3 : x1);
-    }
-
-    public final static int max(int x1, int x2, int x3) {
-        return (x1 < x2) ? ((x2 < x3) ? x3 : x2) : ((x1 < x3) ? x3 : x1);
-    }
-
-    /**
-     * @return Rect bound of flattened (ignoring z). LTRB
-     * @param dimension - 2D or 3D
-     */
-    public static float[] flatBound(float[] poly, int dimension) {
-        int polySize = poly.length/dimension;
-        float left = poly[0];
-        float right = poly[0];
-        float top = poly[1];
-        float bottom = poly[1];
-
-        for (int i = 0; i < polySize; i++) {
-            float x = poly[i * dimension + 0];
-            float y = poly[i * dimension + 1];
-
-            if (left > x) {
-                left = x;
-            } else if (right < x) {
-                right = x;
-            }
-
-            if (top > y) {
-                top = y;
-            } else if (bottom < y) {
-                bottom = y;
-            }
-        }
-        return new float[]{left, top, right, bottom};
-    }
-
-    /**
-     * Translate the polygon to x and y
-     * @param dimension in what dimension is polygon represented (supports 2 or 3D).
-     */
-    public static void translate(float[] poly, float translateX, float translateY, int dimension) {
-        int polySize = poly.length/dimension;
-
-        for (int i = 0; i < polySize; i++) {
-            poly[i * dimension + 0] += translateX;
-            poly[i * dimension + 1] += translateY;
-        }
-    }
-
-}
-
diff --git a/bridge/src/android/view/shadow/AmbientShadowConfig.java b/bridge/src/android/view/shadow/AmbientShadowConfig.java
deleted file mode 100644
index 97efd86..0000000
--- a/bridge/src/android/view/shadow/AmbientShadowConfig.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2018 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.shadow;
-
-/**
- * Model for ambient shadow rendering. Assumes light sources from centroid of the object.
- */
-class AmbientShadowConfig {
-
-    private final float mEdgeScale;
-    private final float mShadowBoundRatio;
-    private final float mShadowStrength;
-
-    private final float[] mPolygon;
-    private float[] mLightSourcePosition;
-
-    private AmbientShadowConfig(Builder builder) {
-        mEdgeScale = builder.mEdgeScale;
-        mShadowBoundRatio = builder.mShadowBoundRatio;
-        mShadowStrength = builder.mShadowStrength;
-        mPolygon = builder.mPolygon;
-        mLightSourcePosition = builder.mLightSourcePosition;
-    }
-
-    /**
-     * Returns scales intensity of the edge of the shadow (opacity) [0-100]
-     */
-    public float getEdgeScale() {
-        return mEdgeScale;
-    }
-
-    /**
-     * Returns scales the area (in xy) of the shadow [0-1]
-     */
-    public float getShadowBoundRatio() {
-        return mShadowBoundRatio;
-    }
-
-    /**
-     * Returns scales the intensity of the entire shadow (opacity) [0-1]
-     */
-    public float getShadowStrength() {
-        return mShadowStrength;
-    }
-
-    /**
-     * Returns opaque polygon to cast shadow
-     */
-    public float[] getPolygon() {
-        return mPolygon;
-    }
-
-    /**
-     * Returns 2D position of the light source
-     */
-    public float[] getLightSourcePosition() {
-        return mLightSourcePosition;
-    }
-
-    public static class Builder {
-
-        private float mEdgeScale;
-        private float mShadowBoundRatio;
-        private float mShadowStrength;
-
-        private float[] mPolygon;
-        private float[] mLightSourcePosition;
-
-        public Builder setEdgeScale(float edgeScale) {
-            mEdgeScale = edgeScale;
-            return this;
-        }
-
-        public Builder setShadowBoundRatio(float shadowBoundRatio) {
-            mShadowBoundRatio = shadowBoundRatio;
-            return this;
-        }
-
-        public Builder setShadowStrength(float shadowStrength) {
-            mShadowStrength = shadowStrength;
-            return this;
-        }
-
-        public Builder setPolygon(float[] polygon) {
-            mPolygon = polygon;
-            return this;
-        }
-
-        public Builder setLightSourcePosition(float x, float y) {
-            mLightSourcePosition = new float[] { x, y };
-            return this;
-        }
-
-        public AmbientShadowConfig build() {
-            return new AmbientShadowConfig(this);
-        }
-    }
-}
diff --git a/bridge/src/android/view/shadow/AmbientShadowTriangulator.java b/bridge/src/android/view/shadow/AmbientShadowTriangulator.java
deleted file mode 100644
index d768fa3..0000000
--- a/bridge/src/android/view/shadow/AmbientShadowTriangulator.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2018 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.shadow;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-
-/**
- * Generates ambient shadow bitmap
- */
-class AmbientShadowTriangulator {
-
-    private final AmbientShadowConfig mShadowConfig;
-    private final AmbientShadowVertexCalculator mCalculator;
-
-    private boolean mValid;
-
-    public AmbientShadowTriangulator(AmbientShadowConfig shadowConfig) {
-        mShadowConfig = shadowConfig;
-
-        mCalculator = new AmbientShadowVertexCalculator(mShadowConfig);
-    }
-
-    /**
-     * Populate vertices and fill the triangle buffers.
-     */
-    public void triangulate() {
-        try {
-            mCalculator.generateVertex();
-            mValid = true;
-        } catch (IndexOutOfBoundsException|ArithmeticException mathError) {
-            Bridge.getLog().warning(ILayoutLog.TAG_INFO,  "Arithmetic error while drawing " +
-                            "ambient shadow",
-                    null, mathError);
-        } catch (Exception ex) {
-            Bridge.getLog().warning(ILayoutLog.TAG_INFO,  "Error while drawing shadow",
-                    null, ex);
-        }
-    }
-
-    public boolean isValid() {
-        return mValid;
-    }
-
-    public float[] getVertices() { return mCalculator.getVertex(); }
-
-    public int[] getIndices() { return mCalculator.getIndex(); }
-
-    public float[] getColors() { return mCalculator.getColor(); }
-}
diff --git a/bridge/src/android/view/shadow/AmbientShadowVertexCalculator.java b/bridge/src/android/view/shadow/AmbientShadowVertexCalculator.java
deleted file mode 100644
index 5928f35..0000000
--- a/bridge/src/android/view/shadow/AmbientShadowVertexCalculator.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2018 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.shadow;
-
-import android.view.math.Math3DHelper;
-
-/**
- * Generates vertices, colours, and indices required for ambient shadow. Ambient shadows are
- * assumed to be raycasted from the centroid of the polygon, and reaches upto a ratio based on
- * the polygon's z-height.
- */
-class AmbientShadowVertexCalculator {
-
-    private final float[] mVertex;
-    private final float[] mColor;
-    private final int[] mIndex;
-    private final AmbientShadowConfig mConfig;
-
-    public AmbientShadowVertexCalculator(AmbientShadowConfig config) {
-        mConfig = config;
-
-        int size = mConfig.getPolygon().length / 3;
-
-        mVertex = new float[size * 2 * 2];
-        mColor = new float[size * 2 * 4];
-        mIndex = new int[(size * 3 - 2) * 3];
-    }
-
-    /**
-     * Generates ambient shadow triangulation using configuration provided
-     */
-    public void generateVertex() {
-        float[] polygon = mConfig.getPolygon();
-        float cx = mConfig.getLightSourcePosition()[0];
-        float cy = mConfig.getLightSourcePosition()[1];
-
-        int polygonLength = polygon.length/3;
-
-        float opacity = .8f * (0.5f / (mConfig.getEdgeScale() / 10f));
-
-        int trShift = 0;
-        for (int i = 0; i < polygonLength; ++i, trShift += 6) {
-            int shift = i * 4;
-            int colorShift = i * 8;
-            int idxShift = i * 2;
-
-            float px = polygon[3 * i + 0];
-            float py = polygon[3 * i + 1];
-            mVertex[shift + 0] = px;
-            mVertex[shift + 1] = py;
-
-            // TODO: I do not really understand this but this matches the previous behavior.
-            // The weird bit is that for outlines with low elevation the ambient shadow is
-            // entirely drawn underneath the shadow caster. This is most probably incorrect
-            float h = polygon[3 * i + 2] * mConfig.getShadowBoundRatio();
-
-            mVertex[shift + 2] = cx + h * (px - cx);
-            mVertex[shift + 3] = cy + h * (py - cy);
-
-            mColor[colorShift + 3] = opacity;
-
-            mIndex[trShift + 0] = idxShift + 0;
-            mIndex[trShift + 1] = idxShift + 1;
-            mIndex[trShift + 2] = idxShift + 2;
-
-            mIndex[trShift + 3] = idxShift + 1;
-            mIndex[trShift + 4] = idxShift + 2;
-            mIndex[trShift + 5] = idxShift + 3;
-        }
-        // cycle back to the front
-        mIndex[trShift - 1] = 1;
-        mIndex[trShift - 2] = 0;
-        mIndex[trShift - 4] = 0;
-
-        // Filling the shadow right under the outline. Can we skip that?
-        for (int i = 1; i < polygonLength - 1; ++i, trShift += 3) {
-            mIndex[trShift + 0] = 0;
-            mIndex[trShift + 1] = 2 * i;
-            mIndex[trShift + 2] = 2 * (i+1);
-        }
-    }
-
-    public int[] getIndex() {
-        return mIndex;
-    }
-
-    /**
-     * @return list of vertices in 2d in format : {x1, y1, x2, y2 ...}
-     */
-    public float[] getVertex() {
-        return mVertex;
-    }
-
-    public float[] getColor() {
-        return mColor;
-    }
-}
diff --git a/bridge/src/android/view/shadow/HighQualityShadowPainter.java b/bridge/src/android/view/shadow/HighQualityShadowPainter.java
deleted file mode 100644
index 7b8873c..0000000
--- a/bridge/src/android/view/shadow/HighQualityShadowPainter.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (C) 2018 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.shadow;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.util.DisplayMetrics;
-import android.view.ViewGroup;
-import android.view.math.Math3DHelper;
-
-import static android.view.shadow.ShadowConstants.MIN_ALPHA;
-import static android.view.shadow.ShadowConstants.SCALE_DOWN;
-
-public class HighQualityShadowPainter {
-    private static final float sRoundedGap = (float) (1.0 - Math.sqrt(2.0) / 2.0);
-
-    private HighQualityShadowPainter() { }
-
-    /**
-     * Draws simple Rect shadow
-     */
-    public static void paintRectShadow(ViewGroup parent, Outline outline, float elevation,
-            Canvas canvas, float alpha, float densityDpi) {
-
-        if (!validate(elevation, densityDpi)) {
-            return;
-        }
-
-        int width = parent.getWidth() / SCALE_DOWN;
-        int height = parent.getHeight() / SCALE_DOWN;
-
-        Rect rectOriginal = new Rect();
-        Rect rectScaled = new Rect();
-        if (!outline.getRect(rectScaled) || alpha < MIN_ALPHA) {
-            // If alpha below MIN_ALPHA it's invisible (based on manual test). Save some perf.
-            return;
-        }
-
-        outline.getRect(rectOriginal);
-
-        rectScaled.left /= SCALE_DOWN;
-        rectScaled.right /= SCALE_DOWN;
-        rectScaled.top /= SCALE_DOWN;
-        rectScaled.bottom /= SCALE_DOWN;
-        float radius = outline.getRadius() / SCALE_DOWN;
-
-        if (radius > rectScaled.width() || radius > rectScaled.height()) {
-            // Rounded edge generation fails if radius is bigger than drawing box.
-            return;
-        }
-
-        // ensure alpha doesn't go over 1
-        alpha = (alpha > 1.0f) ? 1.0f : alpha;
-        boolean isOpaque = outline.getAlpha() * alpha == 1.0f;
-        float[] poly = getPoly(rectScaled, elevation / SCALE_DOWN, radius);
-
-        AmbientShadowConfig ambientConfig = new AmbientShadowConfig.Builder()
-                .setPolygon(poly)
-                .setLightSourcePosition(
-                        (rectScaled.left + rectScaled.right) / 2.0f,
-                        (rectScaled.top + rectScaled.bottom) / 2.0f)
-                .setEdgeScale(ShadowConstants.AMBIENT_SHADOW_EDGE_SCALE)
-                .setShadowBoundRatio(ShadowConstants.AMBIENT_SHADOW_SHADOW_BOUND)
-                .setShadowStrength(ShadowConstants.AMBIENT_SHADOW_STRENGTH * alpha)
-                .build();
-
-        AmbientShadowTriangulator ambientTriangulator = new AmbientShadowTriangulator(ambientConfig);
-        ambientTriangulator.triangulate();
-
-        SpotShadowTriangulator spotTriangulator = null;
-        float lightZHeightPx = ShadowConstants.SPOT_SHADOW_LIGHT_Z_HEIGHT_DP * (densityDpi / DisplayMetrics.DENSITY_DEFAULT);
-        if (lightZHeightPx - elevation / SCALE_DOWN >= ShadowConstants.SPOT_SHADOW_LIGHT_Z_EPSILON) {
-
-            float lightX = (rectScaled.left + rectScaled.right) / 2;
-            float lightY = rectScaled.top;
-            // Light shouldn't be bigger than the object by too much.
-            int dynamicLightRadius = Math.min(rectScaled.width(), rectScaled.height());
-
-            SpotShadowConfig spotConfig = new SpotShadowConfig.Builder()
-                    .setLightCoord(lightX, lightY, lightZHeightPx)
-                    .setLightRadius(dynamicLightRadius)
-                    .setShadowStrength(ShadowConstants.SPOT_SHADOW_STRENGTH * alpha)
-                    .setPolygon(poly, poly.length / ShadowConstants.COORDINATE_SIZE)
-                    .build();
-
-            spotTriangulator = new SpotShadowTriangulator(spotConfig);
-            spotTriangulator.triangulate();
-        }
-
-        int translateX = 0;
-        int translateY = 0;
-        int imgW = 0;
-        int imgH = 0;
-
-        if (ambientTriangulator.isValid()) {
-            float[] shadowBounds = Math3DHelper.flatBound(ambientTriangulator.getVertices(), 2);
-            // Move the shadow to the left top corner to occupy the least possible bitmap
-
-            translateX = -(int) Math.floor(shadowBounds[0]);
-            translateY = -(int) Math.floor(shadowBounds[1]);
-
-            // create bitmap of the least possible size that covers the entire shadow
-            imgW = (int) Math.ceil(shadowBounds[2] + translateX);
-            imgH = (int) Math.ceil(shadowBounds[3] + translateY);
-        }
-
-        if (spotTriangulator != null && spotTriangulator.validate()) {
-
-            // Bit of a hack to re-adjust spot shadow to fit correctly within parent canvas.
-            // Problem is that outline passed is not a final position, which throws off our
-            // whereas our shadow rendering algorithm, which requires pre-set range for
-            // optimization purposes.
-            float[] shadowBounds = Math3DHelper.flatBound(spotTriangulator.getStrips()[0], 3);
-
-            if ((shadowBounds[2] - shadowBounds[0]) > width ||
-                    (shadowBounds[3] - shadowBounds[1]) > height) {
-                // Spot shadow to be casted is larger than the parent canvas,
-                // We'll let ambient shadow do the trick and skip spot shadow here.
-                spotTriangulator = null;
-            }
-
-            translateX = Math.max(-(int) Math.floor(shadowBounds[0]), translateX);
-            translateY = Math.max(-(int) Math.floor(shadowBounds[1]), translateY);
-
-            // create bitmap of the least possible size that covers the entire shadow
-            imgW = Math.max((int) Math.ceil(shadowBounds[2] + translateX), imgW);
-            imgH = Math.max((int) Math.ceil(shadowBounds[3] + translateY), imgH);
-        }
-
-        TriangleBuffer renderer = new TriangleBuffer();
-        renderer.setSize(imgW, imgH, 0);
-
-        if (ambientTriangulator.isValid()) {
-
-            Math3DHelper.translate(ambientTriangulator.getVertices(), translateX, translateY, 2);
-            renderer.drawTriangles(ambientTriangulator.getIndices(), ambientTriangulator.getVertices(),
-                    ambientTriangulator.getColors(), ambientConfig.getShadowStrength());
-        }
-
-        if (spotTriangulator != null && spotTriangulator.validate()) {
-            float[][] strips = spotTriangulator.getStrips();
-            for (int i = 0; i < strips.length; ++i) {
-                Math3DHelper.translate(strips[i], translateX, translateY, 3);
-                renderer.drawTriangles(strips[i], ShadowConstants.SPOT_SHADOW_STRENGTH * alpha);
-            }
-        }
-
-        Bitmap img = renderer.createImage();
-
-        drawScaled(canvas, img, translateX, translateY, rectOriginal, radius, isOpaque);
-    }
-
-    /**
-     * High quality shadow does not work well with object that is too high in elevation. Check if
-     * the object elevation is reasonable and returns true if shadow will work well. False other
-     * wise.
-     */
-    private static boolean validate(float elevation, float densityDpi) {
-        float scaledElevationPx = elevation / SCALE_DOWN;
-        float scaledSpotLightHeightPx = ShadowConstants.SPOT_SHADOW_LIGHT_Z_HEIGHT_DP *
-                (densityDpi / DisplayMetrics.DENSITY_DEFAULT);
-        if (scaledElevationPx > scaledSpotLightHeightPx) {
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Draw the bitmap scaled up.
-     * @param translateX - offset in x axis by which the bitmap is shifted.
-     * @param translateY - offset in y axis by which the bitmap is shifted.
-     * @param shadowCaster - unscaled outline of shadow caster
-     * @param radius
-     */
-    private static void drawScaled(Canvas canvas, Bitmap bitmap, int translateX, int translateY,
-            Rect shadowCaster, float radius, boolean isOpaque) {
-        int unscaledTranslateX = translateX * SCALE_DOWN;
-        int unscaledTranslateY = translateY * SCALE_DOWN;
-
-        // To the canvas
-        Rect dest = new Rect(
-                -unscaledTranslateX,
-                -unscaledTranslateY,
-                (bitmap.getWidth() * SCALE_DOWN) - unscaledTranslateX,
-                (bitmap.getHeight() * SCALE_DOWN) - unscaledTranslateY);
-        Rect destSrc = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
-        // We can skip drawing the shadows behind the caster if either
-        // 1) radius is 0, the shadow caster is rectangle and we can have a perfect cut
-        // 2) shadow caster is opaque and even if remove shadow only partially it won't affect
-        // the visual quality, otherwise we will observe shadow part through the translucent caster
-        // This can be improved by:
-        // TODO: do not draw the shadow behind the caster at all during the tesselation phase
-        if (radius > 0 && !isOpaque) {
-            // Rounded edge.
-            int save = canvas.save();
-            canvas.drawBitmap(bitmap, destSrc, dest, null);
-            canvas.restoreToCount(save);
-            return;
-        }
-
-        /**
-         * ----------------------------------
-         * |                                |
-         * |              top               |
-         * |                                |
-         * ----------------------------------
-         * |      |                 |       |
-         * | left |  shadow caster  | right |
-         * |      |                 |       |
-         * ----------------------------------
-         * |                                |
-         * |            bottom              |
-         * |                                |
-         * ----------------------------------
-         *
-         * dest == top + left + shadow caster + right + bottom
-         * Visually, canvas.drawBitmap(bitmap, destSrc, dest, paint) would achieve the same result.
-         */
-        int gap = (int) Math.ceil(radius * SCALE_DOWN * sRoundedGap);
-        shadowCaster.bottom -= gap;
-        shadowCaster.top += gap;
-        shadowCaster.left += gap;
-        shadowCaster.right -= gap;
-        Rect left = new Rect(dest.left, shadowCaster.top, shadowCaster.left,
-                shadowCaster.bottom);
-        int leftScaled = left.width() / SCALE_DOWN + destSrc.left;
-
-        Rect top = new Rect(dest.left, dest.top, dest.right, shadowCaster.top);
-        int topScaled = top.height() / SCALE_DOWN + destSrc.top;
-
-        Rect right = new Rect(shadowCaster.right, shadowCaster.top, dest.right,
-                shadowCaster.bottom);
-        int rightScaled = (shadowCaster.right - dest.left) / SCALE_DOWN + destSrc.left;
-
-        Rect bottom = new Rect(dest.left, shadowCaster.bottom, dest.right, dest.bottom);
-        int bottomScaled = (shadowCaster.bottom - dest.top) / SCALE_DOWN + destSrc.top;
-
-        // calculate parts of the middle ground that can be ignored.
-        Rect leftSrc = new Rect(destSrc.left, topScaled, leftScaled, bottomScaled);
-        Rect topSrc = new Rect(destSrc.left, destSrc.top, destSrc.right, topScaled);
-        Rect rightSrc = new Rect(rightScaled, topScaled, destSrc.right, bottomScaled);
-        Rect bottomSrc = new Rect(destSrc.left, bottomScaled, destSrc.right, destSrc.bottom);
-
-        int save = canvas.save();
-        Paint paint = new Paint();
-        canvas.drawBitmap(bitmap, leftSrc, left, paint);
-        canvas.drawBitmap(bitmap, topSrc, top, paint);
-        canvas.drawBitmap(bitmap, rightSrc, right, paint);
-        canvas.drawBitmap(bitmap, bottomSrc, bottom, paint);
-        canvas.restoreToCount(save);
-    }
-
-    private static float[] getPoly(Rect rect, float elevation, float radius) {
-        if (radius <= 0) {
-            float[] poly = new float[ShadowConstants.RECT_VERTICES_SIZE * ShadowConstants.COORDINATE_SIZE];
-
-            poly[0] = poly[9] = rect.left;
-            poly[1] = poly[4] = rect.top;
-            poly[3] = poly[6] = rect.right;
-            poly[7] = poly[10] = rect.bottom;
-            poly[2] = poly[5] = poly[8] = poly[11] = elevation;
-
-            return poly;
-        }
-
-        return buildRoundedEdges(rect, elevation, radius);
-    }
-
-    private static float[] buildRoundedEdges(
-            Rect rect, float elevation, float radius) {
-
-        float[] roundedEdgeVertices = new float[(ShadowConstants.SPLICE_ROUNDED_EDGE + 1) * 4 * 3];
-        int index = 0;
-        // 1.0 LT. From theta 0 to pi/2 in K division.
-        for (int i = 0; i <= ShadowConstants.SPLICE_ROUNDED_EDGE; i++) {
-            double theta = (Math.PI / 2.0d) * ((double) i / ShadowConstants.SPLICE_ROUNDED_EDGE);
-            float x = (float) (rect.left + (radius - radius * Math.cos(theta)));
-            float y = (float) (rect.top + (radius - radius * Math.sin(theta)));
-            roundedEdgeVertices[index++] = x;
-            roundedEdgeVertices[index++] = y;
-            roundedEdgeVertices[index++] = elevation;
-        }
-
-        // 2.0 RT
-        for (int i = ShadowConstants.SPLICE_ROUNDED_EDGE; i >= 0; i--) {
-            double theta = (Math.PI / 2.0d) * ((double) i / ShadowConstants.SPLICE_ROUNDED_EDGE);
-            float x = (float) (rect.right - (radius - radius * Math.cos(theta)));
-            float y = (float) (rect.top + (radius - radius * Math.sin(theta)));
-            roundedEdgeVertices[index++] = x;
-            roundedEdgeVertices[index++] = y;
-            roundedEdgeVertices[index++] = elevation;
-        }
-
-        // 3.0 RB
-        for (int i = 0; i <= ShadowConstants.SPLICE_ROUNDED_EDGE; i++) {
-            double theta = (Math.PI / 2.0d) * ((double) i / ShadowConstants.SPLICE_ROUNDED_EDGE);
-            float x = (float) (rect.right - (radius - radius * Math.cos(theta)));
-            float y = (float) (rect.bottom - (radius - radius * Math.sin(theta)));
-            roundedEdgeVertices[index++] = x;
-            roundedEdgeVertices[index++] = y;
-            roundedEdgeVertices[index++] = elevation;
-        }
-
-        // 4.0 LB
-        for (int i = ShadowConstants.SPLICE_ROUNDED_EDGE; i >= 0; i--) {
-            double theta = (Math.PI / 2.0d) * ((double) i / ShadowConstants.SPLICE_ROUNDED_EDGE);
-            float x = (float) (rect.left + (radius - radius * Math.cos(theta)));
-            float y = (float) (rect.bottom - (radius - radius * Math.sin(theta)));
-            roundedEdgeVertices[index++] = x;
-            roundedEdgeVertices[index++] = y;
-            roundedEdgeVertices[index++] = elevation;
-        }
-
-        return roundedEdgeVertices;
-    }
-}
diff --git a/bridge/src/android/view/shadow/ShadowConstants.java b/bridge/src/android/view/shadow/ShadowConstants.java
deleted file mode 100644
index 049a549..0000000
--- a/bridge/src/android/view/shadow/ShadowConstants.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2018 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.shadow;
-
-/**
- * Constant values for shadow related configuration
- */
-class ShadowConstants {
-
-    /**
-     * This is used as a factor by which to scale down the shadow bitmap. If we have world
-     * Width x Height, shadow bitmap will be Width/SCALE_DOWN x Height/SCALE_DOWN and during
-     * canvas draw the shadow will be scaled up, resulting faster perf (due to smaller bitmap) but
-     * blurrier (lower quality) shadow.
-     */
-    public static final int SCALE_DOWN = 5;
-
-    public static final float MIN_ALPHA = 0.2f;
-
-    public static final int SPOT_SHADOW_LIGHT_Z_HEIGHT_DP = 50 / SCALE_DOWN;
-    public static final int SPOT_SHADOW_LIGHT_Z_EPSILON = 10 / SCALE_DOWN;
-    public static final float SPOT_SHADOW_STRENGTH = 0.3f;
-
-    public static final float AMBIENT_SHADOW_EDGE_SCALE = 60f;
-    public static final float AMBIENT_SHADOW_SHADOW_BOUND = 0.02f * SCALE_DOWN;
-    public static final float AMBIENT_SHADOW_STRENGTH = 1.0f;
-
-    public static final int COORDINATE_SIZE = 3;
-    public static final int RECT_VERTICES_SIZE = 4;
-
-    public static final int SPLICE_ROUNDED_EDGE = 5;
-}
diff --git a/bridge/src/android/view/shadow/SpotShadowConfig.java b/bridge/src/android/view/shadow/SpotShadowConfig.java
deleted file mode 100644
index 7b4fa4b..0000000
--- a/bridge/src/android/view/shadow/SpotShadowConfig.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2018 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.shadow;
-
-
-/**
- * Model for spot shadow rendering. Assumes single light, single object.
- */
-class SpotShadowConfig {
-
-    // No need to be final but making it immutable for now.
-    private final int mLightRadius;
-
-
-    // No need to be final but making it immutable for now.
-    private final float[] mPoly;
-    private final int mPolyLength;
-
-    private float[] mLightCoord;
-
-    private final float mShadowStrength;
-
-    private SpotShadowConfig(SpotShadowConfig.Builder builder) {
-        mLightRadius = builder.mLightRadius;
-        mPoly = builder.mPoly;
-        mPolyLength = builder.mPolyLength;
-
-        mLightCoord = new float[3];
-        mLightCoord[0] = builder.mLightX;
-        mLightCoord[1] = builder.mLightY;
-        mLightCoord[2] = builder.mLightHeight;
-        mShadowStrength = builder.mShadowStrength;
-    }
-
-    /**
-     * @return size of the light source radius (light source is always generated as a circular shape)
-     */
-    public int getLightRadius() {
-        return mLightRadius;
-    }
-
-    /**
-     * @return object that casts shadow. xyz coordinates.
-     */
-    public float[] getPoly() {
-        return mPoly;
-    }
-
-    /**
-     * @return # of vertices in the object {@link #getPoly()} that casts shadow.
-     */
-    public int getPolyLength() {
-        return mPolyLength;
-    }
-
-    /**
-     * Update the light source coord.
-     * @param x - horizontal coordinate
-     * @param y - vertical coordinate
-     */
-    public void setLightCoord(float x, float y) {
-        mLightCoord[0] = x;
-        mLightCoord[1] = y;
-    }
-
-    /**
-     * @return shadow intensity from 0 to 1
-     */
-    public float getShadowStrength() {
-        return mShadowStrength;
-    }
-
-    public float[] getLightCoord() {
-        return mLightCoord;
-    }
-
-    public static class Builder {
-
-        // No need to be final but making it immutable for now.
-        private int mLightRadius;
-
-        // No need to be final but making it immutable for now.
-        private float[] mPoly;
-        private int mPolyLength;
-
-        private float mLightX;
-        private float mLightY;
-        private float mLightHeight;
-
-        private float mShadowStrength;
-
-        /**
-         * @param shadowStrength from 0 to 1
-         */
-        public Builder setShadowStrength(float shadowStrength) {
-            this.mShadowStrength = shadowStrength;
-            return this;
-        }
-
-        public Builder setLightRadius(int mLightRadius) {
-            this.mLightRadius = mLightRadius;
-            return this;
-        }
-
-        public Builder setPolygon(float[] poly, int polyLength) {
-            this.mPoly = poly;
-            this.mPolyLength = polyLength;
-            return this;
-        }
-
-        public Builder setLightCoord(float lightX, float lightY, float lightHeight) {
-            this.mLightX = lightX;
-            this.mLightY = lightY;
-            this.mLightHeight = lightHeight;
-            return this;
-        }
-
-        public SpotShadowConfig build() {
-            return new SpotShadowConfig(this);
-        }
-    }
-
-}
\ No newline at end of file
diff --git a/bridge/src/android/view/shadow/SpotShadowTriangulator.java b/bridge/src/android/view/shadow/SpotShadowTriangulator.java
deleted file mode 100644
index d666153..0000000
--- a/bridge/src/android/view/shadow/SpotShadowTriangulator.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2018 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.shadow;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-
-/**
- * Generate spot shadow bitmap.
- */
-class SpotShadowTriangulator {
-
-    private final SpotShadowConfig mShadowConfig;
-    private float[][] mStrips;
-
-    public SpotShadowTriangulator(SpotShadowConfig config) {
-        mShadowConfig = config;
-    }
-
-    /**
-     * Populate the shadow bitmap.
-     */
-    public void triangulate() {
-        try {
-            float[] lightSources =
-                    SpotShadowVertexCalculator.calculateLight(mShadowConfig.getLightRadius(),
-                            mShadowConfig.getLightCoord()[0],
-                            mShadowConfig.getLightCoord()[1], mShadowConfig.getLightCoord()[2]);
-
-
-            mStrips = new float[2][];
-            int[] sizes = SpotShadowVertexCalculator.getStripSizes(mShadowConfig.getPolyLength());
-            for (int i = 0; i < sizes.length; ++i) {
-                mStrips[i] = new float[3 * sizes[i]];
-            }
-
-            SpotShadowVertexCalculator.calculateShadow(lightSources,
-                    mShadowConfig.getPoly(),
-                    mShadowConfig.getPolyLength(),
-                    mShadowConfig.getShadowStrength(),
-                    mStrips);
-        } catch (IndexOutOfBoundsException|ArithmeticException mathError) {
-            Bridge.getLog().warning(ILayoutLog.TAG_INFO,  "Arithmetic error while drawing " +
-                            "spot shadow",
-                    null, mathError);
-        } catch (Exception ex) {
-            Bridge.getLog().warning(ILayoutLog.TAG_INFO,  "Error while drawing shadow",
-                    null, ex);
-        }
-    }
-    /**
-     * @return true if generated shadow poly is valid. False otherwise.
-     */
-    public boolean validate() {
-        return mStrips != null && mStrips[0].length >= 9;
-    }
-
-    public float[][] getStrips() {
-        return mStrips;
-    }
-}
diff --git a/bridge/src/android/view/shadow/SpotShadowVertexCalculator.java b/bridge/src/android/view/shadow/SpotShadowVertexCalculator.java
deleted file mode 100644
index fc02d18..0000000
--- a/bridge/src/android/view/shadow/SpotShadowVertexCalculator.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2018 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.shadow;
-
-import android.graphics.Rect;
-import android.view.math.Math3DHelper;
-
-/**
- * Generates the vertices required for spot shadow and all other shadow-related rendering.
- */
-class SpotShadowVertexCalculator {
-
-    private SpotShadowVertexCalculator() { }
-
-    /**
-     * Create evenly distributed circular light source points from x and y (on flat z plane).
-     * This is useful for ray tracing the shadow points later. Format : (x1,y1,z1,x2,y2,z2 ...)
-     *
-     * @param radius - radius of the light source
-     * @param x - center X of the light source
-     * @param y - center Y of the light source
-     * @param height - how high (z depth) the light should be
-     * @return float points (x,y,z) of light source points.
-     */
-    public static float[] calculateLight(float radius, float x, float y, float height) {
-        float[] ret = new float[4 * 3];
-        // bottom
-        ret[0] = x;
-        ret[1] = y + radius;
-        ret[2] = height;
-        // left
-        ret[3] = x - radius;
-        ret[4] = y;
-        ret[5] = height;
-        // top
-        ret[6] = x;
-        ret[7] = y - radius;
-        ret[8] = height;
-        // right
-        ret[9] = x + radius;
-        ret[10] = y;
-        ret[11] = height;
-
-        return ret;
-    }
-
-    /**
-     * @param polyLength - length of the outline polygon
-     * @return size required for shadow vertices mData array based on # of vertices in the
-     * outline polygon
-     */
-    public static int[] getStripSizes(int polyLength){
-        return new int[] { ((polyLength + 4) / 8) * 16 + 2, 4};
-    }
-
-    /**
-     * Generate shadow vertices based on params. Format : (x1,y1,z1,x2,y2,z2 ...)
-     * Precondition : Light poly must be evenly distributed on a flat surface
-     * Precondition : Poly vertices must be a convex
-     * Precondition : Light height must be higher than any poly vertices
-     *
-     * @param lightPoly - Vertices of a light source.
-     * @param poly - Vertices of opaque object casting shadow
-     * @param polyLength - Size of the vertices
-     * @param strength - Strength of the shadow overall [0-1]
-     * @param retstrips - Arrays of triplets, each triplet represents a point, thus every array to
-     * be filled in format : {x1, y1, z1, x2, y2, z2, ...},
-     * every 3 consecutive triplets constitute a triangle to fill, namely [t1, t2, t3], [t2, t3,
-     * t4], ... If at some point [t(n-1), tn, t(n+1)] is no longer a desired a triangle and
-     * there are more triangles to draw one can start a new array, hence retstrips is a 2D array.
-     */
-    public static void calculateShadow(
-            float[] lightPoly,
-            float[] poly,
-            int polyLength,
-            float strength,
-            float[][] retstrips) {
-        float[] outerStrip = retstrips[0];
-
-        // We would like to unify the cases where we have roundrects and rectangles
-        int roundedEdgeSegments = ((polyLength == 4) ? 0 : ShadowConstants.SPLICE_ROUNDED_EDGE);
-        int sideLength = (roundedEdgeSegments / 2 + 1) * 2;
-        float[] umbra = new float[4 * 2 * sideLength];
-        int idx = (roundedEdgeSegments + 1) / 2;
-        int uShift = 0;
-        // If we have even number of segments in rounded corner (0 included), the central point of
-        // rounded corner contributes to the hull twice, from 2 different light sources, thus
-        // rollBack in that case, otherwise every point contributes only once
-        int rollBack = (((polyLength % 8) == 0) ? 0 : 1);
-        // Calculate umbra - a hull of all projections
-        for (int s = 0; s < 4; ++s) { // 4 sides
-            float lx = lightPoly[s * 3 + 0];
-            float ly = lightPoly[s * 3 + 1];
-            float lz = lightPoly[s * 3 + 2];
-            for (int i = 0; i < sideLength; ++i, uShift += 2, ++idx) {
-                int shift = (idx % polyLength) * 3;
-
-                float t = lz / (lz - poly[shift + 2]);
-
-                umbra[uShift + 0] = lx - t * (lx - poly[shift + 0]);
-                umbra[uShift + 1] = ly - t * (ly - poly[shift + 1]);
-            }
-
-            idx -= rollBack;
-        }
-
-        idx = roundedEdgeSegments;
-        // An array that wil contain top, right, bottom, left coordinate of penumbra
-        float[] penumbraRect = new float[4];
-        // Calculate penumbra
-        for (int s = 0; s < 4; ++s, idx += (roundedEdgeSegments + 1)) { // 4 sides
-            int sp = (s + 2) % 4;
-
-            float lz = lightPoly[sp * 3 + 2];
-
-            int shift = (idx % polyLength) * 3;
-
-            float t = lz / (lz - poly[shift + 2]);
-
-            // We are interested in just one coordinate: x or y, depending on the light source
-            int c = (s + 1) % 2;
-            penumbraRect[s] =
-                    lightPoly[sp * 3 + c] - t * (lightPoly[sp * 3 + c] - poly[shift + c]);
-        }
-        if (penumbraRect[0] > penumbraRect[2]) {
-            float tmp = (penumbraRect[0] + penumbraRect[2]) / 2.0f;
-            penumbraRect[0] = penumbraRect[2] = tmp;
-        }
-        if (penumbraRect[3] > penumbraRect[1]) {
-            float tmp = (penumbraRect[1] + penumbraRect[3]) / 2.0f;
-            penumbraRect[1] = penumbraRect[3] = tmp;
-        }
-
-        // Now just connect umbra points (at least 8 of them) with the closest points from
-        // penumbra (only 4 of them) to form triangles to fill the entire space between umbra and
-        // penumbra
-        idx = sideLength * 4 - sideLength / 2;
-        int rsShift = 0;
-        for (int s = 0; s < 4; ++s) {
-            int xidx = (((s + 3) % 4) / 2) * 2 + 1;
-            int yidx = (s / 2) * 2;
-            float penumbraX = penumbraRect[xidx];
-            float penumbraY = penumbraRect[yidx];
-            for (int i = 0; i < sideLength; ++i, rsShift += 6, ++idx) {
-                int shift = (idx % (sideLength * 4)) * 2;
-
-                outerStrip[rsShift + 0] = umbra[shift + 0];
-                outerStrip[rsShift + 1] = umbra[shift + 1];
-                outerStrip[rsShift + 3] = penumbraX;
-                outerStrip[rsShift + 4] = penumbraY;
-                outerStrip[rsShift + 5] = strength;
-            }
-        }
-        // Connect with the beginning
-        outerStrip[rsShift + 0] = outerStrip[0];
-        outerStrip[rsShift + 1] = outerStrip[1];
-        // outerStrip[rsShift + 2] = 0;
-        outerStrip[rsShift + 3] = outerStrip[3];
-        outerStrip[rsShift + 4] = outerStrip[4];
-        outerStrip[rsShift + 5] = strength;
-
-        float[] innerStrip = retstrips[1];
-        // Covering penumbra rectangle
-        // left, top
-        innerStrip[0] = penumbraRect[3];
-        innerStrip[1] = penumbraRect[0];
-        innerStrip[2] = strength;
-        // right, top
-        innerStrip[3] = penumbraRect[1];
-        innerStrip[4] = penumbraRect[0];
-        innerStrip[5] = strength;
-        // left, bottom
-        innerStrip[6] = penumbraRect[3];
-        innerStrip[7] = penumbraRect[2];
-        innerStrip[8] = strength;
-        // right, bottom
-        innerStrip[9] = penumbraRect[1];
-        innerStrip[10] = penumbraRect[2];
-        innerStrip[11] = strength;
-    }
-}
\ No newline at end of file
diff --git a/bridge/src/android/view/shadow/TriangleBuffer.java b/bridge/src/android/view/shadow/TriangleBuffer.java
deleted file mode 100644
index 3c01171..0000000
--- a/bridge/src/android/view/shadow/TriangleBuffer.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2018 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.shadow;
-
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.view.math.Math3DHelper;
-
-import java.util.Arrays;
-
-import static java.lang.Math.max;
-import static java.lang.Math.min;
-
-/**
- * 2D Triangle buffer element that colours using z value. (z scale set).
- */
-class TriangleBuffer {
-    int mWidth;
-    int mHeight;
-    int mImgWidth;
-    int mImgHeight;
-    int mBorder;
-    Bitmap mBitmap;
-    int mData[];
-
-    public void setSize(int width, int height, int border) {
-        if (mWidth == width && mHeight == height) {
-            return;
-        }
-        mWidth = width-2*border;
-        mHeight = height-2*border;
-        mBorder = border;
-        mImgWidth = width;
-        mImgHeight = height;
-
-        mBitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
-        mData = new int[width * height];
-    }
-
-    public void drawTriangles(int[] index, float[] vert, float[] color,float scale) {
-        int indexSize = index.length / 3;
-        for (int i = 0; i < indexSize; i++) {
-            int vIndex = index[i * 3 + 0];
-            float vx = vert[vIndex * 2 + 0];
-            float vy = vert[vIndex * 2 + 1];
-            float c =  scale*color[vIndex * 4 + 3];
-            float fx3 = vx, fy3 = vy, fz3 = c;
-
-            vIndex = index[i * 3 + 1];
-            vx = vert[vIndex * 2 + 0];
-            vy = vert[vIndex * 2 + 1];
-            c =  scale*color[vIndex * 4 + 3];
-            float fx2 = vx, fy2 = vy, fz2 = c;
-
-            vIndex = index[i * 3 + 2];
-            vx = vert[vIndex * 2 + 0];
-            vy = vert[vIndex * 2 + 1];
-            c =  scale*color[vIndex * 4 + 3];
-            float fx1 = vx, fy1 = vy, fz1 = c;
-
-            triangleZBuffMin(mData, mImgWidth, mImgHeight, fx1, fy1, fz1, fx2, fy2,
-                    fz2, fx3, fy3, fz3);
-        }
-    }
-
-    public void drawTriangles(float[] strip,float scale) {
-        for (int i = 0; i < strip.length-8; i+=3) {
-            float fx3 = strip[i], fy3 = strip[i+1], fz3 = scale* strip[i+2];
-            float fx2 = strip[i+3], fy2 = strip[i+4], fz2 = scale* strip[i+5];
-            float fx1 = strip[i+6], fy1 = strip[i+7], fz1 = scale* strip[i+8];
-
-            if (fx1*(fy2-fy3)+fx2*(fy3-fy1)+fx3*(fy1-fy2) ==0) {
-                continue;
-            }
-            triangleZBuffMin(mData, mImgWidth, mImgHeight, fx3, fy3, fz3, fx2, fy2,
-                    fz2, fx1, fy1, fz1);
-        }
-    }
-
-    public Bitmap createImage() {
-        mBitmap.setPixels(mData, 0, mWidth, 0, 0, mWidth, mHeight);
-        return mBitmap;
-    }
-
-    private static void triangleZBuffMin(int[] buff, int w, int h, float fx3,
-            float fy3, float fz3, float fx2, float fy2, float fz2, float fx1,
-            float fy1, float fz1) {
-        if (((fx1 - fx2) * (fy3 - fy2) - (fy1 - fy2) * (fx3 - fx2)) < 0) {
-            float tmpx = fx1;
-            float tmpy = fy1;
-            float tmpz = fz1;
-            fx1 = fx2;
-            fy1 = fy2;
-            fz1 = fz2;
-            fx2 = tmpx;
-            fy2 = tmpy;
-            fz2 = tmpz;
-        }
-        // using maxmima
-        // solve([x1*dx+y1*dy+zoff=z1,x2*dx+y2*dy+zoff=z2,x3*dx+y3*dy+zoff=z3],[dx,dy,zoff]);
-        double d = (fx1 * (fy3 - fy2) - fx2 * fy3 + fx3 * fy2 + (fx2 - fx3) * fy1);
-        if (d == 0) {
-            return;
-        }
-        float dx = (float) (-(fy1 * (fz3 - fz2) - fy2 * fz3 + fy3 * fz2 + (fy2 - fy3)
-                * fz1) / d);
-        float dy = (float) ((fx1 * (fz3 - fz2) - fx2 * fz3 + fx3 * fz2 + (fx2 - fx3)
-                * fz1) / d);
-        float zoff = (float) ((fx1 * (fy3 * fz2 - fy2 * fz3) + fy1
-                * (fx2 * fz3 - fx3 * fz2) + (fx3 * fy2 - fx2 * fy3) * fz1) / d);
-
-        // 28.4 fixed-point coordinates
-        int y1 = (int) (16.0f * fy1 + .5f);
-        int y2 = (int) (16.0f * fy2 + .5f);
-        int y3 = (int) (16.0f * fy3 + .5f);
-
-        int x1 = (int) (16.0f * fx1 + .5f);
-        int x2 = (int) (16.0f * fx2 + .5f);
-        int x3 = (int) (16.0f * fx3 + .5f);
-
-        int dx12 = x1 - x2;
-        int dx23 = x2 - x3;
-        int dx31 = x3 - x1;
-
-        int dy12 = y1 - y2;
-        int dy23 = y2 - y3;
-        int dy31 = y3 - y1;
-
-        int fdx12 = dx12 << 4;
-        int fdx23 = dx23 << 4;
-        int fdx31 = dx31 << 4;
-
-        int fdy12 = dy12 << 4;
-        int fdy23 = dy23 << 4;
-        int fdy31 = dy31 << 4;
-
-        int minx = (Math3DHelper.min(x1, x2, x3) + 0xF) >> 4;
-        int maxx = (Math3DHelper.max(x1, x2, x3) + 0xF) >> 4;
-        int miny = (Math3DHelper.min(y1, y2, y3) + 0xF) >> 4;
-        int maxy = (Math3DHelper.max(y1, y2, y3) + 0xF) >> 4;
-
-        if (miny < 0) {
-            miny = 0;
-        }
-        if (minx < 0) {
-            minx = 0;
-        }
-        if (maxx > w) {
-            maxx = w;
-        }
-        if (maxy > h) {
-            maxy = h;
-        }
-        int off = miny * w;
-
-        int c1 = dy12 * x1 - dx12 * y1;
-        int c2 = dy23 * x2 - dx23 * y2;
-        int c3 = dy31 * x3 - dx31 * y3;
-
-        if (dy12 < 0 || (dy12 == 0 && dx12 > 0)) {
-            c1++;
-        }
-        if (dy23 < 0 || (dy23 == 0 && dx23 > 0)) {
-            c2++;
-        }
-        if (dy31 < 0 || (dy31 == 0 && dx31 > 0)) {
-            c3++;
-        }
-        int cy1 = c1 + dx12 * (miny << 4) - dy12 * (minx << 4);
-        int cy2 = c2 + dx23 * (miny << 4) - dy23 * (minx << 4);
-        int cy3 = c3 + dx31 * (miny << 4) - dy31 * (minx << 4);
-
-        for (int y = miny; y < maxy; y++) {
-            int cx1 = cy1;
-            int cx2 = cy2;
-            int cx3 = cy3;
-            float p = zoff + dy * y;
-
-            int startx = start(cx1, fdy12, minx, minx, maxx);
-            startx = start(cx2, fdy23, minx, startx, maxx);
-            startx = start(cx3, fdy31, minx, startx, maxx);
-
-            cx1 -= (startx - minx) * fdy12;
-            cx2 -= (startx - minx) * fdy23;
-            cx3 -= (startx - minx) * fdy31;
-
-            int endx = end(cx1, fdy12, startx, minx, maxx);
-            endx = end(cx2, fdy23, startx, minx, endx);
-            endx = end(cx3, fdy31, startx, minx, endx);
-
-            for (int x = startx; x < endx; x++) {
-                int point = x + off;
-                float zval = p + dx * x;
-                // Simple alpha-blending
-                int prev = (buff[point] >> 24) & 0xFF;
-                int res = (int) (zval * (255 - prev )) + prev;
-                buff[point] = res << 24;
-            }
-            cy1 += fdx12;
-            cy2 += fdx23;
-            cy3 += fdx31;
-            off += w;
-        }
-    }
-
-    /**
-     * Returns the minimum value of x in the range [minx, maxx]: y0 - dy * (x - x0) > 0
-     * If no value satisfies the expression, maxx is returned
-     *
-     * @param y0 - value in x0
-     * @param dy - delta y
-     * @param x0 - some position, for which value is known (y0)
-     * @param minx - start of the range
-     * @param maxx - end of the range
-     * @return minimum x
-     */
-    private static int start(int y0, int dy, int x0, int minx, int maxx) {
-        if (y0 > 0) {
-            return minx;
-        }
-        if (dy >= 0) {
-            return maxx;
-        }
-        return max(x0 + y0 / dy + 1, minx);
-    }
-
-    /**
-     * Returns the minimum value of x in range [minx, maxx]: y0 - dy * (x - x0) <= 0
-     * If no value satisfies the expression maxx is returned
-     *
-     * @param y0 - value in x0
-     * @param dy - delta y
-     * @param x0 - some position, for which value is known (y0)
-     * @param minx - start of the range
-     * @param maxx - end of the range
-     * @return minimum x
-     */
-    private static int end(int y0, int dy, int x0, int minx, int maxx) {
-        if (y0 <= 0) {
-            return minx;
-        }
-        if (dy <= 0) {
-            return maxx;
-        }
-        return min(x0 + (y0 - 1) / dy + 1, maxx);
-    }
-
-    public void clear() {
-        Arrays.fill(mData, 0);
-    }
-}
\ No newline at end of file
diff --git a/bridge/src/android/widget/RemoteViews_Delegate.java b/bridge/src/android/widget/RemoteViews_Delegate.java
new file mode 100644
index 0000000..35809d5
--- /dev/null
+++ b/bridge/src/android/widget/RemoteViews_Delegate.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.content.pm.ApplicationInfo;
+
+import static com.android.layoutlib.bridge.impl.RenderAction.getCurrentContext;
+
+public class RemoteViews_Delegate {
+    @LayoutlibDelegate
+    public static ApplicationInfo getApplicationInfo(String packageName, int userId) {
+        return getCurrentContext().getApplicationInfo();
+    }
+}
diff --git a/bridge/src/com/android/internal/lang/System_Delegate.java b/bridge/src/com/android/internal/lang/System_Delegate.java
new file mode 100644
index 0000000..2558989
--- /dev/null
+++ b/bridge/src/com/android/internal/lang/System_Delegate.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.lang;
+
+import com.android.layoutlib.bridge.android.BridgeContext;
+
+import java.util.WeakHashMap;
+import java.util.concurrent.TimeUnit;
+
+import static com.android.layoutlib.bridge.impl.RenderAction.getCurrentContext;
+
+/**
+ * Provides alternative implementations of methods that don't exist on the host VM.
+ * This also providers a time control that allows to set a specific system time.
+ *
+ * @see ReplaceMethodCallsAdapter
+ */
+@SuppressWarnings("unused")
+public class System_Delegate {
+    public static void log(String message) {
+        // ignore.
+    }
+
+    public static void log(String message, Throwable th) {
+        // ignore.
+    }
+
+    public static void setNanosTime(long nanos) {
+        BridgeContext context = getCurrentContext();
+        if (context != null) {
+            context.getSessionInteractiveData().setNanosTime(nanos);
+        }
+    }
+
+    public static void setBootTimeNanos(long nanos) {
+        BridgeContext context = getCurrentContext();
+        if (context != null) {
+            context.getSessionInteractiveData().setBootNanosTime(nanos);
+        }
+    }
+
+    public static long nanoTime() {
+        BridgeContext context = getCurrentContext();
+        if (context != null) {
+            return context.getSessionInteractiveData().getNanosTime();
+        }
+        return 0;
+    }
+
+    public static long currentTimeMillis() {
+        return TimeUnit.NANOSECONDS.toMillis(nanoTime());
+    }
+
+    public static long bootTime() {
+        BridgeContext context = getCurrentContext();
+        if (context != null) {
+            return context.getSessionInteractiveData().getBootNanosTime();
+        }
+        return 0;
+    }
+
+    public static long bootTimeMillis() {
+        return TimeUnit.NANOSECONDS.toMillis(bootTime());
+    }
+
+    // This is no-op since layoutlib infrastructure loads all the native libraries.
+    public static void loadLibrary(String libname) {
+        // ignore.
+    }
+}
diff --git a/bridge/src/com/android/internal/util/ArrayUtils_Delegate.java b/bridge/src/com/android/internal/util/ArrayUtils_Delegate.java
new file mode 100644
index 0000000..f830f0e
--- /dev/null
+++ b/bridge/src/com/android/internal/util/ArrayUtils_Delegate.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import java.util.Arrays;
+
+public class ArrayUtils_Delegate {
+    public static void clearCache() {
+        // Prevent cache from retaining ModuleClassLoader classes, so that when we change
+        // ModuleClassLoader (when rebuilding or using other project/module) we can free the memory.
+        Arrays.fill(ArrayUtils.sCache, null);
+    }
+}
diff --git a/bridge/src/com/android/internal/util/VirtualRefBasePtr_Delegate.java b/bridge/src/com/android/internal/util/VirtualRefBasePtr_Delegate.java
deleted file mode 100644
index 01fe45d..0000000
--- a/bridge/src/com/android/internal/util/VirtualRefBasePtr_Delegate.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.util;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.util.LongSparseLongArray;
-
-/**
- * Delegate used to provide new implementation the native methods of {@link VirtualRefBasePtr}
- *
- * Through the layoutlib_create tool, the original native  methods of VirtualRefBasePtr have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- */
-@SuppressWarnings("unused")
-public class VirtualRefBasePtr_Delegate {
-    private static final DelegateManager<Object> sManager = new DelegateManager<>(Object.class);
-    private static final LongSparseLongArray sRefCount = new LongSparseLongArray();
-
-    @LayoutlibDelegate
-    /*package*/ static synchronized void nIncStrong(long ptr) {
-        long counter = sRefCount.get(ptr);
-        sRefCount.put(ptr, ++counter);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static synchronized void nDecStrong(long ptr) {
-        long counter = sRefCount.get(ptr);
-
-        if (counter > 1) {
-            sRefCount.put(ptr, --counter);
-        } else {
-            sRefCount.delete(ptr);
-            sManager.removeJavaReferenceFor(ptr);
-        }
-    }
-}
diff --git a/bridge/src/com/android/layoutlib/bridge/Bridge.java b/bridge/src/com/android/layoutlib/bridge/Bridge.java
index aa76e75..be8c617 100644
--- a/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -24,40 +24,50 @@
 import com.android.ide.common.rendering.api.Result;
 import com.android.ide.common.rendering.api.Result.Status;
 import com.android.ide.common.rendering.api.SessionParams;
+import com.android.ide.common.rendering.api.XmlParserFactory;
 import com.android.layoutlib.bridge.android.RenderParamsFlags;
+import com.android.layoutlib.bridge.impl.ParserFactory;
 import com.android.layoutlib.bridge.impl.RenderDrawable;
 import com.android.layoutlib.bridge.impl.RenderSessionImpl;
 import com.android.layoutlib.bridge.util.DynamicIdMap;
-import com.android.ninepatch.NinePatchChunk;
+import com.android.layoutlib.common.util.ReflectionUtils;
 import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.annotations.NonNull;
 import com.android.tools.layoutlib.annotations.Nullable;
 import com.android.tools.layoutlib.create.MethodAdapter;
+import com.android.tools.layoutlib.create.NativeConfig;
 import com.android.tools.layoutlib.create.OverrideMethod;
-import com.android.utils.Pair;
 
-import android.animation.PropertyValuesHolder;
-import android.animation.PropertyValuesHolder_Delegate;
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+
 import android.content.res.BridgeAssetManager;
 import android.graphics.Bitmap;
-import android.graphics.FontFamily_Delegate;
 import android.graphics.Typeface;
 import android.graphics.Typeface_Builder_Delegate;
-import android.graphics.Typeface_Delegate;
+import android.graphics.fonts.SystemFonts_Delegate;
 import android.icu.util.ULocale;
 import android.os.Looper;
 import android.os.Looper_Accessor;
+import android.os.SystemProperties;
+import android.util.Pair;
+import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
 
 import java.io.File;
 import java.lang.ref.SoftReference;
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Modifier;
 import java.util.Arrays;
 import java.util.EnumMap;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.WeakHashMap;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -109,12 +119,8 @@
 
     private final static Map<Object, Map<String, SoftReference<Bitmap>>> sProjectBitmapCache =
             new WeakHashMap<>();
-    private final static Map<Object, Map<String, SoftReference<NinePatchChunk>>> sProject9PatchCache =
-            new WeakHashMap<>();
 
     private final static Map<String, SoftReference<Bitmap>> sFrameworkBitmapCache = new HashMap<>();
-    private final static Map<String, SoftReference<NinePatchChunk>> sFramework9PatchCache =
-            new HashMap<>();
 
     private static Map<String, Map<String, Integer>> sEnumValueMap;
     private static Map<String, String> sPlatformProperties;
@@ -138,6 +144,11 @@
         public void warning(String tag, String message, Object viewCookie, Object data) {
             System.out.println(message);
         }
+
+        @Override
+        public void logAndroidFramework(int priority, String tag, String message) {
+            System.out.println(message);
+        }
     };
 
     /**
@@ -145,7 +156,12 @@
      */
     private static ILayoutLog sCurrentLog = sDefaultLog;
 
-    public static boolean sIsTypefaceInitialized;
+    private static String sIcuDataPath;
+
+    private static final String[] LINUX_NATIVE_LIBRARIES = {"libandroid_runtime.so"};
+    private static final String[] MAC_NATIVE_LIBRARIES = {"libandroid_runtime.dylib"};
+    private static final String[] WINDOWS_NATIVE_LIBRARIES =
+            {"libicuuc_stubdata.dll", "libicuuc-host.dll", "libandroid_runtime.dll"};
 
     @Override
     public boolean init(Map<String,String> platformProperties,
@@ -156,8 +172,12 @@
             ILayoutLog log) {
         sPlatformProperties = platformProperties;
         sEnumValueMap = enumValueMap;
+        sIcuDataPath = icuDataPath;
+        sCurrentLog = log;
 
-        BridgeAssetManager.initSystem();
+        if (!loadNativeLibrariesIfNeeded(log, nativeLibPath)) {
+            return false;
+        }
 
         // 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
@@ -188,9 +208,46 @@
             });
         }
 
-        // load the fonts.
-        FontFamily_Delegate.setFontLocation(fontLocation.getAbsolutePath());
-        MemoryMappedFile_Delegate.setDataDir(fontLocation.getAbsoluteFile().getParentFile());
+        try {
+            BridgeAssetManager.initSystem();
+
+            // Do the static initialization of all the classes for which it was deferred.
+            // In order to initialize Typeface, we first need to specify the location of fonts
+            // and set a parser factory that will be used to parse the fonts.xml file.
+            SystemFonts_Delegate.setFontLocation(fontLocation.getAbsolutePath() + File.separator);
+            MemoryMappedFile_Delegate.setDataDir(fontLocation.getAbsoluteFile().getParentFile());
+            ParserFactory.setParserFactory(new XmlParserFactory() {
+                @Override
+                @Nullable
+                public XmlPullParser createXmlParserForPsiFile(@NonNull String fileName) {
+                    return null;
+                }
+
+                @Override
+                @Nullable
+                public XmlPullParser createXmlParserForFile(@NonNull String fileName) {
+                    return null;
+                }
+
+                @Override
+                @NonNull
+                public XmlPullParser createXmlParser() {
+                    return new KXmlParser();
+                }
+            });
+            for (String deferredClass : NativeConfig.DEFERRED_STATIC_INITIALIZER_CLASSES) {
+                ReflectionUtils.invokeStatic(deferredClass, "deferredStaticInitializer");
+            }
+            // Load system fonts now that Typeface has been initialized
+            Typeface.loadPreinstalledSystemFontMap();
+            ParserFactory.setParserFactory(null);
+        } catch (Throwable t) {
+            if (log != null) {
+                log.error(ILayoutLog.TAG_BROKEN, "Layoutlib Bridge initialization failed", t,
+                        null, null);
+            }
+            return false;
+        }
 
         // 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.
@@ -234,7 +291,7 @@
                         Class<?> type = f.getType();
                         if (!type.isArray()) {
                             Integer value = (Integer) f.get(null);
-                            sRMap.put(value, Pair.of(resType, f.getName()));
+                            sRMap.put(value, Pair.create(resType, f.getName()));
                             fullMap.put(f.getName(), value);
                         }
                     }
@@ -253,6 +310,17 @@
     }
 
     /**
+     * Sets System properties using the Android framework code.
+     * This is accessed by the native libraries through JNI.
+     */
+    @SuppressWarnings("unused")
+    private static void setSystemProperties() {
+        for (Entry<String, String> property : sPlatformProperties.entrySet()) {
+            SystemProperties.set(property.getKey(), property.getValue());
+        }
+    }
+
+    /**
      * Tests if the field is pubic, static and one of int or int[].
      */
     private static boolean isValidRField(Field field) {
@@ -324,10 +392,10 @@
             if (arrayValue != null) {
                 String attrName = name.substring(arrayName.length() + 1);
                 int attrValue = arrayValue[index];
-                sRMap.put(attrValue, Pair.of(ResourceType.ATTR, attrName));
+                sRMap.put(attrValue, Pair.create(ResourceType.ATTR, attrName));
                 revRAttrMap.put(attrName, attrValue);
             }
-            sRMap.put(index, Pair.of(ResourceType.STYLEABLE, name));
+            sRMap.put(index, Pair.create(ResourceType.STYLEABLE, name));
             revRStyleableMap.put(name, index);
         }
     }
@@ -337,10 +405,9 @@
         BridgeAssetManager.clearSystem();
 
         // dispose of the default typeface.
-        if (sIsTypefaceInitialized) {
+        if (SystemFonts_Delegate.sIsTypefaceInitialized) {
             Typeface.sDynamicTypefaceCache.evictAll();
         }
-        sProject9PatchCache.clear();
         sProjectBitmapCache.clear();
 
         return true;
@@ -422,14 +489,12 @@
     public void clearResourceCaches(Object projectKey) {
         if (projectKey != null) {
             sProjectBitmapCache.remove(projectKey);
-            sProject9PatchCache.remove(projectKey);
         }
     }
 
     @Override
     public void clearAllCaches(Object projectKey) {
         clearResourceCaches(projectKey);
-        PropertyValuesHolder_Delegate.clearCaches();
     }
 
     @Override
@@ -537,7 +602,7 @@
         }
 
         if (pair != null) {
-            return new ResourceReference(ResourceNamespace.ANDROID, pair.getFirst(), pair.getSecond());
+            return new ResourceReference(ResourceNamespace.ANDROID, pair.first, pair.second);
         }
         return null;
     }
@@ -570,13 +635,6 @@
     }
 
     /**
-     * Returns the platform build properties.
-     */
-    public static Map<String, String> getPlatformProperties() {
-        return sPlatformProperties;
-    }
-
-    /**
      * Returns the bitmap for a specific path, from a specific project cache, or from the
      * framework cache.
      * @param value the path of the bitmap
@@ -620,56 +678,83 @@
     }
 
     /**
-     * Returns the 9 patch chunk for a specific path, from a specific project cache, or from the
-     * framework cache.
-     * @param value the path of the 9 patch
-     * @param projectKey the key of the project, or null to query the framework cache.
-     * @return the cached 9 patch or null if not found.
+     * This is called by the native layoutlib loader.
      */
-    public static NinePatchChunk getCached9Patch(String value, Object projectKey) {
-        if (projectKey != null) {
-            Map<String, SoftReference<NinePatchChunk>> map = sProject9PatchCache.get(projectKey);
-
-            if (map != null) {
-                SoftReference<NinePatchChunk> ref = map.get(value);
-                if (ref != null) {
-                    return ref.get();
-                }
-            }
-        } else {
-            SoftReference<NinePatchChunk> ref = sFramework9PatchCache.get(value);
-            if (ref != null) {
-                return ref.get();
-            }
-        }
-
-        return null;
+    @SuppressWarnings("unused")
+    public static String getIcuDataPath() {
+        return sIcuDataPath;
     }
 
-    /**
-     * Sets a 9 patch chunk in a project cache or in the framework cache.
-     * @param value the path of the 9 patch
-     * @param ninePatch the 9 patch object
-     * @param projectKey the key of the project, or null to put the bitmap in the framework cache.
-     */
-    public static void setCached9Patch(String value, NinePatchChunk ninePatch, Object projectKey) {
-        if (projectKey != null) {
-            Map<String, SoftReference<NinePatchChunk>> map =
-                    sProject9PatchCache.computeIfAbsent(projectKey, k -> new HashMap<>());
+    private static boolean sJniLibLoadAttempted;
+    private static boolean sJniLibLoaded;
 
-            map.put(value, new SoftReference<>(ninePatch));
-        } else {
-            sFramework9PatchCache.put(value, new SoftReference<>(ninePatch));
+    private synchronized static boolean loadNativeLibrariesIfNeeded(ILayoutLog log,
+            String nativeLibDir) {
+        if (!sJniLibLoadAttempted) {
+            try {
+                loadNativeLibraries(nativeLibDir);
+            }
+            catch (Throwable t) {
+                log.error(ILayoutLog.TAG_BROKEN, "Native layoutlib failed to load", t, null, null);
+            }
         }
+        return sJniLibLoaded;
+    }
+
+    private synchronized static void loadNativeLibraries(String nativeLibDir) {
+        if (sJniLibLoadAttempted) {
+            // Already attempted to load, nothing to do here.
+            return;
+        }
+        try {
+            // set the system property so LayoutLibLoader.cpp can read it
+            System.setProperty("core_native_classes", String.join(",",
+                    NativeConfig.CORE_CLASS_NATIVES));
+            System.setProperty("graphics_native_classes", String.join(",",
+                    NativeConfig.GRAPHICS_CLASS_NATIVES));
+            System.setProperty("icu.data.path", Bridge.getIcuDataPath());
+            System.setProperty("use_bridge_for_logging", "true");
+            System.setProperty("register_properties_during_load", "true");
+            for (String library : getNativeLibraries()) {
+                String path = new File(nativeLibDir, library).getAbsolutePath();
+                System.load(path);
+            }
+        }
+        finally {
+            sJniLibLoadAttempted = true;
+        }
+        sJniLibLoaded = true;
+    }
+
+    private static String[] getNativeLibraries() {
+        String osName = System.getProperty("os.name").toLowerCase(Locale.US);
+        if (osName.startsWith("windows")) {
+            return WINDOWS_NATIVE_LIBRARIES;
+        }
+        if (osName.startsWith("mac")) {
+            return MAC_NATIVE_LIBRARIES;
+        }
+        return LINUX_NATIVE_LIBRARIES;
     }
 
     @Override
     public void clearFontCache(String path) {
-        if (sIsTypefaceInitialized) {
+        if (SystemFonts_Delegate.sIsTypefaceInitialized) {
             final String key =
                     Typeface_Builder_Delegate.createAssetUid(BridgeAssetManager.initSystem(), path,
                             0, null, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE, DEFAULT_FAMILY);
             Typeface.sDynamicTypefaceCache.remove(key);
         }
     }
+
+    @Override
+    public Object createMockView(String label, Class<?>[] signature, Object[] args)
+            throws NoSuchMethodException, InstantiationException, IllegalAccessException,
+            InvocationTargetException {
+        Constructor<MockView> constructor = MockView.class.getConstructor(signature);
+        MockView mockView = constructor.newInstance(args);
+        mockView.setText(label);
+        mockView.setGravity(Gravity.CENTER);
+        return mockView;
+    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
index 5584708..3d1b2c7 100644
--- a/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -16,22 +16,32 @@
 
 package com.android.layoutlib.bridge;
 
+import com.android.ide.common.rendering.api.ILayoutLog;
+import com.android.ide.common.rendering.api.RenderParams;
 import com.android.ide.common.rendering.api.RenderSession;
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.Result;
 import com.android.ide.common.rendering.api.ViewInfo;
+import com.android.internal.lang.System_Delegate;
+import com.android.internal.util.ArrayUtils_Delegate;
 import com.android.layoutlib.bridge.impl.RenderSessionImpl;
-import com.android.tools.layoutlib.java.System_Delegate;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Handler_Delegate;
+import android.os.SystemClock_Delegate;
+import android.view.Choreographer;
+import android.view.DisplayEventReceiver_VsyncEventData_Accessor;
+import android.view.MotionEvent;
 
 import java.awt.image.BufferedImage;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
+import static com.android.layoutlib.bridge.impl.RenderAction.getCurrentContext;
+
 /**
  * An implementation of {@link RenderSession}.
  *
@@ -46,6 +56,8 @@
     @NonNull
     private Result mLastResult;
 
+    private static final Runnable NOOP_RUNNABLE = () -> { };
+
     @Override
     public Result getResult() {
         return mLastResult;
@@ -120,12 +132,12 @@
 
     @Override
     public void setSystemTimeNanos(long nanos) {
-        System_Delegate.setNanosTime(nanos);
+        execute(() -> System_Delegate.setNanosTime(nanos));
     }
 
     @Override
     public void setSystemBootTimeNanos(long nanos) {
-        System_Delegate.setBootTimeNanos(nanos);
+        execute(() -> System_Delegate.setBootTimeNanos(nanos));
     }
 
     @Override
@@ -136,10 +148,71 @@
     }
 
     @Override
-    public void dispose() {
-        if (mSession != null) {
-            mSession.dispose();
+    public boolean executeCallbacks(long nanos) {
+        // Currently, Compose relies on Choreographer frame callback and Handler#postAtFrontOfQueue.
+        // Calls to Handler are handled by Handler_Delegate and can be executed by Handler_Delegate#
+        // executeCallbacks. Choreographer frame callback is handled by Choreographer#doFrame.
+        if (mSession == null) {
+            return false;
         }
+        try {
+            Bridge.prepareThread();
+            mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
+            boolean hasMoreCallbacks = Handler_Delegate.executeCallbacks();
+            long currentTimeMs = SystemClock_Delegate.uptimeMillis();
+            getCurrentContext()
+                    .getSessionInteractiveData()
+                    .getChoreographerCallbacks()
+                    .execute(currentTimeMs, Bridge.getLog());
+            return hasMoreCallbacks;
+        } catch (Throwable t) {
+            Bridge.getLog().error(ILayoutLog.TAG_BROKEN, "Failed executing Choreographer#doFrame "
+                    , t, null, null);
+            return false;
+        } finally {
+            mSession.release();
+            Bridge.cleanupThread();
+        }
+    }
+
+    private static int toMotionEventType(TouchEventType eventType) {
+        switch (eventType) {
+            case PRESS:
+                return MotionEvent.ACTION_DOWN;
+            case RELEASE:
+                return MotionEvent.ACTION_UP;
+            case DRAG:
+                return MotionEvent.ACTION_MOVE;
+        }
+        throw new IllegalStateException("Unexpected touch event type: " + eventType);
+    }
+
+    @Override
+    public void triggerTouchEvent(TouchEventType type, int x, int y) {
+        execute(() -> {
+            int motionEventType = toMotionEventType(type);
+            mSession.dispatchTouchEvent(motionEventType, System_Delegate.nanoTime(), x, y);
+        });
+    }
+
+    @Override
+    public void execute(Runnable r) {
+        if (mSession != null) {
+            try {
+                Bridge.prepareThread();
+                mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
+                r.run();
+            } finally {
+                mSession.release();
+                Bridge.cleanupThread();
+            }
+        }
+    }
+
+    @Override
+    public void dispose() {
+        execute(mSession::dispose);
+        ArrayUtils_Delegate.clearCache();
     }
 
     /*package*/ BridgeRenderSession(@Nullable RenderSessionImpl scene, @NonNull Result lastResult) {
@@ -153,7 +226,7 @@
     @Override
     public Object getValidationData() {
         if (mSession != null) {
-            return mSession.getValidatorResult();
+            return mSession.getValidatorHierarchy();
         }
         return null;
     }
diff --git a/bridge/src/com/android/layoutlib/bridge/MockView.java b/bridge/src/com/android/layoutlib/bridge/MockView.java
index a1d1bbc..fac3cd3 100644
--- a/bridge/src/com/android/layoutlib/bridge/MockView.java
+++ b/bridge/src/com/android/layoutlib/bridge/MockView.java
@@ -16,7 +16,7 @@
 
 package com.android.layoutlib.bridge;
 
-import com.android.SdkConstants;
+import com.android.ide.common.rendering.api.AndroidConstants;
 import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
 
 import android.content.Context;
@@ -56,8 +56,8 @@
         // This inner TextView is only used to show some message on render result and should not
         // inherit any attributes from parent (MockView). So it only needs layout width and height.
         Map<String, String> attributes = new HashMap<>();
-        attributes.put(SdkConstants.ATTR_LAYOUT_WIDTH, SdkConstants.VALUE_MATCH_PARENT);
-        attributes.put(SdkConstants.ATTR_LAYOUT_HEIGHT, SdkConstants.VALUE_MATCH_PARENT);
+        attributes.put(AndroidConstants.ATTR_LAYOUT_WIDTH, AndroidConstants.VALUE_MATCH_PARENT);
+        attributes.put(AndroidConstants.ATTR_LAYOUT_HEIGHT, AndroidConstants.VALUE_MATCH_PARENT);
         mView = new TextView(context, new BridgeLayoutParamsMapAttributes(attributes));
         mView.setTextColor(0xFF000000);
         setGravity(Gravity.CENTER);
diff --git a/bridge/src/com/android/layoutlib/bridge/SessionInteractiveData.java b/bridge/src/com/android/layoutlib/bridge/SessionInteractiveData.java
new file mode 100644
index 0000000..39b8b08
--- /dev/null
+++ b/bridge/src/com/android/layoutlib/bridge/SessionInteractiveData.java
@@ -0,0 +1,45 @@
+package com.android.layoutlib.bridge;
+
+import com.android.layoutlib.bridge.util.ChoreographerCallbacks;
+import com.android.layoutlib.bridge.util.HandlerMessageQueue;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+public class SessionInteractiveData {
+    private final HandlerMessageQueue mHandlerMessageQueue = new HandlerMessageQueue();
+    private final ChoreographerCallbacks mChoreographerCallbacks = new ChoreographerCallbacks();
+    // Current system time
+    private final AtomicLong mNanosTime = new AtomicLong(System.nanoTime());
+    // Time that the system booted up in nanos
+    private final AtomicLong mBootNanosTime = new AtomicLong(System.nanoTime());
+
+    @NotNull
+    public HandlerMessageQueue getHandlerMessageQueue() {
+        return mHandlerMessageQueue;
+    }
+
+    @NotNull
+    public ChoreographerCallbacks getChoreographerCallbacks() { return mChoreographerCallbacks; }
+
+    public void setNanosTime(long nanos) {
+        mNanosTime.set(nanos);
+    }
+
+    public long getNanosTime() {
+        return mNanosTime.get();
+    }
+
+    public void setBootNanosTime(long nanos) {
+        mBootNanosTime.set(nanos);
+    }
+
+    public long getBootNanosTime() {
+        return mBootNanosTime.get();
+    }
+
+    public void dispose() {
+        mHandlerMessageQueue.clear();
+        mChoreographerCallbacks.clear();
+    }
+}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java b/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java
index 2dfbd24..abeab83 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java
@@ -23,7 +23,7 @@
 import java.util.Locale;
 
 /**
- * This class provides an alternate implementation for {@code java.util.Locale#adjustLanguageCode}
+ * This class provides an alternate implementation for {@code java.util.Locale#getDefault}
  * which is not available in openJDK. It also overrides the getDefault method.
  *
  * The create tool re-writes references to the above mentioned method to this one. Hence it's
@@ -32,21 +32,6 @@
 @SuppressWarnings("UnusedDeclaration")
 public class AndroidLocale {
 
-    public static String adjustLanguageCode(String languageCode) {
-        String adjusted = languageCode.toLowerCase(Locale.US);
-        // Map new language codes to the obsolete language
-        // codes so the correct resource bundles will be used.
-        if (languageCode.equals("he")) {
-            adjusted = "iw";
-        } else if (languageCode.equals("id")) {
-            adjusted = "in";
-        } else if (languageCode.equals("yi")) {
-            adjusted = "ji";
-        }
-
-        return adjusted;
-    }
-
     public static Locale getDefault() {
         BridgeContext context = RenderAction.getCurrentContext();
         if (context != null) {
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 27fe227..1d42d0a 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -16,7 +16,7 @@
 
 package com.android.layoutlib.bridge.android;
 
-import com.android.SdkConstants;
+import com.android.ide.common.rendering.api.AndroidConstants;
 import com.android.ide.common.rendering.api.AssetRepository;
 import com.android.ide.common.rendering.api.ILayoutLog;
 import com.android.ide.common.rendering.api.ILayoutPullParser;
@@ -30,11 +30,12 @@
 import com.android.ide.common.rendering.api.StyleResourceValue;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.BridgeConstants;
+import com.android.layoutlib.bridge.SessionInteractiveData;
 import com.android.layoutlib.bridge.impl.ParserFactory;
 import com.android.layoutlib.bridge.impl.ResourceHelper;
 import com.android.layoutlib.bridge.impl.Stack;
 import com.android.resources.ResourceType;
-import com.android.utils.Pair;
+import com.android.tools.layoutlib.annotations.NotNull;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -71,6 +72,7 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
+import android.media.AudioManager;
 import android.net.ConnectivityManager;
 import android.net.Uri;
 import android.os.Bundle;
@@ -86,6 +88,7 @@
 import android.os.UserHandle;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
+import android.util.Pair;
 import android.util.TypedValue;
 import android.view.BridgeInflater;
 import android.view.Display;
@@ -156,8 +159,6 @@
      */
     private final HashMap<Object, Object> mViewKeyHelpMap = new HashMap<>();
     private final BridgeAssetManager mAssets;
-    private final boolean mShadowsEnabled;
-    private final boolean mHighQualityShadows;
     private Resources mSystemResources;
     private final Object mProjectKey;
     private final DisplayMetrics mMetrics;
@@ -171,6 +172,7 @@
     private final ClipboardManager mClipboardManager;
     private final ActivityManager mActivityManager;
     private final ConnectivityManager mConnectivityManager;
+    private final AudioManager mAudioManager;
     private final HashMap<View, Integer> mScrollYPos = new HashMap<>();
     private final HashMap<View, Integer> mScrollXPos = new HashMap<>();
 
@@ -195,6 +197,8 @@
     private final ResourceNamespace mAppCompatNamespace;
     private final Map<Key<?>, Object> mUserData = new HashMap<>();
 
+    private final SessionInteractiveData mSessionInteractiveData;
+
     /**
      * Some applications that target both pre API 17 and post API 17, set the newer attrs to
      * reference the older ones. For example, android:paddingStart will resolve to
@@ -231,9 +235,7 @@
             @NonNull LayoutlibCallback layoutlibCallback,
             @NonNull Configuration config,
             int targetSdkVersion,
-            boolean hasRtlSupport,
-            boolean shadowsEnabled,
-            boolean highQualityShadows) {
+            boolean hasRtlSupport) {
         mProjectKey = projectKey;
         mMetrics = metrics;
         mLayoutlibCallback = layoutlibCallback;
@@ -260,6 +262,7 @@
         mClipboardManager = new ClipboardManager(this, null);
         mActivityManager = ActivityManager_Accessor.getActivityManagerInstance(this);
         mConnectivityManager = new ConnectivityManager(this, null);
+        mAudioManager = new AudioManager(this);
 
         if (mLayoutlibCallback.isResourceNamespacingRequired()) {
             if (mLayoutlibCallback.hasAndroidXAppCompat()) {
@@ -271,8 +274,7 @@
             mAppCompatNamespace = ResourceNamespace.RES_AUTO;
         }
 
-        mShadowsEnabled = shadowsEnabled;
-        mHighQualityShadows = highQualityShadows;
+        mSessionInteractiveData = new SessionInteractiveData();
     }
 
     /**
@@ -281,9 +283,11 @@
      *
      * @see #disposeResources()
      */
-    public void initResources() {
+    public void initResources(@NonNull AssetRepository assetRepository) {
         AssetManager assetManager = AssetManager.getSystem();
 
+        mAssets.setAssetRepository(assetRepository);
+
         mSystemResources = Resources_Delegate.initSystem(
                 this,
                 assetManager,
@@ -498,7 +502,7 @@
                         new BridgeXmlBlockParser(parser, this, layout.getNamespace());
                 try {
                     pushParser(blockParser);
-                    return Pair.of(
+                    return Pair.create(
                             mBridgeInflater.inflate(blockParser, parent, attachToRoot),
                             Boolean.TRUE);
                 } finally {
@@ -523,7 +527,8 @@
                             new BridgeXmlBlockParser(parser, this, layout.getNamespace());
                     try {
                         pushParser(blockParser);
-                        return Pair.of(mBridgeInflater.inflate(blockParser, parent, attachToRoot),
+                        return Pair.create(mBridgeInflater.inflate(blockParser, parent,
+                                attachToRoot),
                                 Boolean.FALSE);
                     } finally {
                         popParser();
@@ -545,7 +550,7 @@
                             layout.getName()), null, null);
         }
 
-        return Pair.of(null, Boolean.FALSE);
+        return Pair.create(null, Boolean.FALSE);
     }
 
     /**
@@ -662,11 +667,19 @@
                 return mConnectivityManager;
 
             case AUDIO_SERVICE:
+                return mAudioManager;
+
             case TEXT_CLASSIFICATION_SERVICE:
             case CONTENT_CAPTURE_MANAGER_SERVICE:
+            case ALARM_SERVICE:
                 return null;
             default:
-                assert false : "Unsupported Service: " + service;
+                // Only throw exception if the required service is unsupported but recognized as
+                // an existing system service.
+                assert SystemServiceRegistry.getSystemServiceClassName(service) == null :
+                        "Unsupported Service: " + service;
+                Bridge.getLog().warning(ILayoutLog.TAG_UNSUPPORTED, "Service " + service +
+                        " was not found or is unsupported", null, null);
         }
 
         return null;
@@ -718,20 +731,20 @@
             mTypedArrayCache.put(attrs, currentThemes, resId, typeArrayAndPropertiesPair);
         }
         // Add value to defaultPropsMap if needed
-        if (typeArrayAndPropertiesPair.getSecond() != null) {
+        if (typeArrayAndPropertiesPair.second != null) {
             BridgeXmlBlockParser parser = getCurrentParser();
             Object key = parser != null ? parser.getViewCookie() : null;
             if (key != null) {
                 Map<ResourceReference, ResourceValue> defaultPropMap = mDefaultPropMaps.get(key);
                 if (defaultPropMap == null) {
-                    defaultPropMap = typeArrayAndPropertiesPair.getSecond();
+                    defaultPropMap = typeArrayAndPropertiesPair.second;
                     mDefaultPropMaps.put(key, defaultPropMap);
                 } else {
-                    defaultPropMap.putAll(typeArrayAndPropertiesPair.getSecond());
+                    defaultPropMap.putAll(typeArrayAndPropertiesPair.second);
                 }
             }
         }
-        return typeArrayAndPropertiesPair.getFirst();
+        return typeArrayAndPropertiesPair.first;
     }
 
     /**
@@ -949,7 +962,7 @@
                         // If the value is a reference to another theme attribute that doesn't
                         // exist, we should log a warning and omit it.
                         String val = defaultValue.getValue();
-                        if (val != null && val.startsWith(SdkConstants.PREFIX_THEME_REF)) {
+                        if (val != null && val.startsWith(AndroidConstants.PREFIX_THEME_REF)) {
                             // Because we always use the latest framework code, some resources might
                             // fail to resolve when using old themes (they haven't been backported).
                             // Since this is an artifact caused by us using always the latest
@@ -1068,7 +1081,7 @@
 
         ta.sealArray();
 
-        return Pair.of(ta, defaultPropMap);
+        return Pair.create(ta, defaultPropMap);
     }
 
     /**
@@ -2097,20 +2110,6 @@
         return true;
     }
 
-    /**
-     * Returns whether shadows should be rendered or not
-     */
-    public boolean isShadowsEnabled() {
-        return mShadowsEnabled;
-    }
-
-    /**
-     * Returns whether high quality shadows should be used
-     */
-    public boolean isHighQualityShadows() {
-        return mHighQualityShadows;
-    }
-
     public <T> void putUserData(@NonNull Key<T> key, @Nullable T data) {
         mUserData.put(key, data);
     }
@@ -2250,4 +2249,9 @@
         }
 
     }
+
+    @NotNull
+    public SessionInteractiveData getSessionInteractiveData() {
+        return mSessionInteractiveData;
+    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index b0b09b2..902f11d 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -75,7 +75,7 @@
 
     @Override
     public PackageInfo getPackageInfo(VersionedPackage versionedPackage,
-            @PackageInfoFlags int flags) throws NameNotFoundException {
+            int packageInfoFlags) throws NameNotFoundException {
         return null;
     }
 
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index 41e4e2d..22676e4 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -20,6 +20,7 @@
 import android.os.ParcelDuration;
 import android.os.IBinder;
 import android.os.IPowerManager;
+import android.os.IWakeLockCallback;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeReason;
 import android.os.PowerSaveState;
@@ -106,7 +107,7 @@
 
     @Override
     public void acquireWakeLock(IBinder arg0, int arg1, String arg2, String arg2_5, WorkSource arg3,
-            String arg4, int arg5)
+            String arg4, int arg5, IWakeLockCallback callback)
             throws RemoteException {
         // pass for now.
     }
@@ -119,7 +120,7 @@
 
     @Override
     public void acquireWakeLockWithUid(IBinder arg0, int arg1, String arg2, String arg2_5,
-            int arg3, int arg4)
+            int arg3, int arg4, IWakeLockCallback callback)
             throws RemoteException {
         // pass for now.
     }
@@ -210,6 +211,11 @@
     }
 
     @Override
+    public void updateWakeLockCallback(IBinder arg0, IWakeLockCallback arg1) throws RemoteException {
+        // pass for now.
+    }
+
+    @Override
     public boolean isWakeLockLevelSupported(int level) throws RemoteException {
         // pass for now.
         return true;
@@ -243,6 +249,31 @@
     }
 
     @Override
+    public boolean isLowPowerStandbySupported() {
+        return false;
+    }
+
+    @Override
+    public boolean isLowPowerStandbyEnabled() {
+        return false;
+    }
+
+    @Override
+    public void setLowPowerStandbyEnabled(boolean enabled) {
+        // pass for now
+    }
+
+    @Override
+    public void setLowPowerStandbyActiveDuringMaintenance(boolean activeDuringMaintenance) {
+        // pass for now
+    }
+
+    @Override
+    public void forceLowPowerStandbyActive(boolean active) {
+        // pass for now
+    }
+
+    @Override
     public boolean isScreenBrightnessBoosted() throws RemoteException {
         return false;
     }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
index 0269352..460dd43 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
@@ -304,6 +304,13 @@
 
         int ev = mParser.next();
 
+        // AAPT treats resources so that XmlBlock$Parser never has TEXT events that are
+        // whitespace only. Ignore those events here as resources from Studio have not gone
+        // through AAPT compilation.
+        while (ev == TEXT && mParser.isWhitespace()) {
+            ev = mParser.next();
+        }
+
         if (ParserFactory.LOG_PARSER) {
             System.out.println("NEXT " + mParser.toString() + " " +
                     eventTypeToString(mEventType) + " -> " + eventTypeToString(ev));
diff --git a/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java b/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
index 4eaf352..e370fa0 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
@@ -36,24 +36,13 @@
             new Key<Boolean>("disableBitmapCaching", Boolean.class);
     public static final Key<Boolean> FLAG_KEY_RENDER_ALL_DRAWABLE_STATES =
             new Key<Boolean>("renderAllDrawableStates", Boolean.class);
-    /**
-     * To tell LayoutLib that the IDE supports RecyclerView.
-     * <p/>
-     * Default is false.
-     */
-    public static final Key<Boolean> FLAG_KEY_RECYCLER_VIEW_SUPPORT =
-            new Key<Boolean>("recyclerViewSupport", Boolean.class);
+
     /**
      * The application package name. Used via {@link LayoutlibCallback#getFlag(Key)}
      */
     public static final Key<String> FLAG_KEY_APPLICATION_PACKAGE =
             new Key<String>("applicationPackage", String.class);
-    /**
-     * To tell LayoutLib that IDE supports providing XML Parser for a file (useful for getting in
-     * memory contents of the file). Used via {@link LayoutlibCallback#getFlag(Key)}
-     */
-    public static final Key<Boolean> FLAG_KEY_XML_FILE_PARSER_SUPPORT =
-            new Key<Boolean>("xmlFileParser", Boolean.class);
+
     /**
      * To tell LayoutLib to not render when creating a new session. This allows controlling when the first
      * layout rendering will happen.
@@ -76,18 +65,6 @@
             new Key<Boolean>("enableResultImageAutoScale", Boolean.class);
 
     /**
-     * Enables Ray Traced shadows in layoutlib.
-     */
-    public static final Key<Boolean> FLAG_RENDER_HIGH_QUALITY_SHADOW =
-            new Key<>("renderHighQualityShadow", Boolean.class);
-
-    /**
-     * Flags to enable shadows in layoutlib.
-     */
-    public static final Key<Boolean> FLAG_ENABLE_SHADOW =
-            new Key<>("enableShadow", Boolean.class);
-
-    /**
      * Enables layout validation calls within rendering.
      */
     public static final Key<Boolean> FLAG_ENABLE_LAYOUT_VALIDATOR =
@@ -95,7 +72,7 @@
 
     /**
      * Enables image-related validation checks within layout validation.
-     * {@link FLAG_ENABLE_LAYOUT_VALIDATOR} must be enabled before this can be effective.
+     * {@link #FLAG_ENABLE_LAYOUT_VALIDATOR} must be enabled before this can be effective.
      */
     public static final Key<Boolean> FLAG_ENABLE_LAYOUT_VALIDATOR_IMAGE_CHECK =
             new Key<>("enableLayoutValidatorImageCheck", Boolean.class);
diff --git a/bridge/src/com/android/layoutlib/bridge/android/graphics/NopCanvas.java b/bridge/src/com/android/layoutlib/bridge/android/graphics/NopCanvas.java
index 131aa17..6e424fa 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/graphics/NopCanvas.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/graphics/NopCanvas.java
@@ -42,7 +42,7 @@
     public boolean isHardwareAccelerated() {
         // We return true the first time so there are no allocations for the software renderer in
         // the constructor
-        return !mIsInitialized;
+        return false;
     }
 
     @Override
@@ -304,4 +304,9 @@
     @Override
     public void drawPicture(Picture picture, Rect dst) {
     }
+
+    @Override
+    public boolean quickReject(float left, float top, float right, float bottom) {
+        return false;
+    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java
index 92693c6..226be24 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java
@@ -18,14 +18,14 @@
 
 import com.android.ide.common.rendering.api.ILayoutLog;
 import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+import com.android.layoutlib.common.util.ReflectionUtils.ReflectionException;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.view.View;
 
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
+import static com.android.layoutlib.common.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.common.util.ReflectionUtils.invoke;
 
 /**
  * Utility class for working with the design support lib.
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java
index 08e7419..3f99c64 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java
@@ -18,7 +18,7 @@
 
 import com.android.ide.common.rendering.api.ILayoutLog;
 import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+import com.android.layoutlib.common.util.ReflectionUtils.ReflectionException;
 
 import android.annotation.Nullable;
 import android.view.View;
@@ -27,9 +27,9 @@
 import static android.view.Gravity.LEFT;
 import static android.view.Gravity.RIGHT;
 import static android.view.Gravity.START;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getCause;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
+import static com.android.layoutlib.common.util.ReflectionUtils.getCause;
+import static com.android.layoutlib.common.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.common.util.ReflectionUtils.invoke;
 
 public class DrawerLayoutUtil {
 
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java
index a426b27..1224e57 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java
@@ -18,14 +18,14 @@
 
 import com.android.ide.common.rendering.api.ILayoutLog;
 import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+import com.android.layoutlib.common.util.ReflectionUtils.ReflectionException;
 
 import android.content.Context;
 import android.widget.TabHost;
 
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getCause;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
+import static com.android.layoutlib.common.util.ReflectionUtils.getCause;
+import static com.android.layoutlib.common.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.common.util.ReflectionUtils.invoke;
 
 /**
  * Utility class for working with android.support.v4.app.FragmentTabHost
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
index ff20fbf..46dd0b4 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
@@ -20,18 +20,17 @@
 import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
-import com.android.layoutlib.bridge.android.RenderParamsFlags;
-import com.android.layoutlib.bridge.util.ReflectionUtils;
-import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+import com.android.layoutlib.common.util.ReflectionUtils;
+import com.android.layoutlib.common.util.ReflectionUtils.ReflectionException;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.view.View;
 
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getCause;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
+import static com.android.layoutlib.common.util.ReflectionUtils.getCause;
+import static com.android.layoutlib.common.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.common.util.ReflectionUtils.invoke;
 
 /**
  * Utility class for working with android.support.v7.widget.RecyclerView and
@@ -54,13 +53,20 @@
      */
     public static void setAdapter(@NonNull View recyclerView, @NonNull BridgeContext context,
             @NonNull LayoutlibCallback layoutlibCallback, int adapterLayout, int itemCount) {
-        String recyclerViewClassName =
+        Class<?> recyclerViewClass =
                 ReflectionUtils.getParentClass(recyclerView, RecyclerViewUtil.CN_RECYCLER_VIEW);
+        if (recyclerViewClass == null) {
+            Bridge.getLog().error(ILayoutLog.TAG_BROKEN,
+                    "Unable to setup RecyclerView. No parent found.", null, null, null);
+            return;
+        }
+        String recyclerViewClassName = recyclerViewClass.getName();
+        String recyclerViewPackageName = recyclerViewClass.getPackage().getName();
         String adapterClassName = recyclerViewClassName + "$Adapter";
         String layoutMgrClassName = recyclerViewClassName + "$LayoutManager";
 
         try {
-            setLayoutManager(recyclerView, layoutMgrClassName, context, layoutlibCallback);
+            setLayoutManager(recyclerView, recyclerViewPackageName, layoutMgrClassName, context, layoutlibCallback);
             Object adapter = createAdapter(layoutlibCallback, adapterClassName);
             if (adapter != null) {
                 setProperty(recyclerView, adapterClassName, adapter, "setAdapter");
@@ -78,11 +84,11 @@
     }
 
     private static void setLayoutManager(@NonNull View recyclerView,
+            @NonNull String recyclerViewPackageName,
             @NonNull String layoutMgrClassName, @NonNull BridgeContext context,
             @NonNull LayoutlibCallback callback) throws ReflectionException {
         if (getLayoutManager(recyclerView) == null) {
-            String linearLayoutMgrClassManager =
-                    recyclerView.getClass().getPackage().getName() + ".LinearLayoutManager";
+            String linearLayoutMgrClassManager = recyclerViewPackageName + ".LinearLayoutManager";
             // Only set the layout manager if not already set by the recycler view.
             Object layoutManager =
                     createLayoutManager(context, linearLayoutMgrClassManager, callback);
@@ -113,11 +119,6 @@
     @Nullable
     private static Object createAdapter(@NonNull LayoutlibCallback layoutlibCallback,
             @NonNull String adapterClassName) throws ReflectionException {
-        Boolean ideSupport =
-                layoutlibCallback.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT);
-        if (ideSupport != Boolean.TRUE) {
-            return null;
-        }
         try {
             return layoutlibCallback.loadClass(adapterClassName, new Class[0], new Object[0]);
         } catch (Exception e) {
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java
index f195a5e..6876312 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java
@@ -16,15 +16,14 @@
 
 package com.android.layoutlib.bridge.android.support;
 
+import com.android.ide.common.rendering.api.ILayoutLog;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.RenderResources;
-import com.android.ide.common.rendering.api.ResourceNamespace;
-import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.StyleResourceValue;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
-import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+import com.android.layoutlib.common.util.ReflectionUtils.ReflectionException;
 import com.android.resources.ResourceType;
 import com.android.tools.layoutlib.annotations.NotNull;
 
@@ -45,10 +44,11 @@
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getAccessibleMethod;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getClassInstance;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
+import static com.android.layoutlib.bridge.Bridge.getLog;
+import static com.android.layoutlib.common.util.ReflectionUtils.getAccessibleMethod;
+import static com.android.layoutlib.common.util.ReflectionUtils.getClassInstance;
+import static com.android.layoutlib.common.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.common.util.ReflectionUtils.invoke;
 
 /**
  * Class with utility methods to instantiate Preferences provided by the support library.
@@ -215,7 +215,7 @@
      */
     @Nullable
     public static View inflatePreference(@NonNull BridgeContext bridgeContext,
-            @NonNull XmlPullParser parser, @Nullable ViewGroup root) {
+            @NonNull XmlPullParser parser, @Nullable ViewGroup root) throws Throwable {
         String preferencePackageName = null;
         String preferenceManagerClassName = null;
         // Find the correct package for the classes
@@ -308,6 +308,21 @@
 
             return scrollView;
         } catch (ReflectionException e) {
+            Throwable t = e;
+            while (t.getCause() != null) {
+                t = t.getCause();
+            }
+            if (t instanceof ClassNotFoundException) {
+                String message = t.getMessage();
+                if (message != null && !message.contains(preferencePackageName)) {
+                    // If the class not found is not part of the preference library, then it
+                    // must be a custom preference. Log the error and throw the exception, which
+                    // will prevent trying to inflate with the Android framework preference
+                    // installer
+                    getLog().error(ILayoutLog.TAG_INFLATE, t.getMessage(), null, null);
+                    throw t;
+                }
+            }
             return null;
         }
     }
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/bridge/src/com/android/layoutlib/bridge/bars/Config.java
index a49a664..1c6d55c 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/Config.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/Config.java
@@ -92,8 +92,8 @@
     }
 
     public static String getTime(int platformVersion) {
-        if (isGreaterOrEqual(platformVersion, R)) {
-            return "11:00";
+        if (isGreaterOrEqual(platformVersion, S)) {
+            return "12:00";
         }
         if (platformVersion < GINGERBREAD) {
             return "2:20";
@@ -137,6 +137,9 @@
         if (platformVersion < R) {
             return "10:00";
         }
+        if (platformVersion < S) {
+            return "11:00";
+        }
         // Should never happen.
         return "4:04";
     }
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 0bb72ed..63efec3 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -33,7 +33,8 @@
 import android.annotation.NonNull;
 import android.content.res.ColorStateList;
 import android.graphics.Bitmap;
-import android.graphics.Bitmap_Delegate;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapFactory.Options;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.util.TypedValue;
@@ -44,7 +45,6 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-import java.io.IOException;
 import java.io.InputStream;
 
 import static android.os._Original_Build.VERSION_CODES.LOLLIPOP;
@@ -116,12 +116,10 @@
             // look for a cached bitmap
             Bitmap bitmap = Bridge.getCachedBitmap(path, Boolean.TRUE /*isFramework*/);
             if (bitmap == null) {
-                try {
-                    bitmap = Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density);
-                    Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/);
-                } catch (IOException e) {
-                    return imageView;
-                }
+                Options options = new Options();
+                options.inDensity = density.getDpiValue();
+                bitmap = BitmapFactory.decodeStream(stream, null, options);
+                Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/);
             }
 
             if (bitmap != null) {
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java b/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
index 5991eb0..398c260 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
@@ -16,7 +16,6 @@
 
 package com.android.layoutlib.bridge.impl;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.layoutlib.bridge.util.Debug;
 import com.android.layoutlib.bridge.util.SparseWeakArray;
 
@@ -26,12 +25,9 @@
 import java.io.PrintStream;
 import java.lang.ref.WeakReference;
 import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.Set;
-import java.util.WeakHashMap;
 import java.util.concurrent.atomic.AtomicLong;
 
-import libcore.util.NativeAllocationRegistry_Delegate;
 
 /**
  * Manages native delegates.
@@ -86,30 +82,6 @@
      */
     private static final Set<Object> sJavaReferences = new HashSet<>();
     private static final AtomicLong sDelegateCounter = new AtomicLong(1);
-    /**
-     * Tracks "native" allocations. This means that we know of the object in the Java side and we
-     * can attach the delegate lifecycle to the lifecycle of the Java object. If the Java object
-     * is disposed, it means we can get rid of the delegate allocation.
-     * Ideally, we would use a {@link WeakHashMap} but we do not control the equals() method of the
-     * referents so we can not safely rely on them.
-     */
-    private static final LinkedList<NativeAllocationHolder> sNativeAllocations = new LinkedList<>();
-    /**
-     * Map that allows to do a quick lookup of delegates that have been marked as native
-     * allocations.
-     * This allows us to quickly check if, when a manual dispose happens, there is work we have
-     * to do.
-     */
-    @GuardedBy("sNativeAllocations")
-    private static final WeakHashMap<Object, WeakReference<NativeAllocationHolder>>
-            sNativeAllocationsReferences = new WeakHashMap<>();
-    /**
-     * Counter of the number of native allocations. We use this counter to trigger the collection
-     * of unlinked references in the sNativeAllocations list. We do not need to do this process
-     * on every allocation so only run it every 50 allocations.
-     */
-    @GuardedBy("sNativeAllocations")
-    private static long sNativeAllocationsCount = 0;
 
     private final Class<T> mClass;
 
@@ -150,7 +122,6 @@
                 }
             }
 
-            assert delegate != null;
             //noinspection unchecked
             return (T)delegate;
         }
@@ -193,80 +164,7 @@
                         " with int " + native_object);
             }
 
-            if (!sJavaReferences.remove(delegate)) {
-                // We didn't have any strong references to the delegate so it might be tracked by
-                // the native allocations tracker. If so, we want to remove that reference to
-                // make it available to collect ASAP.
-                synchronized (sNativeAllocations) {
-                    WeakReference<NativeAllocationHolder> holderRef = sNativeAllocationsReferences.get(delegate);
-                    NativeAllocationHolder holder = holderRef.get();
-                    if (holder != null) {
-                        // We only null the referred delegate. We do not spend the time in finding
-                        // the holder in the list and removing it since the "garbage collection" in
-                        // markAsNativeAllocation will do it for us.
-                        holder.mReferred = null;
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * This method marks the given native_object as a native allocation of the passed referent.
-     * This means that the lifecycle of the native_object can now be attached to the referent and
-     * if the referent is disposed, we can safely dispose the delegate.
-     * This method is called by the {@link NativeAllocationRegistry_Delegate} and allows the
-     * DelegateManager to remove the strong reference to the delegate.
-     */
-    public void markAsNativeAllocation(Object referent, long native_object) {
-        NativeAllocationHolder holder;
-        synchronized (DelegateManager.class) {
-            T delegate = getDelegate(native_object);
-            if (Debug.DEBUG) {
-                if (delegate == null) {
-                    System.err.println("Unknown " + mClass.getSimpleName() + " with int " +
-                            native_object);
-                }
-                else {
-                    System.err.println("Marking element as native " + native_object);
-                }
-            }
-
-            assert delegate != null;
-            if (sJavaReferences.remove(delegate)) {
-                // We had a strong reference, move to the native allocation tracker.
-                holder = new NativeAllocationHolder(referent, delegate);
-            }
-            else {
-                holder = null;
-            }
-        }
-
-        if (holder != null) {
-            synchronized (sNativeAllocations) {
-                sNativeAllocations.add(holder);
-                // The value references the key in this case but we use a WeakReference value.
-                sNativeAllocationsReferences.put(holder.mReferred, new WeakReference<>(holder));
-
-                if (++sNativeAllocationsCount % 50 == 0) {
-                    // Do garbage collection
-                    boolean collected = sNativeAllocations.removeIf(e -> e.mReferent.get() == null);
-                    if (Debug.DEBUG && collected) {
-                        System.err.println("Elements collected");
-                    }
-                }
-            }
-        }
-    }
-
-    private static class NativeAllocationHolder {
-        private final WeakReference<Object> mReferent;
-        // The referred object is not null so we can null them on demand
-        private Object mReferred;
-
-        private NativeAllocationHolder(Object referent, Object referred) {
-            mReferent = new WeakReference<>(referent);
-            mReferred = referred;
+            sJavaReferences.remove(delegate);
         }
     }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
deleted file mode 100644
index f3ebd40..0000000
--- a/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
+++ /dev/null
@@ -1,925 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.layoutlib.bridge.impl;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-
-import android.graphics.Bitmap_Delegate;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter_Delegate;
-import android.graphics.Paint;
-import android.graphics.Paint_Delegate;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
-import android.graphics.Region_Delegate;
-import android.graphics.Shader_Delegate;
-
-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.Shape;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Area;
-import java.awt.geom.NoninvertibleTransformException;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
-import java.util.ArrayList;
-
-import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
-import static java.awt.image.BufferedImage.TYPE_INT_RGB;
-import static java.lang.Math.max;
-import static java.lang.Math.min;
-
-/**
- * Class representing a graphics context snapshot, as well as a context stack as a linked list.
- * <p>
- * This is based on top of {@link Graphics2D} but can operate independently if none are available
- * yet when setting transforms and clip information.
- * <p>
- * This allows for drawing through {@link #draw(Drawable, Paint_Delegate, boolean, boolean)} and
- * {@link #draw(Drawable)}
- *
- * Handling of layers (created with {@link Canvas#saveLayer(RectF, Paint, int)}) is handled through
- * a list of Graphics2D for each layers. The class actually maintains a list of {@link Layer}
- * for each layer. Doing a save() will duplicate this list so that each graphics2D object
- * ({@link Layer#getGraphics()}) is configured only for the new snapshot.
- */
-public class GcSnapshot {
-    private static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform();
-
-    private final GcSnapshot mPrevious;
-    private final int mFlags;
-
-    /** list of layers. The first item in the list is always the  */
-    private final ArrayList<Layer> mLayers = new ArrayList<Layer>();
-
-    /** temp transform in case transformation are set before a Graphics2D exists */
-    private AffineTransform mTransform = null;
-    /** temp clip in case clipping is set before a Graphics2D exists */
-    private Area mClip = null;
-
-    // local layer data
-    /** a local layer created with {@link Canvas#saveLayer(RectF, Paint, int)}.
-     * If this is null, this does not mean there's no layer, just that the snapshot is not the
-     * one that created the layer.
-     */
-    private final Layer mLocalLayer;
-    private final Paint_Delegate mLocalLayerPaint;
-    private final Rect mLayerBounds;
-
-    public interface Drawable {
-        void draw(Graphics2D graphics, Paint_Delegate paint);
-    }
-
-    /**
-     * Class containing information about a layer.
-     *
-     * This contains graphics, bitmap and layer information.
-     */
-    private static class Layer {
-        private final Graphics2D mGraphics;
-        private final Bitmap_Delegate mBitmap;
-        private final BufferedImage mImage;
-        /** the flags that were used to configure the layer. This is never changed, and passed
-         * as is when {@link #makeCopy()} is called */
-        private final int mFlags;
-        /** the original content of the layer when the next object was created. This is not
-         * passed in {@link #makeCopy()} and instead is recreated when a new layer is added
-         * (depending on its flags) */
-        private BufferedImage mOriginalCopy;
-
-        /**
-         * Creates a layer with a graphics and a bitmap. This is only used to create
-         * the base layer.
-         *
-         * @param graphics the graphics
-         * @param bitmap the bitmap
-         */
-        Layer(Graphics2D graphics, Bitmap_Delegate bitmap) {
-            mGraphics = graphics;
-            mBitmap = bitmap;
-            mImage = mBitmap.getImage();
-            mFlags = 0;
-        }
-
-        /**
-         * Creates a layer with a graphics and an image. If the image belongs to a
-         * {@link Bitmap_Delegate} (case of the base layer), then
-         * {@link Layer#Layer(Graphics2D, Bitmap_Delegate)} should be used.
-         *
-         * @param graphics the graphics the new graphics for this layer
-         * @param image the image the image from which the graphics came
-         * @param flags the flags that were used to save this layer
-         */
-        Layer(Graphics2D graphics, BufferedImage image, int flags) {
-            mGraphics = graphics;
-            mBitmap = null;
-            mImage = image;
-            mFlags = flags;
-        }
-
-        /** The Graphics2D, guaranteed to be non null */
-        Graphics2D getGraphics() {
-            return mGraphics;
-        }
-
-        /** The BufferedImage, guaranteed to be non null */
-        BufferedImage getImage() {
-            return mImage;
-        }
-
-        /** Returns the layer save flags. This is only valid for additional layers.
-         * For the base layer this will always return 0;
-         * For a given layer, all further copies of this {@link Layer} object in new snapshots
-         * will always return the same value.
-         */
-        int getFlags() {
-            return mFlags;
-        }
-
-        Layer makeCopy() {
-            if (mBitmap != null) {
-                return new Layer((Graphics2D) mGraphics.create(), mBitmap);
-            }
-
-            return new Layer((Graphics2D) mGraphics.create(), mImage, mFlags);
-        }
-
-        /** sets an optional copy of the original content to be used during restore */
-        void setOriginalCopy(BufferedImage image) {
-            mOriginalCopy = image;
-        }
-
-        BufferedImage getOriginalCopy() {
-            return mOriginalCopy;
-        }
-
-        void change() {
-            if (mBitmap != null) {
-                mBitmap.change();
-            }
-        }
-
-        /**
-         * Sets the clip for the graphics2D object associated with the layer.
-         * This should be used over the normal Graphics2D setClip method.
-         *
-         * @param clipShape the shape to use a the clip shape.
-         */
-        void setClip(Shape clipShape) {
-            // because setClip is only guaranteed to work with rectangle shape,
-            // first reset the clip to max and then intersect the current (empty)
-            // clip with the shap.
-            mGraphics.setClip(null);
-            mGraphics.clip(clipShape);
-        }
-
-        /**
-         * Clips the layer with the given shape. This performs an intersect between the current
-         * clip shape and the given shape.
-         * @param shape the new clip shape.
-         */
-        public void clip(Shape shape) {
-            mGraphics.clip(shape);
-        }
-    }
-
-    /**
-     * Creates the root snapshot associating it with a given bitmap.
-     * <p>
-     * If <var>bitmap</var> is null, then {@link GcSnapshot#setBitmap(Bitmap_Delegate)} must be
-     * called before the snapshot can be used to draw. Transform and clip operations are permitted
-     * before.
-     *
-     * @param bitmap the image to associate to the snapshot or null.
-     * @return the root snapshot
-     */
-    public static GcSnapshot createDefaultSnapshot(Bitmap_Delegate bitmap) {
-        GcSnapshot snapshot = new GcSnapshot();
-        if (bitmap != null) {
-            snapshot.setBitmap(bitmap);
-        }
-
-        return snapshot;
-    }
-
-    /**
-     * Saves the current state according to the given flags and returns the new current snapshot.
-     * <p/>
-     * This is the equivalent of {@link Canvas#save(int)}
-     *
-     * @param flags the save flags.
-     * @return the new snapshot
-     *
-     * @see Canvas#save(int)
-     */
-    public GcSnapshot save(int flags) {
-        return new GcSnapshot(this, null /*layerbounds*/, null /*paint*/, flags);
-    }
-
-    /**
-     * Saves the current state and creates a new layer, and returns the new current snapshot.
-     * <p/>
-     * This is the equivalent of {@link Canvas#saveLayer(RectF, Paint, int)}
-     *
-     * @param layerBounds the layer bounds
-     * @param paint the Paint information used to blit the layer back into the layers underneath
-     *          upon restore
-     * @param flags the save flags.
-     * @return the new snapshot
-     *
-     * @see Canvas#saveLayer(RectF, Paint, int)
-     */
-    public GcSnapshot saveLayer(RectF layerBounds, Paint_Delegate paint, int flags) {
-        return new GcSnapshot(this, layerBounds, paint, flags);
-    }
-
-    /**
-     * Creates the root snapshot.
-     */
-    private GcSnapshot() {
-        mPrevious = null;
-        mFlags = 0;
-        mLocalLayer = null;
-        mLocalLayerPaint = null;
-        mLayerBounds = null;
-    }
-
-    /**
-     * Creates a new {@link GcSnapshot} on top of another one, with a layer data to be restored
-     * into the main graphics when {@link #restore()} is called.
-     *
-     * @param previous the previous snapshot head.
-     * @param layerBounds the region of the layer. Optional, if null, this is a normal save()
-     * @param paint the Paint information used to blit the layer back into the layers underneath
-     *          upon restore
-     * @param flags the flags regarding what should be saved.
-     */
-    private GcSnapshot(GcSnapshot previous, RectF layerBounds, Paint_Delegate paint, int flags) {
-        assert previous != null;
-        mPrevious = previous;
-        mFlags = flags;
-
-        // make a copy of the current layers before adding the new one.
-        // This keeps the same BufferedImage reference but creates new Graphics2D for this
-        // snapshot.
-        // It does not copy whatever original copy the layers have, as they will be done
-        // only if the new layer doesn't clip drawing to itself.
-        for (Layer layer : mPrevious.mLayers) {
-            mLayers.add(layer.makeCopy());
-        }
-
-        if (layerBounds != null) {
-            // get the current transform
-            AffineTransform matrix = mLayers.get(0).getGraphics().getTransform();
-
-            // transform the layerBounds with the current transform and stores it into a int rect
-            RectF rect2 = new RectF();
-            mapRect(matrix, rect2, layerBounds);
-            mLayerBounds = new Rect();
-            rect2.round(mLayerBounds);
-
-            // get the base layer (always at index 0)
-            Layer baseLayer = mLayers.get(0);
-
-            // create the image for the layer
-            BufferedImage layerImage = new BufferedImage(
-                    baseLayer.getImage().getWidth(),
-                    baseLayer.getImage().getHeight(),
-                    (mFlags & Canvas.HAS_ALPHA_LAYER_SAVE_FLAG) != 0 ?
-                            TYPE_INT_ARGB :
-                                TYPE_INT_RGB);
-
-            // create a graphics for it so that drawing can be done.
-            Graphics2D layerGraphics = layerImage.createGraphics();
-
-            // because this layer inherits the current context for transform and clip,
-            // set them to one from the base layer.
-            AffineTransform currentMtx = baseLayer.getGraphics().getTransform();
-            layerGraphics.setTransform(currentMtx);
-
-            // create a new layer for this new layer and add it to the list at the end.
-            mLayers.add(mLocalLayer = new Layer(layerGraphics, layerImage, flags));
-
-            // set the clip on it.
-            Shape currentClip = baseLayer.getGraphics().getClip();
-            mLocalLayer.setClip(currentClip);
-
-            // if the drawing is not clipped to the local layer only, we save the current content
-            // of all other layers. We are only interested in the part that will actually
-            // be drawn, so we create as small bitmaps as we can.
-            // This is so that we can erase the drawing that goes in the layers below that will
-            // be coming from the layer itself.
-            if ((mFlags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0) {
-                int w = mLayerBounds.width();
-                int h = mLayerBounds.height();
-                for (int i = 0 ; i < mLayers.size() - 1 ; i++) {
-                    Layer layer = mLayers.get(i);
-                    BufferedImage image = new BufferedImage(w, h, TYPE_INT_ARGB);
-                    Graphics2D graphics = image.createGraphics();
-                    graphics.drawImage(layer.getImage(),
-                            0, 0, w, h,
-                            mLayerBounds.left, mLayerBounds.top,
-                                    mLayerBounds.right, mLayerBounds.bottom,
-                            null);
-                    graphics.dispose();
-                    layer.setOriginalCopy(image);
-                }
-            }
-        } else {
-            mLocalLayer = null;
-            mLayerBounds = null;
-        }
-
-        mLocalLayerPaint  = paint;
-    }
-
-    public void dispose() {
-        for (Layer layer : mLayers) {
-            layer.getGraphics().dispose();
-        }
-
-        if (mPrevious != null) {
-            mPrevious.dispose();
-        }
-    }
-
-    /**
-     * Restores the top {@link GcSnapshot}, and returns the next one.
-     */
-    public GcSnapshot restore() {
-        return doRestore();
-    }
-
-    /**
-     * Restores the {@link GcSnapshot} to <var>saveCount</var>.
-     * @param saveCount the saveCount or -1 to only restore 1.
-     *
-     * @return the new head of the Gc snapshot stack.
-     */
-    public GcSnapshot restoreTo(int saveCount) {
-        return doRestoreTo(size(), saveCount);
-    }
-
-    public int size() {
-        if (mPrevious != null) {
-            return mPrevious.size() + 1;
-        }
-
-        return 1;
-    }
-
-    /**
-     * Link the snapshot to a Bitmap_Delegate.
-     * <p/>
-     * This is only for the case where the snapshot was created with a null image when calling
-     * {@link #createDefaultSnapshot(Bitmap_Delegate)}, and is therefore not yet linked to
-     * a previous snapshot.
-     * <p/>
-     * If any transform or clip information was set before, they are put into the Graphics object.
-     * @param bitmap the bitmap to link to.
-     */
-    public void setBitmap(Bitmap_Delegate bitmap) {
-        // create a new Layer for the bitmap. This will be the base layer.
-        Graphics2D graphics2D = bitmap.getImage().createGraphics();
-        Layer baseLayer = new Layer(graphics2D, bitmap);
-
-        // Set the current transform and clip which can either come from mTransform/mClip if they
-        // were set when there was no bitmap/layers or from the current base layers if there is
-        // one already.
-
-        graphics2D.setTransform(getTransform());
-        // reset mTransform in case there was one.
-        mTransform = null;
-
-        baseLayer.setClip(getClip());
-        // reset mClip in case there was one.
-        mClip = null;
-
-        // replace whatever current layers we have with this.
-        mLayers.clear();
-        mLayers.add(baseLayer);
-
-    }
-
-    public void translate(float dx, float dy) {
-        if (mLayers.size() > 0) {
-            for (Layer layer : mLayers) {
-                layer.getGraphics().translate(dx, dy);
-            }
-        } else {
-            if (mTransform == null) {
-                mTransform = new AffineTransform();
-            }
-            mTransform.translate(dx, dy);
-        }
-    }
-
-    public void rotate(double radians) {
-        if (mLayers.size() > 0) {
-            for (Layer layer : mLayers) {
-                layer.getGraphics().rotate(radians);
-            }
-        } else {
-            if (mTransform == null) {
-                mTransform = new AffineTransform();
-            }
-            mTransform.rotate(radians);
-        }
-    }
-
-    public void scale(float sx, float sy) {
-        if (mLayers.size() > 0) {
-            for (Layer layer : mLayers) {
-                layer.getGraphics().scale(sx, sy);
-            }
-        } else {
-            if (mTransform == null) {
-                mTransform = new AffineTransform();
-            }
-            mTransform.scale(sx, sy);
-        }
-    }
-
-    public AffineTransform getTransform() {
-        if (mLayers.size() > 0) {
-            // all graphics2D in the list have the same transform
-            return mLayers.get(0).getGraphics().getTransform();
-        } else {
-            if (mTransform == null) {
-                mTransform = new AffineTransform();
-            }
-            return mTransform;
-        }
-    }
-
-    public void setTransform(AffineTransform transform) {
-        if (mLayers.size() > 0) {
-            for (Layer layer : mLayers) {
-                layer.getGraphics().setTransform(transform);
-            }
-        } else {
-            if (mTransform == null) {
-                mTransform = new AffineTransform();
-            }
-            mTransform.setTransform(transform);
-        }
-    }
-
-    public boolean clip(Shape shape, int regionOp) {
-        // Simple case of intersect with existing layers.
-        // Because Graphics2D#setClip works a bit peculiarly, we optimize
-        // the case of clipping by intersection, as it's supported natively.
-        if (regionOp == Region.Op.INTERSECT.nativeInt && mLayers.size() > 0) {
-            for (Layer layer : mLayers) {
-                layer.clip(shape);
-            }
-
-            Shape currentClip = getClip();
-            return currentClip != null && currentClip.getBounds().isEmpty() == false;
-        }
-
-        Area area = null;
-
-        if (regionOp == Region.Op.REPLACE.nativeInt) {
-            area = new Area(shape);
-        } else {
-            area = Region_Delegate.combineShapes(getClip(), shape, regionOp);
-        }
-
-        if (mLayers.size() > 0) {
-            if (area != null) {
-                for (Layer layer : mLayers) {
-                    layer.setClip(area);
-                }
-            }
-
-            Shape currentClip = getClip();
-            return currentClip != null && currentClip.getBounds().isEmpty() == false;
-        } else {
-            if (area != null) {
-                mClip = area;
-            } else {
-                mClip = new Area();
-            }
-
-            return mClip.getBounds().isEmpty() == false;
-        }
-    }
-
-    public boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
-        return clip(new Rectangle2D.Float(left, top, right - left, bottom - top), regionOp);
-    }
-
-    /**
-     * Returns the current clip, or null if none have been setup.
-     */
-    public Shape getClip() {
-        if (mLayers.size() > 0) {
-            // they all have the same clip
-            return mLayers.get(0).getGraphics().getClip();
-        } else {
-            return mClip;
-        }
-    }
-
-    private GcSnapshot doRestoreTo(int size, int saveCount) {
-        if (size <= saveCount) {
-            return this;
-        }
-
-        // restore the current one first.
-        GcSnapshot previous = doRestore();
-
-        if (size == saveCount + 1) { // this was the only one that needed restore.
-            return previous;
-        } else {
-            return previous.doRestoreTo(size - 1, saveCount);
-        }
-    }
-
-    /**
-     * Executes the Drawable's draw method, with a null paint delegate.
-     * <p/>
-     * Note that the method can be called several times if there are more than one active layer.
-     */
-    public void draw(Drawable drawable) {
-        draw(drawable, null, false /*compositeOnly*/, false /*forceSrcMode*/);
-    }
-
-    /**
-     * Executes the Drawable's draw method.
-     * <p/>
-     * Note that the method can be called several times if there are more than one active layer.
-     * @param compositeOnly whether the paint is used for composite only. This is typically
-     *          the case for bitmaps.
-     * @param forceSrcMode if true, this overrides the composite to be SRC
-     */
-    public void draw(Drawable drawable, Paint_Delegate paint, boolean compositeOnly,
-            boolean forceSrcMode) {
-        int forceMode = forceSrcMode ? AlphaComposite.SRC : 0;
-        // the current snapshot may not have a mLocalLayer (ie it was created on save() instead
-        // of saveLayer(), but that doesn't mean there's no layer.
-        // mLayers however saves all the information we need (flags).
-        if (mLayers.size() == 1) {
-            // no layer, only base layer. easy case.
-            drawInLayer(mLayers.get(0), drawable, paint, compositeOnly, forceMode);
-        } else {
-            // draw in all the layers until the layer save flags tells us to stop (ie drawing
-            // in that layer is limited to the layer itself.
-            int flags;
-            int i = mLayers.size() - 1;
-
-            do {
-                Layer layer = mLayers.get(i);
-
-                drawInLayer(layer, drawable, paint, compositeOnly, forceMode);
-
-                // then go to previous layer, only if there are any left, and its flags
-                // doesn't restrict drawing to the layer itself.
-                i--;
-                flags = layer.getFlags();
-            } while (i >= 0 && (flags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0);
-        }
-    }
-
-    /**
-     * This function calculates a minimum (in area) integer rectangle that contains the input
-     * rectangle after applying to it the affine transform
-     *
-     * @param rect input rectangle
-     * @param transform affine transform applied to the input rectangle
-     *
-     * Returns an output rectangle
-     */
-    private static Rectangle transformRect(Rectangle rect, AffineTransform transform) {
-        double[] coords = new double[16];
-        coords[0] = rect.x;
-        coords[1] = rect.y;
-        coords[2] = rect.x + rect.width;
-        coords[3] = rect.y + rect.height;
-        coords[4] = rect.x;
-        coords[5] = rect.y + rect.height;
-        coords[6] = rect.x + rect.width;
-        coords[7] = rect.y;
-        transform.transform(coords, 0, coords, 8, 4);
-        // From 4 transformed vertices of the input rectangle we search for the minimum and maximum
-        // for both coordinates. We round the found extrema to the closest integer, smaller of equal
-        // for the minimums and larger or equal for the maximums. These values represent the border
-        // or the minimum rectangle with sides parallel to the coordinate axis that contains
-        // the transformed rectangle
-        int x = (int) Math.floor(min(min(coords[8], coords[10]), min(coords[12], coords[14])));
-        int y = (int) Math.floor(min(min(coords[9], coords[11]), min(coords[13], coords[15])));
-        int w = (int) Math.ceil(max(max(coords[8], coords[10]), max(coords[12], coords[14]))) - x;
-        int h = (int) Math.ceil(max(max(coords[9], coords[11]), max(coords[13], coords[15]))) - y;
-        return new Rectangle(x, y, w, h);
-    }
-
-    private void drawInLayer(Layer layer, Drawable drawable, Paint_Delegate paint,
-            boolean compositeOnly, int forceMode) {
-        Graphics2D originalGraphics = layer.getGraphics();
-        if (paint == null) {
-            drawOnGraphics((Graphics2D) originalGraphics.create(), drawable,
-                    null /*paint*/, layer);
-        } else {
-            ColorFilter_Delegate filter = paint.getColorFilter();
-            if (filter == null || !filter.isSupported()) {
-                // get a Graphics2D object configured with the drawing parameters.
-                Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint,
-                        compositeOnly, forceMode);
-                drawOnGraphics(configuredGraphics, drawable, paint, layer);
-                return;
-            }
-
-            Rectangle clipBounds = originalGraphics.getClip() != null ? originalGraphics
-                    .getClipBounds() : null;
-            AffineTransform transform = originalGraphics.getTransform();
-            Rectangle imgRect;
-            if (clipBounds != null) {
-                if (clipBounds.width == 0 || clipBounds.height == 0) {
-                    // Clip is 0 so no need to paint anything.
-                    return;
-                }
-                // Calculate integer rectangle that contains clipBounds after the transform, that is
-                // the minimum image size we can use to render the drawable
-                imgRect = transformRect(clipBounds, transform);
-                transform = new AffineTransform(
-                        transform.getScaleX(),
-                        transform.getShearY(),
-                        transform.getShearX(),
-                        transform.getScaleY(),
-                        transform.getTranslateX() - imgRect.x,
-                        transform.getTranslateY() - imgRect.y);
-            } else {
-                imgRect =
-                        new Rectangle(
-                                0, 0, layer.getImage().getWidth(), layer.getImage().getHeight());
-            }
-
-            // Create a temporary image to which the color filter will be applied.
-            BufferedImage image = new BufferedImage(imgRect.width, imgRect.height, TYPE_INT_ARGB);
-            Graphics2D imageBaseGraphics = (Graphics2D) image.getGraphics();
-            // Configure the Graphics2D object with drawing parameters and shader.
-            Graphics2D imageGraphics = createCustomGraphics(
-                    imageBaseGraphics, paint, compositeOnly,
-                    AlphaComposite.SRC_OVER);
-
-            // get a Graphics2D object configured with the drawing parameters, but no shader.
-            Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint,
-                    true /*compositeOnly*/, forceMode);
-            configuredGraphics.setTransform(IDENTITY_TRANSFORM);
-            try {
-                // The main draw operation.
-                // We translate the operation to take into account that the rendering does not
-                // know about the clipping area.
-                imageGraphics.setTransform(transform);
-                drawable.draw(imageGraphics, paint);
-
-                // Apply the color filter.
-                // Restore the original coordinates system and apply the filter only to the
-                // clipped area.
-                imageGraphics.setTransform(IDENTITY_TRANSFORM);
-                filter.applyFilter(imageGraphics, imgRect.width, imgRect.height);
-
-                // Draw the tinted image on the main layer using as start point the clipping
-                // upper left coordinates.
-                configuredGraphics.drawImage(image, imgRect.x, imgRect.y, null);
-                layer.change();
-            } finally {
-                // dispose Graphics2D objects
-                imageGraphics.dispose();
-                imageBaseGraphics.dispose();
-                configuredGraphics.dispose();
-            }
-        }
-    }
-
-    private void drawOnGraphics(Graphics2D g, Drawable drawable, Paint_Delegate paint,
-            Layer layer) {
-        try {
-            drawable.draw(g, paint);
-            layer.change();
-        } finally {
-            g.dispose();
-        }
-    }
-
-    private GcSnapshot doRestore() {
-        if (mPrevious != null) {
-            if (mLocalLayer != null) {
-                // prepare to blit the layers in which we have draw, in the layer beneath
-                // them, starting with the top one (which is the current local layer).
-                int i = mLayers.size() - 1;
-                int flags;
-                do {
-                    Layer dstLayer = mLayers.get(i - 1);
-
-                    restoreLayer(dstLayer);
-
-                    flags = dstLayer.getFlags();
-                    i--;
-                } while (i > 0 && (flags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0);
-            }
-
-            // if this snapshot does not save everything, then set the previous snapshot
-            // to this snapshot content
-
-            // didn't save the matrix? set the current matrix on the previous snapshot
-            if ((mFlags & Canvas.MATRIX_SAVE_FLAG) == 0) {
-                AffineTransform mtx = getTransform();
-                for (Layer layer : mPrevious.mLayers) {
-                    layer.getGraphics().setTransform(mtx);
-                }
-            }
-
-            // didn't save the clip? set the current clip on the previous snapshot
-            if ((mFlags & Canvas.CLIP_SAVE_FLAG) == 0) {
-                Shape clip = getClip();
-                for (Layer layer : mPrevious.mLayers) {
-                    layer.setClip(clip);
-                }
-            }
-        }
-
-        for (Layer layer : mLayers) {
-            layer.getGraphics().dispose();
-        }
-
-        return mPrevious;
-    }
-
-    private void restoreLayer(Layer dstLayer) {
-
-        Graphics2D baseGfx = dstLayer.getImage().createGraphics();
-
-        // if the layer contains an original copy this means the flags
-        // didn't restrict drawing to the local layer and we need to make sure the
-        // layer bounds in the layer beneath didn't receive any drawing.
-        // so we use the originalCopy to erase the new drawings in there.
-        BufferedImage originalCopy = dstLayer.getOriginalCopy();
-        if (originalCopy != null) {
-            Graphics2D g = (Graphics2D) baseGfx.create();
-            g.setComposite(AlphaComposite.Src);
-
-            g.drawImage(originalCopy,
-                    mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
-                    0, 0, mLayerBounds.width(), mLayerBounds.height(),
-                    null);
-            g.dispose();
-        }
-
-        // now draw put the content of the local layer onto the layer,
-        // using the paint information
-        Graphics2D g = createCustomGraphics(baseGfx, mLocalLayerPaint,
-                true /*alphaOnly*/, 0 /*forceMode*/);
-
-        g.drawImage(mLocalLayer.getImage(),
-                mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
-                mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
-                null);
-        g.dispose();
-
-        baseGfx.dispose();
-    }
-
-    /**
-     * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
-     * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
-     */
-    private Graphics2D createCustomGraphics(Graphics2D original, Paint_Delegate paint,
-            boolean compositeOnly, int forceMode) {
-        // make new one graphics
-        Graphics2D g = (Graphics2D) original.create();
-
-        if (paint == null) {
-            return g;
-        }
-
-        // configure it
-
-        if (paint.isAntiAliased()) {
-            g.setRenderingHint(
-                    RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
-            g.setRenderingHint(
-                    RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
-        }
-
-        // set the shader first, as it'll replace the color if it can be used it.
-        if (!compositeOnly) {
-            setShader(g, paint);
-            // set the stroke
-            g.setStroke(paint.getJavaStroke());
-        }
-        // set the composite.
-        setComposite(g, paint, compositeOnly, forceMode);
-
-        return g;
-    }
-
-    private void setShader(Graphics2D g, Paint_Delegate paint) {
-        Shader_Delegate shaderDelegate = paint.getShader();
-        if (shaderDelegate != null) {
-            if (shaderDelegate.isSupported()) {
-                java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
-                assert shaderPaint != null;
-                g.setPaint(shaderPaint);
-                return;
-            } else {
-                Bridge.getLog().fidelityWarning(ILayoutLog.TAG_SHADER,
-                        shaderDelegate.getSupportMessage(), null, null, null);
-            }
-        }
-
-        // if no shader, use the paint color
-        g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
-    }
-
-    private void setComposite(Graphics2D g, Paint_Delegate paint, boolean usePaintAlpha,
-            int forceMode) {
-        // the alpha for the composite. Always opaque if the normal paint color is used since
-        // it contains the alpha
-        int alpha = usePaintAlpha ? paint.getAlpha() : 0xFF;
-        Shader_Delegate shader = paint.getShader();
-        if (shader != null) {
-            alpha = (int)(alpha * shader.getAlpha());
-        }
-        if (forceMode != 0) {
-            g.setComposite(AlphaComposite.getInstance(forceMode, (float) alpha / 255.f));
-            return;
-        }
-        Mode mode = PorterDuff.intToMode(paint.getPorterDuffMode());
-        Composite composite = PorterDuffUtility.getComposite(mode, alpha);
-        g.setComposite(composite);
-    }
-
-    private void mapRect(AffineTransform matrix, RectF dst, RectF src) {
-        // array with 4 corners
-        float[] corners = new float[] {
-                src.left, src.top,
-                src.right, src.top,
-                src.right, src.bottom,
-                src.left, src.bottom,
-        };
-
-        // apply the transform to them.
-        matrix.transform(corners, 0, corners, 0, 4);
-
-        // now put the result in the rect. We take the min/max of Xs and min/max of Ys
-        dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
-        dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
-
-        dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
-        dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
-    }
-
-    /**
-     * Returns the clip of the oldest snapshot of the stack, appropriately translated to be
-     * expressed in the coordinate system of the latest snapshot.
-     */
-    public Rectangle getOriginalClip() {
-        GcSnapshot originalSnapshot = this;
-        while (originalSnapshot.mPrevious != null) {
-            originalSnapshot = originalSnapshot.mPrevious;
-        }
-        if (originalSnapshot.mLayers.isEmpty()) {
-            return null;
-        }
-        Graphics2D graphics2D = originalSnapshot.mLayers.get(0).getGraphics();
-        Rectangle bounds = graphics2D.getClipBounds();
-        if (bounds == null) {
-            return null;
-        }
-        try {
-            AffineTransform originalTransform =
-                    ((Graphics2D) graphics2D.create()).getTransform().createInverse();
-            AffineTransform latestTransform = getTransform().createInverse();
-            bounds.x += latestTransform.getTranslateX() - originalTransform.getTranslateX();
-            bounds.y += latestTransform.getTranslateY() - originalTransform.getTranslateY();
-        } catch (NoninvertibleTransformException e) {
-            Bridge.getLog().warning(null, "Non invertible transformation", null, null);
-        }
-        return bounds;
-    }
-
-}
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
index c2fb3e1..c8e5009 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
@@ -18,7 +18,6 @@
 
 import com.android.ide.common.rendering.api.HardwareConfig;
 import com.android.ide.common.rendering.api.RenderResources;
-import com.android.ide.common.rendering.api.ResourceNamespace;
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.SessionParams;
@@ -38,7 +37,6 @@
 
 import android.R.id;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.util.DisplayMetrics;
@@ -148,7 +146,8 @@
         NavigationBar navBar = null;
 
         if (builder.mWindowBackground != null) {
-            Drawable d = ResourceHelper.getDrawable(builder.mWindowBackground, builder.mContext);
+            Drawable d = ResourceHelper.getDrawable(builder.mWindowBackground, builder.mContext,
+                    builder.mContext.getTheme());
             setBackground(d);
         }
 
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java b/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java
index 71e7fd2..8c3b128 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java
@@ -49,6 +49,7 @@
     private List<Attribute> mAttributes;
     private String mText;
     private String mName;
+    private boolean mIsWhitespace;
 
     // Used to end the document before the actual parser ends.
     private int mFinalDepth = -1;
@@ -119,6 +120,7 @@
         mDepth = mDelegate.getDepth();
         mText = mDelegate.getText();
         mName = mDelegate.getName();
+        mIsWhitespace = mNext == TEXT && mDelegate.isWhitespace();
         mPeeked = true;
         return mNext;
     }
@@ -203,6 +205,11 @@
         return returnValue;
     }
 
+    @Override
+    public boolean isWhitespace() throws XmlPullParserException {
+        return mPeeked ? mIsWhitespace : mDelegate.isWhitespace();
+    }
+
     private static class Attribute {
         @Nullable
         public final String namespace;
@@ -311,11 +318,6 @@
     }
 
     @Override
-    public boolean isWhitespace() throws XmlPullParserException {
-        throw new UnsupportedOperationException("Only few parser methods are supported.");
-    }
-
-    @Override
     public char[] getTextCharacters(int[] ints) {
         throw new UnsupportedOperationException("Only few parser methods are supported.");
     }
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java b/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java
deleted file mode 100644
index 7b70180..0000000
--- a/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.layoutlib.bridge.impl;
-
-import com.android.ide.common.rendering.api.IAnimationListener;
-import com.android.ide.common.rendering.api.Result;
-import com.android.ide.common.rendering.api.Result.Status;
-
-import android.animation.AnimationThread;
-import android.animation.Animator;
-
-public class PlayAnimationThread extends AnimationThread {
-
-    private final Animator mAnimator;
-
-    public PlayAnimationThread(Animator animator, RenderSessionImpl scene, String animName,
-            IAnimationListener listener) {
-        super(scene, animName, listener);
-        mAnimator = animator;
-    }
-
-    @Override
-    public Result preAnimation() {
-        // start the animation. This will send a message to the handler right away, so
-        // the queue is filled when this method returns.
-        mAnimator.start();
-
-        return Status.SUCCESS.createResult();
-    }
-
-    @Override
-    public void postAnimation() {
-        // nothing to be done.
-    }
-}
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java b/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
deleted file mode 100644
index 86f80fe..0000000
--- a/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.layoutlib.bridge.impl;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-
-import android.graphics.BlendComposite;
-import android.graphics.BlendComposite.BlendingMode;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffColorFilter_Delegate;
-
-import java.awt.AlphaComposite;
-import java.awt.Composite;
-
-/**
- * Provides various utility methods for {@link PorterDuffColorFilter_Delegate}.
- */
-public final class PorterDuffUtility {
-
-    private static final int MODES_COUNT = Mode.values().length;
-
-    // Make the class non-instantiable.
-    private PorterDuffUtility() {
-    }
-
-    /**
-     * Convert the porterDuffMode from the framework to its corresponding enum. This defaults to
-     * {@link Mode#SRC_OVER} for invalid modes.
-     */
-    public static Mode getPorterDuffMode(int porterDuffMode) {
-        if (porterDuffMode >= 0 && porterDuffMode < MODES_COUNT) {
-            return PorterDuff.intToMode(porterDuffMode);
-        }
-        Bridge.getLog().error(ILayoutLog.TAG_BROKEN,
-                String.format("Unknown PorterDuff.Mode: %1$d", porterDuffMode), null, null);
-        assert false;
-        return Mode.SRC_OVER;
-    }
-
-    /**
-     * A utility method to get the {@link Composite} that represents the filter for the given
-     * PorterDuff mode and the alpha. Defaults to {@link Mode#SRC_OVER} for invalid modes.
-     */
-    public static Composite getComposite(Mode mode, int alpha255) {
-        float alpha1 = alpha255 != 0xFF ? alpha255 / 255.f : 1.f;
-        switch (mode) {
-            case CLEAR:
-                return AlphaComposite.getInstance(AlphaComposite.CLEAR, alpha1);
-            case SRC:
-                return AlphaComposite.getInstance(AlphaComposite.SRC, alpha1);
-            case DST:
-                return AlphaComposite.getInstance(AlphaComposite.DST, alpha1);
-            case SRC_OVER:
-                return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha1);
-            case DST_OVER:
-                return AlphaComposite.getInstance(AlphaComposite.DST_OVER, alpha1);
-            case SRC_IN:
-                return AlphaComposite.getInstance(AlphaComposite.SRC_IN, alpha1);
-            case DST_IN:
-                return AlphaComposite.getInstance(AlphaComposite.DST_IN, alpha1);
-            case SRC_OUT:
-                return AlphaComposite.getInstance(AlphaComposite.SRC_OUT, alpha1);
-            case DST_OUT:
-                return AlphaComposite.getInstance(AlphaComposite.DST_OUT, alpha1);
-            case SRC_ATOP:
-                return AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha1);
-            case DST_ATOP:
-                return AlphaComposite.getInstance(AlphaComposite.DST_ATOP, alpha1);
-            case XOR:
-                return AlphaComposite.getInstance(AlphaComposite.XOR, alpha1);
-            case DARKEN:
-                return BlendComposite.getInstance(BlendingMode.DARKEN, alpha1);
-            case LIGHTEN:
-                return BlendComposite.getInstance(BlendingMode.LIGHTEN, alpha1);
-            case MULTIPLY:
-                return BlendComposite.getInstance(BlendingMode.MULTIPLY, alpha1);
-            case SCREEN:
-                return BlendComposite.getInstance(BlendingMode.SCREEN, alpha1);
-            case ADD:
-                return BlendComposite.getInstance(BlendingMode.ADD, alpha1);
-            case OVERLAY:
-                return BlendComposite.getInstance(BlendingMode.OVERLAY, alpha1);
-            default:
-                Bridge.getLog().fidelityWarning(ILayoutLog.TAG_BROKEN,
-                        String.format("Unsupported PorterDuff Mode: %1$s", mode.name()),
-                        null, null, null /*data*/);
-
-                return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha1);
-        }
-    }
-}
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index 2ab0509..d444754 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -28,10 +28,11 @@
 import com.android.resources.ScreenOrientation;
 import com.android.resources.ScreenRound;
 import com.android.resources.ScreenSize;
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
 import com.android.tools.layoutlib.annotations.VisibleForTesting;
 
 import android.animation.PropertyValuesHolder_Accessor;
-import android.animation.PropertyValuesHolder_Delegate;
 import android.content.res.Configuration;
 import android.os.HandlerThread_Delegate;
 import android.util.DisplayMetrics;
@@ -42,7 +43,10 @@
 import android.view.WindowManagerGlobal_Delegate;
 import android.view.inputmethod.InputMethodManager_Accessor;
 
+import java.util.Collections;
 import java.util.Locale;
+import java.util.Set;
+import java.util.WeakHashMap;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -64,6 +68,10 @@
  */
 public abstract class RenderAction<T extends RenderParams> {
 
+    private static final Set<String> COMPOSE_CLASS_FQNS =
+            Set.of("androidx.compose.ui.tooling.ComposeViewAdapter",
+                    "androidx.compose.ui.tooling.preview.ComposeViewAdapter");
+
     /**
      * The current context being rendered. This is set through {@link #acquire(long)} and
      * {@link #init(long)}, and unset in {@link #release()}.
@@ -75,6 +83,10 @@
 
     private BridgeContext mContext;
 
+    private static final Object sContextLock = new Object();
+    private static final Set<BridgeContext> sContexts =
+            Collections.newSetFromMap(new WeakHashMap<>());
+
     /**
      * Creates a renderAction.
      * <p>
@@ -129,10 +141,11 @@
         // build the context
         mContext = new BridgeContext(mParams.getProjectKey(), metrics, resources,
                 mParams.getAssets(), mParams.getLayoutlibCallback(), getConfiguration(mParams),
-                mParams.getTargetSdkVersion(), mParams.isRtlSupported(),
-                Boolean.TRUE.equals(mParams.getFlag(RenderParamsFlags.FLAG_ENABLE_SHADOW)),
-                Boolean.TRUE.equals(mParams.getFlag(RenderParamsFlags.FLAG_RENDER_HIGH_QUALITY_SHADOW)));
+                mParams.getTargetSdkVersion(), mParams.isRtlSupported());
 
+        synchronized (sContextLock) {
+            sContexts.add(mContext);
+        }
         setUp();
 
         return SUCCESS.createResult();
@@ -241,7 +254,7 @@
 
         // make sure the Resources object references the context (and other objects) for this
         // scene
-        mContext.initResources();
+        mContext.initResources(mParams.getAssets());
         sCurrentContext = mContext;
 
         // Set-up WindowManager
@@ -269,19 +282,12 @@
             mContext.disposeResources();
         }
 
-        if (sCurrentContext != null) {
-            // quit HandlerThread created during this session.
-            HandlerThread_Delegate.cleanUp(sCurrentContext);
-        }
-
         // clear the stored ViewConfiguration since the map is per density and not per context.
         ViewConfiguration_Accessor.clearConfigurations();
 
         // remove the InputMethodManager
         InputMethodManager_Accessor.tearDownEditMode();
 
-        sCurrentContext = null;
-
         Bridge.setLog(null);
         if (mContext != null) {
             mContext.getRenderResources().setLogger(null);
@@ -409,9 +415,52 @@
         if (locale != null && !locale.isEmpty()) config.locale = new Locale(locale);
 
         config.fontScale = params.getFontScale();
+        config.uiMode = params.getUiMode();
 
         // TODO: fill in more config info.
 
         return config;
     }
+
+    @Nullable
+    private static ClassLoader findComposeClassLoader(@NotNull BridgeContext context) {
+        for (String composeClassName: COMPOSE_CLASS_FQNS) {
+            try {
+                return context.getLayoutlibCallback().findClass(composeClassName).getClassLoader();
+            } catch (Throwable ignore) {}
+        }
+
+        return null;
+    }
+
+    @Nullable
+    public static BridgeContext findContextFor(@NotNull ClassLoader classLoader) {
+        synchronized (sContextLock) {
+            for (BridgeContext c : RenderAction.sContexts) {
+                if (c == null) {
+                    continue;
+                }
+                try {
+                    if (findComposeClassLoader(c) == classLoader) {
+                        return c;
+                    }
+                } catch (Throwable ignore) {
+                }
+            }
+            return null;
+        }
+    }
+
+    protected void dispose() {
+        synchronized (sContextLock) {
+            sContexts.remove(mContext);
+        }
+
+        if (sCurrentContext != null) {
+            // quit HandlerThread created during this session.
+            HandlerThread_Delegate.cleanUp(sCurrentContext);
+        }
+
+        sCurrentContext = null;
+    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
index dc05951..60c3864 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
@@ -27,7 +27,7 @@
 
 import android.annotation.NonNull;
 import android.graphics.Bitmap;
-import android.graphics.Bitmap_Delegate;
+import android.graphics.Bitmap.Config;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.StateListDrawable;
@@ -115,7 +115,7 @@
         content.setBackground(d);
 
         // Set the AttachInfo on the root view.
-        AttachInfo_Accessor.setAttachInfo(content);
+        AttachInfo_Accessor.setAttachInfo(content, null);
 
         // Measure.
         int w = d.getIntrinsicWidth();
@@ -153,8 +153,11 @@
         BufferedImage image = getImage(w, h);
 
         // Create an Android bitmap around the BufferedImage.
-        Bitmap bitmap = Bitmap_Delegate.createBitmap(image,
-                true /*isMutable*/, hardwareConfig.getDensity());
+        Bitmap bitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(),
+                Config.ARGB_8888);
+        bitmap.setPixels(image.getRGB(0, 0, image.getWidth(), image.getHeight(),
+                null, 0, image.getWidth()), 0, image.getWidth(), 0, 0, image
+                .getWidth(), image.getHeight());
 
         // Create a Canvas around the Android bitmap.
         Canvas canvas = new Canvas(bitmap);
@@ -162,6 +165,10 @@
 
         // Draw.
         content.draw(canvas);
+        int[] pixels = new int[image.getWidth() * image.getHeight()];
+        bitmap.getPixels(pixels, 0, image.getWidth(), 0, 0, image.getWidth(),
+                image.getHeight());
+        image.setRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());
 
         // Detach root from window after draw.
         AttachInfo_Accessor.detachFromWindow(content);
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 93d3df8..8c46e33 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -47,28 +47,43 @@
 import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
 import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
 import com.android.tools.idea.validator.LayoutValidator;
+import com.android.tools.idea.validator.ValidatorData.Issue.IssueBuilder;
+import com.android.tools.idea.validator.ValidatorData.Level;
+import com.android.tools.idea.validator.ValidatorData.Type;
+import com.android.tools.idea.validator.ValidatorHierarchy;
 import com.android.tools.idea.validator.ValidatorResult;
 import com.android.tools.idea.validator.ValidatorResult.Builder;
-import com.android.tools.layoutlib.java.System_Delegate;
-import com.android.utils.Pair;
+import com.android.tools.idea.validator.hierarchy.CustomHierarchyHelper;
+import com.android.tools.layoutlib.annotations.NotNull;
 
+import android.animation.AnimationHandler;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.Context;
 import android.graphics.Bitmap;
-import android.graphics.Bitmap_Delegate;
 import android.graphics.Canvas;
-import android.graphics.NinePatch_Delegate;
-import android.os.Looper;
+import android.graphics.HardwareRenderer;
+import android.graphics.LayoutlibRenderer;
+import android.graphics.PixelFormat;
+import android.graphics.RenderNode;
+import android.graphics.drawable.AnimatedVectorDrawable_VectorDrawableAnimatorUI_Delegate;
+import android.media.Image;
+import android.media.Image.Plane;
+import android.media.ImageReader;
 import android.preference.Preference_Delegate;
+import android.util.Pair;
+import android.util.TimeUtils;
 import android.view.AttachInfo_Accessor;
 import android.view.BridgeInflater;
 import android.view.Choreographer_Delegate;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.MeasureSpec;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewGroup.MarginLayoutParams;
 import android.view.ViewParent;
+import android.view.WindowManagerImpl;
 import android.widget.AbsListView;
 import android.widget.AbsSpinner;
 import android.widget.ActionMenuView;
@@ -82,22 +97,24 @@
 import android.widget.TabHost.TabSpec;
 import android.widget.TabWidget;
 
-import java.awt.AlphaComposite;
-import java.awt.Color;
-import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.IntBuffer;
 import java.util.ArrayList;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
 
-import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchyAndroid_ViewElementClassNamesAndroid_Delegate;
+import com.android.internal.R;
+import android.content.res.TypedArray;
 
 import static com.android.ide.common.rendering.api.Result.Status.ERROR_INFLATION;
 import static com.android.ide.common.rendering.api.Result.Status.ERROR_NOT_INFLATED;
 import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN;
 import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.isInstanceOf;
+import static com.android.layoutlib.common.util.ReflectionUtils.isInstanceOf;
 
 /**
  * Class implementing the render session.
@@ -116,7 +133,6 @@
     private BridgeInflater mInflater;
     private ViewGroup mViewRoot;
     private FrameLayout mContentRoot;
-    private Canvas mCanvas;
     private int mMeasuredScreenWidth = -1;
     private int mMeasuredScreenHeight = -1;
     /** If >= 0, a frame will be executed */
@@ -130,7 +146,19 @@
     private List<ViewInfo> mSystemViewInfoList;
     private Layout.Builder mLayoutBuilder;
     private boolean mNewRenderSize;
+    private ImageReader mImageReader;
+    private Image mNativeImage;
+    private LayoutlibRenderer mRenderer = new LayoutlibRenderer();
+
+    // Passed in MotionEvent initialization when dispatching a touch event.
+    private final MotionEvent.PointerProperties[] mPointerProperties =
+            MotionEvent.PointerProperties.createArray(1);
+    private final MotionEvent.PointerCoords[] mPointerCoords =
+            MotionEvent.PointerCoords.createArray(1);
+
+    private long mLastActionDownTimeNanos = -1;
     @Nullable private ValidatorResult mValidatorResult = null;
+    @Nullable private ValidatorHierarchy mValidatorHierarchy = null;
 
     private static final class PostInflateException extends Exception {
         private static final long serialVersionUID = 1L;
@@ -183,6 +211,11 @@
         ILayoutPullParser layoutParser = params.getLayoutDescription();
         mBlockParser = new BridgeXmlBlockParser(layoutParser, context, layoutParser.getLayoutNamespace());
 
+        Bitmap.setDefaultDensity(params.getHardwareConfig().getDensity().getDpiValue());
+
+        // Needed in order to initialize static state of ImageReader
+        ImageReader.nativeClassInit();
+
         return SUCCESS.createResult();
     }
 
@@ -191,16 +224,15 @@
      */
     private void measureLayout(@NonNull SessionParams params) {
         // only do the screen measure when needed.
-        if (mMeasuredScreenWidth != -1) {
-            return;
+        int previousWidth = mMeasuredScreenWidth;
+        int previousHeight = mMeasuredScreenHeight;
+        HardwareConfig hardwareConfig = params.getHardwareConfig();
+        if (mMeasuredScreenWidth == -1) {
+            mMeasuredScreenWidth = hardwareConfig.getScreenWidth();
+            mMeasuredScreenHeight = hardwareConfig.getScreenHeight();
         }
 
         RenderingMode renderingMode = params.getRenderingMode();
-        HardwareConfig hardwareConfig = params.getHardwareConfig();
-
-        mNewRenderSize = true;
-        mMeasuredScreenWidth = hardwareConfig.getScreenWidth();
-        mMeasuredScreenHeight = hardwareConfig.getScreenHeight();
 
         if (renderingMode != RenderingMode.NORMAL) {
             int widthMeasureSpecMode = renderingMode.getHorizAction() == SizeAction.EXPAND ?
@@ -224,55 +256,62 @@
             // and apply this to the screen size.
 
             View measuredView = mContentRoot.getChildAt(0);
+            if (measuredView == null) {
+                return;
+            }
 
+            int maxWidth = hardwareConfig.getScreenWidth();
+            int maxHeight = hardwareConfig.getScreenHeight();
             // first measure the full layout, with EXACTLY to get the size of the
             // content as it is inside the decor/dialog
             Pair<Integer, Integer> exactMeasure = measureView(
                     mViewRoot, measuredView,
-                    mMeasuredScreenWidth, MeasureSpec.EXACTLY,
-                    mMeasuredScreenHeight, MeasureSpec.EXACTLY);
+                    maxWidth, MeasureSpec.EXACTLY,
+                    maxHeight, MeasureSpec.EXACTLY);
 
             // now measure the content only using UNSPECIFIED (where applicable, based on
             // the rendering mode). This will give us the size the content needs.
             Pair<Integer, Integer> neededMeasure = measureView(
-                    mContentRoot, mContentRoot.getChildAt(0),
-                    mMeasuredScreenWidth, widthMeasureSpecMode,
-                    mMeasuredScreenHeight, heightMeasureSpecMode);
-            int neededWidth = neededMeasure.getFirst();
-            int neededHeight = neededMeasure.getSecond();
+                    mContentRoot, measuredView,
+                    maxWidth, widthMeasureSpecMode,
+                    maxHeight, heightMeasureSpecMode);
 
             // If measuredView is not null, exactMeasure nor result will be null.
-            assert (exactMeasure != null && neededMeasure != null) || measuredView == null;
+            assert (exactMeasure != null && neededMeasure != null);
 
             // now look at the difference and add what is needed.
-            if (renderingMode.getHorizAction() == SizeAction.EXPAND) {
-                int measuredWidth = exactMeasure.getFirst();
-                if (neededWidth > measuredWidth) {
-                    mMeasuredScreenWidth += neededWidth - measuredWidth;
-                }
-                if (mMeasuredScreenWidth < measuredWidth) {
-                    // If the screen width is less than the exact measured width,
-                    // expand to match.
-                    mMeasuredScreenWidth = measuredWidth;
-                }
-            } else if (renderingMode.getHorizAction() == SizeAction.SHRINK) {
-                mMeasuredScreenWidth = neededWidth;
-            }
-
-            if (renderingMode.getVertAction() == SizeAction.EXPAND) {
-                int measuredHeight = exactMeasure.getSecond();
-                if (neededHeight > measuredHeight) {
-                    mMeasuredScreenHeight += neededHeight - measuredHeight;
-                }
-                if (mMeasuredScreenHeight < measuredHeight) {
-                    // If the screen height is less than the exact measured height,
-                    // expand to match.
-                    mMeasuredScreenHeight = measuredHeight;
-                }
-            } else if (renderingMode.getVertAction() == SizeAction.SHRINK) {
-                mMeasuredScreenHeight = neededHeight;
-            }
+            mMeasuredScreenWidth = calcSize(mMeasuredScreenWidth, neededMeasure.first,
+                    exactMeasure.first, renderingMode.getHorizAction());
+            mMeasuredScreenHeight = calcSize(mMeasuredScreenHeight, neededMeasure.second,
+                    exactMeasure.second, renderingMode.getVertAction());
         }
+        mNewRenderSize =
+                mMeasuredScreenWidth != previousWidth || mMeasuredScreenHeight != previousHeight;
+    }
+
+    /**
+     * Calculate the required vertical (height) or horizontal (width) size of the canvas for the
+     * view, given current size requirements.
+     * @param currentSize current size of the canvas
+     * @param neededSize the size the content actually needs
+     * @param measuredSize the measured size of the content (restricted by the current size)
+     * @param action the {@link SizeAction} of the view
+     * @return the size the canvas should be
+     */
+    private static int calcSize(int currentSize, int neededSize, int measuredSize,
+            SizeAction action) {
+        if (action == SizeAction.EXPAND) {
+            if (neededSize > measuredSize) {
+                currentSize += neededSize - measuredSize;
+            }
+            if (currentSize < measuredSize) {
+                // If the screen size is less than the exact measured size, expand to match.
+                currentSize = measuredSize;
+            }
+        } else if (action == SizeAction.SHRINK) {
+            currentSize = neededSize;
+        }
+        return currentSize;
     }
 
     /**
@@ -329,7 +368,7 @@
             context.popParser();
 
             // set the AttachInfo on the root view.
-            AttachInfo_Accessor.setAttachInfo(mViewRoot);
+            AttachInfo_Accessor.setAttachInfo(mViewRoot, mRenderer);
 
             // post-inflate process. For now this supports TabHost/TabWidget
             postInflateProcess(view, params.getLayoutlibCallback(), isPreference ? view : null);
@@ -346,8 +385,6 @@
                     visitAllChildren(mViewRoot, 0, 0, params.getExtendedViewInfoMode(),
                     false);
 
-            Choreographer_Delegate.clearFrames();
-
             return SUCCESS.createResult();
         } catch (PostInflateException e) {
             return ERROR_INFLATION.createResult(e.getMessage(), e);
@@ -387,18 +424,19 @@
     }
 
     /**
-     * Renders the given view hierarchy to the passed canvas and returns the result of the render
-     * operation.
-     * @param canvas an optional canvas to render the views to. If null, only the measure and
-     * layout steps will be executed.
+     * Creates a display list for the root view and draws that display list with a "hardware"
+     * renderer. In layoutlib the renderer is not actually hardware (in contrast to the actual
+     * android) but pretends to be so in order to draw all the advanced android features (e.g.
+     * shadows).
      */
-    private static Result renderAndBuildResult(@NonNull ViewGroup viewRoot, @Nullable Canvas canvas) {
-        if (canvas == null) {
-            return SUCCESS.createResult();
-        }
+    private static Result renderAndBuildResult(@NonNull ViewGroup viewRoot,
+        @NonNull HardwareRenderer renderer) {
 
         AttachInfo_Accessor.dispatchOnPreDraw(viewRoot);
-        viewRoot.draw(canvas);
+
+        RenderNode node = viewRoot.updateDisplayListIfDirty();
+        renderer.setContentRoot(node);
+        renderer.createRenderRequest().syncAndDraw();
 
         return SUCCESS.createResult();
     }
@@ -466,16 +504,14 @@
 
             HardwareConfig hardwareConfig = params.getHardwareConfig();
             Result renderResult = SUCCESS.createResult();
+            float scaleX = 1.0f;
+            float scaleY = 1.0f;
             if (onlyMeasure) {
                 // delete the canvas and image to reset them on the next full rendering
                 mImage = null;
-                mCanvas = null;
+                disposeImageSurface();
                 doLayout(getContext(), mViewRoot, mMeasuredScreenWidth, mMeasuredScreenHeight);
             } else {
-                // draw the views
-                // create the BufferedImage into which the layout will be rendered.
-                boolean newImage = false;
-
                 // When disableBitmapCaching is true, we do not reuse mImage and
                 // we create a new one in every render.
                 // This is useful when mImage is just a wrapper of Graphics2D so
@@ -483,8 +519,7 @@
                 boolean disableBitmapCaching = Boolean.TRUE.equals(params.getFlag(
                     RenderParamsFlags.FLAG_KEY_DISABLE_BITMAP_CACHING));
 
-                if (mNewRenderSize || mCanvas == null || disableBitmapCaching) {
-                    mNewRenderSize = false;
+                if (mNewRenderSize || mImageReader == null || disableBitmapCaching) {
                     if (params.getImageFactory() != null) {
                         mImage = params.getImageFactory().getImage(
                                 mMeasuredScreenWidth,
@@ -494,99 +529,104 @@
                                 mMeasuredScreenWidth,
                                 mMeasuredScreenHeight,
                                 BufferedImage.TYPE_INT_ARGB);
-                        newImage = true;
-                    }
-
-                    if (params.isTransparentBackground()) {
-                        // since we override the content, it's the same as if it was a new image.
-                        newImage = true;
-                        Graphics2D gc = mImage.createGraphics();
-                        gc.setColor(new Color(0, true));
-                        gc.setComposite(AlphaComposite.Src);
-                        gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
-                        gc.dispose();
-                    }
-
-                    // create an Android bitmap around the BufferedImage
-                    Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
-                            true /*isMutable*/, hardwareConfig.getDensity());
-
-                    if (mCanvas == null) {
-                        // create a Canvas around the Android bitmap
-                        mCanvas = new Canvas(bitmap);
-                    } else {
-                        mCanvas.setBitmap(bitmap);
                     }
 
                     boolean enableImageResizing =
                             mImage.getWidth() != mMeasuredScreenWidth &&
-                            mImage.getHeight() != mMeasuredScreenHeight &&
-                            Boolean.TRUE.equals(params.getFlag(
-                                    RenderParamsFlags.FLAG_KEY_RESULT_IMAGE_AUTO_SCALE));
+                                    mImage.getHeight() != mMeasuredScreenHeight &&
+                                    Boolean.TRUE.equals(params.getFlag(
+                                            RenderParamsFlags.FLAG_KEY_RESULT_IMAGE_AUTO_SCALE));
 
-                    if (enableImageResizing) {
-                        float scaleX = (float)mImage.getWidth() / mMeasuredScreenWidth;
-                        float scaleY = (float)mImage.getHeight() / mMeasuredScreenHeight;
-                        mCanvas.scale(scaleX, scaleY);
+                    if (enableImageResizing || mNewRenderSize) {
+                        disposeImageSurface();
                     }
 
-                    mCanvas.setDensity(hardwareConfig.getDensity().getDpiValue());
-                }
+                    if (enableImageResizing) {
+                        scaleX = mImage.getWidth() * 1.0f / mMeasuredScreenWidth;
+                        scaleY = mImage.getHeight() * 1.0f / mMeasuredScreenHeight;
+                        mRenderer.setScale(scaleX, scaleY);
+                    } else {
+                        mRenderer.setScale(1.0f, 1.0f);
+                    }
 
-                if (freshRender && !newImage) {
-                    Graphics2D gc = mImage.createGraphics();
-                    gc.setComposite(AlphaComposite.Src);
-
-                    gc.setColor(new Color(0x00000000, true));
-                    gc.fillRect(0, 0,
-                            mMeasuredScreenWidth, mMeasuredScreenHeight);
-
-                    // done
-                    gc.dispose();
+                    if (mImageReader == null) {
+                        mImageReader = ImageReader.newInstance(mImage.getWidth(), mImage.getHeight(), PixelFormat.RGBA_8888, 1);
+                        mRenderer.setSurface(mImageReader.getSurface());
+                        mNativeImage = mImageReader.acquireNextImage();
+                    }
+                    mNewRenderSize = false;
                 }
 
                 doLayout(getContext(), mViewRoot, mMeasuredScreenWidth, mMeasuredScreenHeight);
+
                 if (mElapsedFrameTimeNanos >= 0) {
-                    long initialTime = System_Delegate.nanoTime();
                     if (!mFirstFrameExecuted) {
                         // We need to run an initial draw call to initialize the animations
-                        renderAndBuildResult(mViewRoot, NOP_CANVAS);
+                        AttachInfo_Accessor.dispatchOnPreDraw(mViewRoot);
+                        mViewRoot.draw(NOP_CANVAS);
 
                         // The first frame will initialize the animations
-                        Choreographer_Delegate.doFrame(initialTime);
                         mFirstFrameExecuted = true;
                     }
                     // Second frame will move the animations
-                    Choreographer_Delegate.doFrame(initialTime + mElapsedFrameTimeNanos);
+                    AnimatedVectorDrawable_VectorDrawableAnimatorUI_Delegate.sFrameTime =
+                            mElapsedFrameTimeNanos / 1000000;
                 }
-                renderResult = renderAndBuildResult(mViewRoot, mCanvas);
+
+                final TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
+                float lightY = a.getDimension(R.styleable.Lighting_lightY, 0);
+                float lightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
+                float lightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
+                float ambientShadowAlpha = a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0);
+                float spotShadowAlpha = a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0);
+                a.recycle();
+
+                mRenderer.setLightSourceGeometry(mMeasuredScreenWidth / 2, lightY, lightZ, lightRadius);
+                mRenderer.setLightSourceAlpha(ambientShadowAlpha, spotShadowAlpha);
+
+                renderResult = renderAndBuildResult(mViewRoot, mRenderer);
+
+                int[] imageData = ((DataBufferInt) mImage.getRaster().getDataBuffer()).getData();
+
+                Plane[] planes = mNativeImage.getPlanes();
+                IntBuffer buff = planes[0].getBuffer().asIntBuffer();
+                int len = buff.remaining();
+                buff.get(imageData, 0, len);
             }
 
             mSystemViewInfoList =
                     visitAllChildren(mViewRoot, 0, 0, params.getExtendedViewInfoMode(),
                     false);
 
-            try {
-                boolean enableLayoutValidation = Boolean.TRUE.equals(params.getFlag(RenderParamsFlags.FLAG_ENABLE_LAYOUT_VALIDATOR));
-                boolean enableLayoutValidationImageCheck = Boolean.TRUE.equals(
-                         params.getFlag(RenderParamsFlags.FLAG_ENABLE_LAYOUT_VALIDATOR_IMAGE_CHECK));
+            boolean enableLayoutValidation = Boolean.TRUE.equals(params.getFlag(RenderParamsFlags.FLAG_ENABLE_LAYOUT_VALIDATOR));
+            boolean enableLayoutValidationImageCheck = Boolean.TRUE.equals(
+                    params.getFlag(RenderParamsFlags.FLAG_ENABLE_LAYOUT_VALIDATOR_IMAGE_CHECK));
 
+            try {
                 if (enableLayoutValidation && !getViewInfos().isEmpty()) {
-                    AccessibilityHierarchyAndroid_ViewElementClassNamesAndroid_Delegate.sLayoutlibCallback =
+                    CustomHierarchyHelper.sLayoutlibCallback =
                             getContext().getLayoutlibCallback();
 
                     BufferedImage imageToPass =
                             enableLayoutValidationImageCheck ? getImage() : null;
-                    ValidatorResult validatorResult =
-                            LayoutValidator.validate(((View) getViewInfos().get(0).getViewObject()), imageToPass);
-                    setValidatorResult(validatorResult);
+
+                    ValidatorHierarchy hierarchy = LayoutValidator.buildHierarchy(
+                            ((View) getViewInfos().get(0).getViewObject()),
+                            imageToPass,
+                            scaleX,
+                            scaleY);
+                    setValidatorHierarchy(hierarchy);
                 }
             } catch (Throwable e) {
-                ValidatorResult.Builder builder = new Builder();
-                builder.mMetric.mErrorMessage = e.getMessage();
-                setValidatorResult(builder.build());
+                StringWriter sw = new StringWriter();
+                PrintWriter pw = new PrintWriter(sw);
+                e.printStackTrace(pw);
+
+                ValidatorHierarchy hierarchy = new ValidatorHierarchy();
+                hierarchy.mErrorMessage = sw.toString();
+                setValidatorHierarchy(hierarchy);
             } finally {
-                AccessibilityHierarchyAndroid_ViewElementClassNamesAndroid_Delegate.sLayoutlibCallback = null;
+                CustomHierarchyHelper.sLayoutlibCallback = null;
             }
 
             // success!
@@ -624,7 +664,7 @@
         viewToMeasure.measure(w_spec, h_spec);
 
         if (measuredView != null) {
-            return Pair.of(measuredView.getMeasuredWidth(), measuredView.getMeasuredHeight());
+            return Pair.create(measuredView.getMeasuredWidth(), measuredView.getMeasuredHeight());
         }
 
         return null;
@@ -683,11 +723,11 @@
                                 Pair<View, Boolean> pair = context.inflateView(
                                         binding.getHeaderAt(i),
                                         list, false, skipCallbackParser);
-                                if (pair.getFirst() != null) {
-                                    list.addHeaderView(pair.getFirst());
+                                if (pair.first != null) {
+                                    list.addHeaderView(pair.first);
                                 }
 
-                                skipCallbackParser |= pair.getSecond();
+                                skipCallbackParser |= pair.second;
                             }
 
                             count = binding.getFooterCount();
@@ -695,11 +735,11 @@
                                 Pair<View, Boolean> pair = context.inflateView(
                                         binding.getFooterAt(i),
                                         list, false, skipCallbackParser);
-                                if (pair.getFirst() != null) {
-                                    list.addFooterView(pair.getFirst());
+                                if (pair.first != null) {
+                                    list.addFooterView(pair.first);
                                 }
 
-                                skipCallbackParser |= pair.getSecond();
+                                skipCallbackParser |= pair.second;
                             }
                         }
 
@@ -1021,7 +1061,7 @@
             int shiftX = -scrollX + Math.round(view.getTranslationX()) + hOffset;
             int shiftY = -scrollY + Math.round(view.getTranslationY()) + vOffset;
             result = new ViewInfo(view.getClass().getName(),
-                    getContext().getViewKey(view),
+                    getViewKey(view),
                     shiftX + view.getLeft(),
                     shiftY + view.getTop(),
                     shiftX + view.getRight(),
@@ -1088,6 +1128,17 @@
     @Nullable
     private Object getViewKey(View view) {
         BridgeContext context = getContext();
+        if ("com.google.android.material.tabs.TabLayout.TabView".equals(
+                view.getClass().getCanonicalName())) {
+            // TabView from the material library is a LinearLayout, but it is defined in XML
+            // as a TabItem. Because of this, TabView doesn't get the correct cookie, but its
+            // children do. So this reassigns the cookie from the first child to link the XML
+            // TabItem to the actual TabView view.
+            ViewGroup tabView = (ViewGroup)view;
+            if (tabView.getChildCount() > 0) {
+                return context.getViewKey(tabView.getChildAt(0));
+            }
+        }
         if (!(view instanceof MenuView.ItemView)) {
             return context.getViewKey(view);
         }
@@ -1151,6 +1202,15 @@
         mValidatorResult = result;
     }
 
+    @Nullable
+    public ValidatorHierarchy getValidatorHierarchy() {
+        return mValidatorHierarchy;
+    }
+
+    public void setValidatorHierarchy(@NotNull ValidatorHierarchy validatorHierarchy) {
+        mValidatorHierarchy = validatorHierarchy;
+    }
+
     public void setScene(RenderSession session) {
         mScene = session;
     }
@@ -1159,38 +1219,79 @@
         return mScene;
     }
 
+    public void dispatchTouchEvent(int motionEventType, long currentTimeNanos, float x, float y) {
+        // Events should be dispatched to the top window if there are more than one present.
+        WindowManagerImpl wm =
+                (WindowManagerImpl)getContext().getSystemService(Context.WINDOW_SERVICE);
+        ViewGroup root = wm.getCurrentRootView();
+        if (root == null) {
+            root = mViewRoot;
+        }
+        if (root == null) {
+            return;
+        }
+        if (motionEventType == MotionEvent.ACTION_DOWN) {
+            mLastActionDownTimeNanos = currentTimeNanos;
+        }
+        // Ignore events not started with MotionEvent.ACTION_DOWN
+        if (mLastActionDownTimeNanos == -1) {
+            return;
+        }
+
+        mPointerProperties[0].id = 0;
+        mPointerProperties[0].toolType = MotionEvent.TOOL_TYPE_FINGER;
+
+        mPointerCoords[0].clear();
+        mPointerCoords[0].x = x;
+        mPointerCoords[0].y = y;
+        mPointerCoords[0].pressure = 1.0f;
+        mPointerCoords[0].size = 1.0f;
+
+        MotionEvent event = MotionEvent.obtain(
+            mLastActionDownTimeNanos / TimeUtils.NANOS_PER_MS,
+            currentTimeNanos / TimeUtils.NANOS_PER_MS,
+            motionEventType,
+            1, mPointerProperties, mPointerCoords,
+            0, 0, 1.0f, 1.0f, 0, 0, 0, 0);
+
+        root.dispatchTouchEvent(event);
+    }
+
+    private void disposeImageSurface() {
+        if (mImageReader != null) {
+            mImageReader.close();
+            mImageReader = null;
+        }
+    }
+
+    @Override
     public void dispose() {
         try {
-            boolean createdLooper = false;
-            if (Looper.myLooper() == null) {
-                // Detaching the root view from the window will try to stop any running animations.
-                // The stop method checks that it can run in the looper so, if there is no current
-                // looper, we create a temporary one to complete the shutdown.
-                Bridge.prepareThread();
-                createdLooper = true;
-            }
+            mRenderer.destroy();
+            disposeImageSurface();
+            mImage = null;
+            // detachFromWindow might create Handler callbacks, thus before Handler_Delegate.dispose
             AttachInfo_Accessor.detachFromWindow(mViewRoot);
-            if (mCanvas != null) {
-                mCanvas.release();
-                mCanvas = null;
+            AnimationHandler animationHandler = AnimationHandler.sAnimatorHandler.get();
+            if (animationHandler != null) {
+                animationHandler.mDelayedCallbackStartTime.clear();
+                animationHandler.mAnimationCallbacks.clear();
+                animationHandler.mCommitCallbacks.clear();
             }
+            getContext().getSessionInteractiveData().dispose();
             if (mViewInfoList != null) {
                 mViewInfoList.clear();
             }
             if (mSystemViewInfoList != null) {
                 mSystemViewInfoList.clear();
             }
-            mImage = null;
+            mValidatorResult = null;
+            mValidatorHierarchy = null;
             mViewRoot = null;
             mContentRoot = null;
-            NinePatch_Delegate.clearCache();
-
-            if (createdLooper) {
-                Choreographer_Delegate.dispose();
-                Bridge.cleanupThread();
-            }
         } catch (Throwable t) {
             getContext().error("Error while disposing a RenderSession", t);
         }
+        super.dispose();
     }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index 0a36359..b344537 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -15,7 +15,7 @@
  */
 package com.android.layoutlib.bridge.impl;
 
-import com.android.SdkConstants;
+import com.android.ide.common.rendering.api.AndroidConstants;
 import com.android.ide.common.rendering.api.AssetRepository;
 import com.android.ide.common.rendering.api.DensityBasedResourceValue;
 import com.android.ide.common.rendering.api.ILayoutLog;
@@ -30,11 +30,17 @@
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeContext.Key;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.ninepatch.GraphicsUtilities;
 import com.android.ninepatch.NinePatch;
-import com.android.ninepatch.NinePatchChunk;
 import com.android.resources.Density;
 import com.android.resources.ResourceType;
 
+import org.ccil.cowan.tagsoup.HTMLSchema;
+import org.ccil.cowan.tagsoup.Parser;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -47,9 +53,12 @@
 import android.content.res.GradientColor;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
+import android.content.res.StringBlock;
+import android.content.res.StringBlock.Height;
 import android.graphics.Bitmap;
-import android.graphics.Bitmap_Delegate;
-import android.graphics.NinePatch_Delegate;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapFactory.Options;
 import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.graphics.Typeface_Accessor;
@@ -58,17 +67,42 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.NinePatchDrawable;
+import android.text.Annotation;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.SpannedString;
+import android.text.TextUtils;
+import android.text.style.AbsoluteSizeSpan;
+import android.text.style.BulletSpan;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.StrikethroughSpan;
+import android.text.style.StyleSpan;
+import android.text.style.SubscriptSpan;
+import android.text.style.SuperscriptSpan;
+import android.text.style.TypefaceSpan;
+import android.text.style.URLSpan;
+import android.text.style.UnderlineSpan;
 import android.util.TypedValue;
 
+import java.awt.image.BufferedImage;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.MalformedURLException;
+import java.io.StringReader;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import com.google.common.base.Strings;
+
 import static android.content.res.AssetManager.ACCESS_STREAMING;
 
 /**
@@ -77,7 +111,7 @@
 public final class ResourceHelper {
     private static final Key<Set<ResourceValue>> KEY_GET_DRAWABLE =
             Key.create("ResourceHelper.getDrawable");
-    private static final Pattern sFloatPattern = Pattern.compile("(-?[0-9]+(?:\\.[0-9]+)?)(.*)");
+    private static final Pattern sFloatPattern = Pattern.compile("(-?[0-9]*(?:\\.[0-9]+)?)(.*)");
     private static final float[] sFloatOut = new float[1];
 
     private static final TypedValue mValue = new TypedValue();
@@ -106,7 +140,7 @@
         }
 
         if (value.charAt(0) != '#') {
-            if (value.startsWith(SdkConstants.PREFIX_THEME_REF)) {
+            if (value.startsWith(AndroidConstants.PREFIX_THEME_REF)) {
                 throw new NumberFormatException(String.format(
                         "Attribute '%s' not found. Are you using the right theme?", value));
             }
@@ -339,18 +373,7 @@
         }
 
         String lowerCaseValue = stringValue.toLowerCase();
-        if (lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) {
-            try {
-                return getNinePatchDrawable(density, value.isFramework(), stringValue, context);
-            } catch (IOException e) {
-                // failed to read the file, we'll return null below.
-                Bridge.getLog().error(ILayoutLog.TAG_RESOURCES_READ,
-                        "Failed to load " + stringValue, e, null, null /*data*/);
-            }
-
-            return null;
-        } else if (lowerCaseValue.endsWith(".xml") ||
-                value.getResourceType() == ResourceType.AAPT) {
+        if (lowerCaseValue.endsWith(".xml") || value.getResourceType() == ResourceType.AAPT) {
             // create a block parser for the file
             try {
                 BridgeXmlBlockParser blockParser = getXmlBlockParser(context, value);
@@ -396,13 +419,39 @@
                         } catch (FileNotFoundException e) {
                             stream = null;
                         }
-                        bitmap =
-                                Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density);
+                        Options options = new Options();
+                        options.inDensity = density.getDpiValue();
+                        bitmap = BitmapFactory.decodeStream(stream, null, options);
+                        if (bitmap != null && bitmap.getNinePatchChunk() == null &&
+                                lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) {
+                            //We are dealing with a non-compiled nine patch.
+                            stream = repository.openNonAsset(0, stringValue, ACCESS_STREAMING);
+                            NinePatch ninePatch = NinePatch.load(stream, true /*is9Patch*/, false /* convert */);
+                            BufferedImage image = ninePatch.getImage();
+
+                            // width and height of the nine patch without the special border.
+                            int width = image.getWidth();
+                            int height = image.getHeight();
+
+                            // Get pixel data from image independently of its type.
+                            int[] imageData = GraphicsUtilities.getPixels(image, 0, 0, width,
+                                    height, null);
+
+                            bitmap = Bitmap.createBitmap(imageData, width, height, Config.ARGB_8888);
+
+                            bitmap.setDensity(options.inDensity);
+                            bitmap.setNinePatchChunk(ninePatch.getChunk().getSerializedChunk());
+                        }
                         Bridge.setCachedBitmap(stringValue, bitmap,
                                 value.isFramework() ? null : context.getProjectKey());
                     }
 
-                    return new BitmapDrawable(context.getResources(), bitmap);
+                    if (bitmap != null && bitmap.getNinePatchChunk() != null) {
+                        return new NinePatchDrawable(context.getResources(), bitmap, bitmap
+                                .getNinePatchChunk(), new Rect(), lowerCaseValue);
+                    } else {
+                        return new BitmapDrawable(context.getResources(), bitmap);
+                    }
                 } catch (IOException e) {
                     // we'll return null below
                     Bridge.getLog().error(ILayoutLog.TAG_RESOURCES_READ,
@@ -451,58 +500,6 @@
         return getFont(value.getValue(), context, theme, value.isFramework());
     }
 
-    private static Drawable getNinePatchDrawable(Density density, boolean isFramework,
-            String path, BridgeContext context) throws IOException {
-        // see if we still have both the chunk and the bitmap in the caches
-        NinePatchChunk chunk = Bridge.getCached9Patch(path,
-                isFramework ? null : context.getProjectKey());
-        Bitmap bitmap = Bridge.getCachedBitmap(path,
-                isFramework ? null : context.getProjectKey());
-
-        // if either chunk or bitmap is null, then we reload the 9-patch file.
-        if (chunk == null || bitmap == null) {
-            try {
-                AssetRepository repository = getAssetRepository(context);
-                if (!repository.isFileResource(path)) {
-                    return null;
-                }
-                InputStream stream = repository.openNonAsset(0, path, ACCESS_STREAMING);
-                NinePatch ninePatch = NinePatch.load(stream, true /*is9Patch*/,
-                        false /* convert */);
-                if (ninePatch != null) {
-                    if (chunk == null) {
-                        chunk = ninePatch.getChunk();
-
-                        Bridge.setCached9Patch(path, chunk,
-                                isFramework ? null : context.getProjectKey());
-                    }
-
-                    if (bitmap == null) {
-                        bitmap = Bitmap_Delegate.createBitmap(ninePatch.getImage(),
-                                false /*isMutable*/,
-                                density);
-
-                        Bridge.setCachedBitmap(path, bitmap,
-                                isFramework ? null : context.getProjectKey());
-                    }
-                }
-            } catch (MalformedURLException e) {
-                // URL is wrong, we'll return null below
-            }
-        }
-
-        if (chunk != null && bitmap != null) {
-            int[] padding = chunk.getPadding();
-            Rect paddingRect = new Rect(padding[0], padding[1], padding[2], padding[3]);
-
-            return new NinePatchDrawable(context.getResources(), bitmap,
-                    NinePatch_Delegate.serialize(chunk),
-                    paddingRect, null);
-        }
-
-        return null;
-    }
-
     /**
      * Looks for an attribute in the current theme.
      *
@@ -535,6 +532,175 @@
         return getBooleanThemeValue(resources, attrRef, defaultValue);
     }
 
+    /**
+     * This takes a resource string containing HTML tags for styling,
+     * and returns it correctly formatted to be displayed.
+     */
+    public static CharSequence parseHtml(String string) {
+        // The parser requires <li> tags to be surrounded by <ul> tags to handle whitespace
+        // correctly, though Android does not support <ul> tags.
+        String str = string.replaceAll("<li>", "<ul><li>")
+                .replaceAll("</li>","</li></ul>");
+        int firstTagIndex = str.indexOf('<');
+        int lastTagIndex = str.lastIndexOf('>');
+        StringBuilder stringBuilder = new StringBuilder(str.substring(0, firstTagIndex));
+        List<Tag> tagList = new ArrayList<>();
+        Map<String, Deque<Tag>> startStacks = new HashMap<>();
+        Parser parser = new Parser();
+        parser.setContentHandler(new DefaultHandler() {
+            @Override
+            public void startElement(String uri, String localName, String qName,
+                    Attributes attributes) {
+                if (!Strings.isNullOrEmpty(localName)) {
+                    Tag tag = new Tag(localName);
+                    tag.mStart = stringBuilder.length();
+                    tag.mAttributes = attributes;
+                    startStacks.computeIfAbsent(localName, key -> new ArrayDeque<>()).addFirst(tag);
+                }
+            }
+
+            @Override
+            public void endElement(String uri, String localName, String qName) {
+                if (!Strings.isNullOrEmpty(localName)) {
+                    Tag tag = startStacks.get(localName).removeFirst();
+                    tag.mEnd = stringBuilder.length();
+                    tagList.add(tag);
+                }
+            }
+
+            @Override
+            public void characters(char[] ch, int start, int length) {
+                stringBuilder.append(ch, start, length);
+            }
+        });
+        try {
+            parser.setProperty(Parser.schemaProperty, new HTMLSchema());
+            parser.parse(new InputSource(
+                    new StringReader(str.substring(firstTagIndex, lastTagIndex + 1))));
+        } catch (SAXException | IOException e) {
+            Bridge.getLog().warning(ILayoutLog.TAG_RESOURCES_FORMAT,
+                    "The string " + str + " is not valid HTML", null, null);
+            return str;
+        }
+        stringBuilder.append(str.substring(lastTagIndex + 1));
+        return applyStyles(stringBuilder, tagList);
+    }
+
+    /**
+     * This applies the styles from tagList that are supported by Android
+     * and returns a {@link SpannedString}.
+     * This should mirror {@link StringBlock#applyStyles}
+     */
+    @NonNull
+    private static SpannedString applyStyles(@NonNull StringBuilder stringBuilder,
+            @NonNull List<Tag> tagList) {
+        SpannableString spannableString = new SpannableString(stringBuilder);
+        for (Tag tag : tagList) {
+            int start = tag.mStart;
+            int end = tag.mEnd;
+            Attributes attrs = tag.mAttributes;
+            switch (tag.mLabel) {
+                case "b":
+                    spannableString.setSpan(new StyleSpan(Typeface.BOLD), start, end,
+                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    break;
+                case "i":
+                    spannableString.setSpan(new StyleSpan(Typeface.ITALIC), start, end,
+                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    break;
+                case "u":
+                    spannableString.setSpan(new UnderlineSpan(), start, end,
+                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    break;
+                case "tt":
+                    spannableString.setSpan(new TypefaceSpan("monospace"), start, end,
+                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    break;
+                case "big":
+                    spannableString.setSpan(new RelativeSizeSpan(1.25f), start, end,
+                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    break;
+                case "small":
+                    spannableString.setSpan(new RelativeSizeSpan(0.8f), start, end,
+                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    break;
+                case "sup":
+                    spannableString.setSpan(new SuperscriptSpan(), start, end,
+                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    break;
+                case "sub":
+                    spannableString.setSpan(new SubscriptSpan(), start, end,
+                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    break;
+                case "strike":
+                    spannableString.setSpan(new StrikethroughSpan(), start, end,
+                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    break;
+                case "li":
+                    StringBlock.addParagraphSpan(spannableString, new BulletSpan(10), start, end);
+                    break;
+                case "marquee":
+                    spannableString.setSpan(TextUtils.TruncateAt.MARQUEE, start, end,
+                            Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+                    break;
+                case "font":
+                    String heightAttr = attrs.getValue("height");
+                    if (heightAttr != null) {
+                        int height = Integer.parseInt(heightAttr);
+                        StringBlock.addParagraphSpan(spannableString, new Height(height), start,
+                                end);
+                    }
+
+                    String sizeAttr = attrs.getValue("size");
+                    if (sizeAttr != null) {
+                        int size = Integer.parseInt(sizeAttr);
+                        spannableString.setSpan(new AbsoluteSizeSpan(size, true), start, end,
+                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+
+                    String fgcolorAttr = attrs.getValue("fgcolor");
+                    if (fgcolorAttr != null) {
+                        spannableString.setSpan(StringBlock.getColor(fgcolorAttr, true), start, end,
+                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+
+                    String colorAttr = attrs.getValue("color");
+                    if (colorAttr != null) {
+                        spannableString.setSpan(StringBlock.getColor(colorAttr, true), start, end,
+                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+
+                    String bgcolorAttr = attrs.getValue("bgcolor");
+                    if (bgcolorAttr != null) {
+                        spannableString.setSpan(StringBlock.getColor(bgcolorAttr, false), start,
+                                end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+
+                    String faceAttr = attrs.getValue("face");
+                    if (faceAttr != null) {
+                        spannableString.setSpan(new TypefaceSpan(faceAttr), start, end,
+                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+                    break;
+                case "a":
+                    String href = tag.mAttributes.getValue("href");
+                    if (href != null) {
+                        spannableString.setSpan(new URLSpan(href), start, end,
+                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+                    break;
+                case "annotation":
+                    for (int i = 0; i < attrs.getLength(); i++) {
+                        String key = attrs.getLocalName(i);
+                        String value = attrs.getValue(i);
+                        spannableString.setSpan(new Annotation(key, value), start, end,
+                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+            }
+        }
+        return new SpannedString(spannableString);
+    }
+
     // ------- TypedValue stuff
     // This is taken from //device/libs/utils/ResourceTypes.cpp
 
@@ -720,5 +886,16 @@
         outValue.data = unit.unit << TypedValue.COMPLEX_UNIT_SHIFT;
         outScale[0] = unit.scale;
     }
+
+    private static class Tag {
+        private String mLabel;
+        private int mStart;
+        private int mEnd;
+        private Attributes mAttributes;
+
+        private Tag(String label) {
+            mLabel = label;
+        }
+    }
 }
 
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java b/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
index b7f59c4..640b563 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
@@ -24,8 +24,8 @@
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.impl.RenderAction;
-import com.android.utils.Pair;
 
+import android.util.Pair;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
@@ -48,8 +48,8 @@
         Pair<View, Boolean> pair = context.inflateView(dataBindingItem.getViewReference(),
                 parent, false /*attachToRoot*/, skipCallbackParser);
 
-        View view = pair.getFirst();
-        skipCallbackParser |= pair.getSecond();
+        View view = pair.first;
+        skipCallbackParser |= pair.second;
 
         if (view != null) {
             fillView(context, view, item, parentItem, callback, adapterRef);
@@ -60,7 +60,7 @@
             view = tv;
         }
 
-        return Pair.of(view, skipCallbackParser);
+        return Pair.create(view, skipCallbackParser);
     }
 
     private static void fillView(BridgeContext context, View view, AdapterItem item,
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java b/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java
index 83ff28d..1c5c6a6 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java
@@ -20,8 +20,8 @@
 import com.android.ide.common.rendering.api.DataBindingItem;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.ResourceReference;
-import com.android.utils.Pair;
 
+import android.util.Pair;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
@@ -112,8 +112,8 @@
         AdapterItem item = mItems.get(position);
         Pair<View, Boolean> pair = AdapterHelper.getView(item, null, parent, mCallback,
                 mAdapterRef, mSkipCallbackParser);
-        mSkipCallbackParser = pair.getSecond();
-        return pair.getFirst();
+        mSkipCallbackParser = pair.second;
+        return pair.first;
     }
 
     @Override
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java b/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java
index 21679f7..1f72978 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java
@@ -20,9 +20,9 @@
 import com.android.ide.common.rendering.api.DataBindingItem;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.ResourceReference;
-import com.android.utils.Pair;
 
 import android.database.DataSetObserver;
+import android.util.Pair;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ExpandableListAdapter;
@@ -135,8 +135,8 @@
         AdapterItem item = mItems.get(groupPosition);
         Pair<View, Boolean> pair = AdapterHelper.getView(item, null /*parentItem*/, parent,
                 mCallback, mAdapterRef, mSkipCallbackParser);
-        mSkipCallbackParser = pair.getSecond();
-        return pair.getFirst();
+        mSkipCallbackParser = pair.second;
+        return pair.first;
     }
 
     @Override
@@ -147,8 +147,8 @@
         AdapterItem item = getChildItem(groupPosition, childPosition);
         Pair<View, Boolean> pair = AdapterHelper.getView(item, parentItem, parent, mCallback,
                 mAdapterRef, mSkipCallbackParser);
-        mSkipCallbackParser = pair.getSecond();
-        return pair.getFirst();
+        mSkipCallbackParser = pair.second;
+        return pair.first;
     }
 
     @Override
diff --git a/bridge/src/com/android/layoutlib/bridge/libcore/util/Cleaner.java b/bridge/src/com/android/layoutlib/bridge/libcore/util/Cleaner.java
index 7c95e64..22b3d0c 100644
--- a/bridge/src/com/android/layoutlib/bridge/libcore/util/Cleaner.java
+++ b/bridge/src/com/android/layoutlib/bridge/libcore/util/Cleaner.java
@@ -19,6 +19,8 @@
 import java.lang.ref.Cleaner.Cleanable;
 
 public class Cleaner {
+    private static final java.lang.ref.Cleaner sCleaner = java.lang.ref.Cleaner.create();
+
     private final Cleanable mCleanable;
 
     private Cleaner(Cleanable cleanable) {
@@ -37,10 +39,11 @@
      * @return  The new cleaner
      */
     public static Cleaner create(Object ob, Runnable thunk) {
-        if (thunk == null)
+        if (thunk == null) {
             return null;
-        java.lang.ref.Cleaner cleaner = java.lang.ref.Cleaner.create();
-        return new Cleaner(cleaner.register(ob, thunk));
+        }
+        Cleanable cleanable = sCleaner.register(ob, thunk);
+        return new Cleaner(cleanable);
     }
 
     /**
diff --git a/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java b/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java
index d84b411..84ed6a0 100644
--- a/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java
+++ b/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java
@@ -31,7 +31,8 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.graphics.Bitmap_Delegate;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapFactory.Options;
 import android.graphics.drawable.BitmapDrawable;
 import android.widget.ImageView;
 
@@ -81,12 +82,10 @@
             // look for a cached bitmap
             Bitmap bitmap = Bridge.getCachedBitmap(path, Boolean.TRUE /*isFramework*/);
             if (bitmap == null) {
-                try {
-                    bitmap = Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density);
-                    Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/);
-                } catch (IOException e) {
-                    return imageView;
-                }
+                Options options = new Options();
+                options.inDensity = density.getDpiValue();
+                bitmap = BitmapFactory.decodeStream(stream, null, options);
+                Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/);
             }
 
             if (bitmap != null) {
diff --git a/bridge/src/com/android/layoutlib/bridge/util/CachedPathIteratorFactory.java b/bridge/src/com/android/layoutlib/bridge/util/CachedPathIteratorFactory.java
deleted file mode 100644
index 0a9b9ec..0000000
--- a/bridge/src/com/android/layoutlib/bridge/util/CachedPathIteratorFactory.java
+++ /dev/null
@@ -1,485 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.layoutlib.bridge.util;
-
-import android.annotation.NonNull;
-
-import java.awt.geom.CubicCurve2D;
-import java.awt.geom.PathIterator;
-import java.awt.geom.Point2D;
-import java.awt.geom.QuadCurve2D;
-import java.util.ArrayList;
-
-import com.google.android.collect.Lists;
-
-/**
- * Class that returns iterators for a given path. These iterators are lightweight and can be reused
- * multiple times to iterate over the path.
- */
-public class CachedPathIteratorFactory {
-    /*
-     * A few conventions used in the code:
-     * Coordinates or coords arrays store segment coordinates. They use the same format as
-     * PathIterator#currentSegment coordinates array.
-     * float arrays store always points where the first element is X and the second is Y.
-     */
-
-    // This governs how accurate the approximation of the Path is.
-    private static final float PRECISION = 0.002f;
-
-    private final int mWindingRule;
-    private final int[] mTypes;
-    private final float[][] mCoordinates;
-    private final float[] mSegmentsLength;
-    private final float mTotalLength;
-
-    public CachedPathIteratorFactory(@NonNull PathIterator iterator) {
-        mWindingRule = iterator.getWindingRule();
-
-        ArrayList<Integer> typesArray = Lists.newArrayList();
-        ArrayList<float[]> pointsArray = Lists.newArrayList();
-        float[] points = new float[6];
-        while (!iterator.isDone()) {
-            int type = iterator.currentSegment(points);
-            int nPoints = getNumberOfPoints(type) * 2; // 2 coordinates per point
-
-            typesArray.add(type);
-            float[] itemPoints = new float[nPoints];
-            System.arraycopy(points, 0, itemPoints, 0, nPoints);
-            pointsArray.add(itemPoints);
-            iterator.next();
-        }
-
-        mTypes = new int[typesArray.size()];
-        mCoordinates = new float[mTypes.length][];
-        for (int i = 0; i < typesArray.size(); i++) {
-            mTypes[i] = typesArray.get(i);
-            mCoordinates[i] = pointsArray.get(i);
-        }
-
-        // Do measurement
-        mSegmentsLength = new float[mTypes.length];
-
-        // Curves that we can reuse to estimate segments length
-        CubicCurve2D.Float cubicCurve = new CubicCurve2D.Float();
-        QuadCurve2D.Float quadCurve = new QuadCurve2D.Float();
-        float lastX = 0;
-        float lastY = 0;
-        float totalLength = 0;
-        for (int i = 0; i < mTypes.length; i++) {
-            switch (mTypes[i]) {
-                case PathIterator.SEG_CUBICTO:
-                    cubicCurve.setCurve(lastX, lastY,
-                            mCoordinates[i][0], mCoordinates[i][1], mCoordinates[i][2],
-                            mCoordinates[i][3], lastX = mCoordinates[i][4],
-                            lastY = mCoordinates[i][5]);
-                    mSegmentsLength[i] =
-                            getFlatPathLength(cubicCurve.getPathIterator(null, PRECISION));
-                    break;
-                case PathIterator.SEG_QUADTO:
-                    quadCurve.setCurve(lastX, lastY, mCoordinates[i][0], mCoordinates[i][1],
-                            lastX = mCoordinates[i][2], lastY = mCoordinates[i][3]);
-                    mSegmentsLength[i] =
-                            getFlatPathLength(quadCurve.getPathIterator(null, PRECISION));
-                    break;
-                case PathIterator.SEG_CLOSE:
-                    mSegmentsLength[i] = (float) Point2D.distance(lastX, lastY,
-                            lastX = mCoordinates[0][0],
-                            lastY = mCoordinates[0][1]);
-                    mCoordinates[i] = new float[2];
-                    // We convert a SEG_CLOSE segment to a SEG_LINETO so we do not have to worry
-                    // about this special case in the rest of the code.
-                    mTypes[i] = PathIterator.SEG_LINETO;
-                    mCoordinates[i][0] = mCoordinates[0][0];
-                    mCoordinates[i][1] = mCoordinates[0][1];
-                    break;
-                case PathIterator.SEG_MOVETO:
-                    mSegmentsLength[i] = 0;
-                    lastX = mCoordinates[i][0];
-                    lastY = mCoordinates[i][1];
-                    break;
-                case PathIterator.SEG_LINETO:
-                    mSegmentsLength[i] = (float) Point2D.distance(lastX, lastY, mCoordinates[i][0],
-                            mCoordinates[i][1]);
-                    lastX = mCoordinates[i][0];
-                    lastY = mCoordinates[i][1];
-                default:
-            }
-            totalLength += mSegmentsLength[i];
-        }
-
-        mTotalLength = totalLength;
-    }
-
-    private static void quadCurveSegment(float[] coords, float t0, float t1) {
-        // Calculate X and Y at 0.5 (We'll use this to reconstruct the control point later)
-        float mt = t0 + (t1 - t0) / 2;
-        float mu = 1 - mt;
-        float mx = mu * mu * coords[0] + 2 * mu * mt * coords[2] + mt * mt * coords[4];
-        float my = mu * mu * coords[1] + 2 * mu * mt * coords[3] + mt * mt * coords[5];
-
-        float u0 = 1 - t0;
-        float u1 = 1 - t1;
-
-        // coords at t0
-        coords[0] = coords[0] * u0 * u0 + coords[2] * 2 * t0 * u0 + coords[4] * t0 * t0;
-        coords[1] = coords[1] * u0 * u0 + coords[3] * 2 * t0 * u0 + coords[5] * t0 * t0;
-
-        // coords at t1
-        coords[4] = coords[0] * u1 * u1 + coords[2] * 2 * t1 * u1 + coords[4] * t1 * t1;
-        coords[5] = coords[1] * u1 * u1 + coords[3] * 2 * t1 * u1 + coords[5] * t1 * t1;
-
-        // estimated control point at t'=0.5
-        coords[2] = 2 * mx - coords[0] / 2 - coords[4] / 2;
-        coords[3] = 2 * my - coords[1] / 2 - coords[5] / 2;
-    }
-
-    private static void cubicCurveSegment(float[] coords, float t0, float t1) {
-        // http://stackoverflow.com/questions/11703283/cubic-bezier-curve-segment
-        float u0 = 1 - t0;
-        float u1 = 1 - t1;
-
-        // Calculate the points at t0 and t1 for the quadratic curves formed by (P0, P1, P2) and
-        // (P1, P2, P3)
-        float qxa = coords[0] * u0 * u0 + coords[2] * 2 * t0 * u0 + coords[4] * t0 * t0;
-        float qxb = coords[0] * u1 * u1 + coords[2] * 2 * t1 * u1 + coords[4] * t1 * t1;
-        float qxc = coords[2] * u0 * u0 + coords[4] * 2 * t0 * u0 + coords[6] * t0 * t0;
-        float qxd = coords[2] * u1 * u1 + coords[4] * 2 * t1 * u1 + coords[6] * t1 * t1;
-
-        float qya = coords[1] * u0 * u0 + coords[3] * 2 * t0 * u0 + coords[5] * t0 * t0;
-        float qyb = coords[1] * u1 * u1 + coords[3] * 2 * t1 * u1 + coords[5] * t1 * t1;
-        float qyc = coords[3] * u0 * u0 + coords[5] * 2 * t0 * u0 + coords[7] * t0 * t0;
-        float qyd = coords[3] * u1 * u1 + coords[5] * 2 * t1 * u1 + coords[7] * t1 * t1;
-
-        // Linear interpolation
-        coords[0] = qxa * u0 + qxc * t0;
-        coords[1] = qya * u0 + qyc * t0;
-
-        coords[2] = qxa * u1 + qxc * t1;
-        coords[3] = qya * u1 + qyc * t1;
-
-        coords[4] = qxb * u0 + qxd * t0;
-        coords[5] = qyb * u0 + qyd * t0;
-
-        coords[6] = qxb * u1 + qxd * t1;
-        coords[7] = qyb * u1 + qyd * t1;
-    }
-
-    /**
-     * Returns the end point of a given segment
-     *
-     * @param type the segment type
-     * @param coords the segment coordinates array
-     * @param point the return array where the point will be stored
-     */
-    private static void getShapeEndPoint(int type, @NonNull float[] coords, @NonNull float[]
-            point) {
-        // start index of the end point for the segment type
-        int pointIndex = (getNumberOfPoints(type) - 1) * 2;
-        point[0] = coords[pointIndex];
-        point[1] = coords[pointIndex + 1];
-    }
-
-    /**
-     * Returns the number of points stored in a coordinates array for the given segment type.
-     */
-    private static int getNumberOfPoints(int segmentType) {
-        switch (segmentType) {
-            case PathIterator.SEG_QUADTO:
-                return 2;
-            case PathIterator.SEG_CUBICTO:
-                return 3;
-            case PathIterator.SEG_CLOSE:
-                return 0;
-            default:
-                return 1;
-        }
-    }
-
-    /**
-     * Returns the estimated length of a flat path. If the passed path is not flat (i.e. contains a
-     * segment that is not {@link PathIterator#SEG_CLOSE}, {@link PathIterator#SEG_MOVETO} or {@link
-     * PathIterator#SEG_LINETO} this method will fail.
-     */
-    private static float getFlatPathLength(@NonNull PathIterator iterator) {
-        float segment[] = new float[6];
-        float totalLength = 0;
-        float[] previousPoint = new float[2];
-        boolean isFirstPoint = true;
-
-        while (!iterator.isDone()) {
-            int type = iterator.currentSegment(segment);
-            assert type == PathIterator.SEG_LINETO || type == PathIterator.SEG_CLOSE || type ==
-                    PathIterator.SEG_MOVETO;
-
-            // MoveTo shouldn't affect the length
-            if (!isFirstPoint && type != PathIterator.SEG_MOVETO) {
-                totalLength += Point2D.distance(previousPoint[0], previousPoint[1], segment[0],
-                        segment[1]);
-            } else {
-                isFirstPoint = false;
-            }
-            previousPoint[0] = segment[0];
-            previousPoint[1] = segment[1];
-            iterator.next();
-        }
-
-        return totalLength;
-    }
-
-    /**
-     * Returns the estimated position along a path of the given length.
-     */
-    private void getPointAtLength(int type, @NonNull float[] coords, float lastX, float
-            lastY, float t, @NonNull float[] point) {
-        if (type == PathIterator.SEG_LINETO) {
-            point[0] = lastX + (coords[0] - lastX) * t;
-            point[1] = lastY + (coords[1] - lastY) * t;
-            // Return here, since we do not need a shape to estimate
-            return;
-        }
-
-        float[] curve = new float[8];
-        int lastPointIndex = (getNumberOfPoints(type) - 1) * 2;
-
-        System.arraycopy(coords, 0, curve, 2, coords.length);
-        curve[0] = lastX;
-        curve[1] = lastY;
-        if (type == PathIterator.SEG_CUBICTO) {
-            cubicCurveSegment(curve, 0f, t);
-        } else {
-            quadCurveSegment(curve, 0f, t);
-        }
-
-        point[0] = curve[2 + lastPointIndex];
-        point[1] = curve[2 + lastPointIndex + 1];
-    }
-
-    public CachedPathIterator iterator() {
-        return new CachedPathIterator();
-    }
-
-    /**
-     * Class that allows us to iterate over a path multiple times
-     */
-    public class CachedPathIterator implements PathIterator {
-        private int mNextIndex;
-
-        /**
-         * Current segment type.
-         *
-         * @see PathIterator
-         */
-        private int mCurrentType;
-
-        /**
-         * Stores the coordinates array of the current segment. The number of points stored depends
-         * on the segment type.
-         *
-         * @see PathIterator
-         */
-        private float[] mCurrentCoords = new float[6];
-        private float mCurrentSegmentLength;
-
-        /**
-         * Current segment length offset. When asking for the length of the current segment, the
-         * length will be reduced by this amount. This is useful when we are only using portions of
-         * the segment.
-         *
-         * @see #jumpToSegment(float)
-         */
-        private float mOffsetLength;
-
-        /** Point where the current segment started */
-        private float[] mLastPoint = new float[2];
-        private boolean isIteratorDone;
-
-        private CachedPathIterator() {
-            next();
-        }
-
-        public float getCurrentSegmentLength() {
-            return mCurrentSegmentLength;
-        }
-
-        @Override
-        public int getWindingRule() {
-            return mWindingRule;
-        }
-
-        @Override
-        public boolean isDone() {
-            return isIteratorDone;
-        }
-
-        @Override
-        public void next() {
-            if (mNextIndex >= mTypes.length) {
-                isIteratorDone = true;
-                return;
-            }
-
-            if (mNextIndex >= 1) {
-                // We've already called next() once so there is a previous segment in this path.
-                // We want to get the coordinates where the path ends.
-                getShapeEndPoint(mCurrentType, mCurrentCoords, mLastPoint);
-            } else {
-                // This is the first segment, no previous point so initialize to 0, 0
-                mLastPoint[0] = mLastPoint[1] = 0f;
-            }
-            mCurrentType = mTypes[mNextIndex];
-            mCurrentSegmentLength = mSegmentsLength[mNextIndex] - mOffsetLength;
-
-            if (mOffsetLength > 0f && (mCurrentType == SEG_CUBICTO || mCurrentType == SEG_QUADTO)) {
-                // We need to skip part of the start of the current segment (because
-                // mOffsetLength > 0)
-                float[] points = new float[8];
-
-                if (mNextIndex < 1) {
-                    points[0] = points[1] = 0f;
-                } else {
-                    getShapeEndPoint(mTypes[mNextIndex - 1], mCoordinates[mNextIndex - 1], points);
-                }
-
-                System.arraycopy(mCoordinates[mNextIndex], 0, points, 2,
-                        mCoordinates[mNextIndex].length);
-                float t0 = (mSegmentsLength[mNextIndex] - mCurrentSegmentLength) /
-                        mSegmentsLength[mNextIndex];
-                if (mCurrentType == SEG_CUBICTO) {
-                    cubicCurveSegment(points, t0, 1f);
-                } else {
-                    quadCurveSegment(points, t0, 1f);
-                }
-                System.arraycopy(points, 2, mCurrentCoords, 0, mCoordinates[mNextIndex].length);
-            } else {
-                System.arraycopy(mCoordinates[mNextIndex], 0, mCurrentCoords, 0,
-                        mCoordinates[mNextIndex].length);
-            }
-
-            mOffsetLength = 0f;
-            mNextIndex++;
-        }
-
-        @Override
-        public int currentSegment(float[] coords) {
-            System.arraycopy(mCurrentCoords, 0, coords, 0, getNumberOfPoints(mCurrentType) * 2);
-            return mCurrentType;
-        }
-
-        @Override
-        public int currentSegment(double[] coords) {
-            throw new UnsupportedOperationException();
-        }
-
-        /**
-         * Returns the point where the current segment ends
-         */
-        public void getCurrentSegmentEnd(float[] point) {
-            point[0] = mLastPoint[0];
-            point[1] = mLastPoint[1];
-        }
-
-        /**
-         * Restarts the iterator and jumps all the segments of this path up to the length value.
-         */
-        public void jumpToSegment(float length) {
-            isIteratorDone = false;
-            if (length <= 0f) {
-                mNextIndex = 0;
-                return;
-            }
-
-            float accLength = 0;
-            float lastPoint[] = new float[2];
-            for (mNextIndex = 0; mNextIndex < mTypes.length; mNextIndex++) {
-                float segmentLength = mSegmentsLength[mNextIndex];
-                if (accLength + segmentLength >= length && mTypes[mNextIndex] != SEG_MOVETO) {
-                    float[] estimatedPoint = new float[2];
-                    getPointAtLength(mTypes[mNextIndex],
-                            mCoordinates[mNextIndex], lastPoint[0], lastPoint[1],
-                            (length - accLength) / segmentLength,
-                            estimatedPoint);
-
-                    // This segment makes us go further than length so we go back one step,
-                    // set a moveto and offset the length of the next segment by the length
-                    // of this segment that we've already used.
-                    mCurrentType = PathIterator.SEG_MOVETO;
-                    mCurrentCoords[0] = estimatedPoint[0];
-                    mCurrentCoords[1] = estimatedPoint[1];
-                    mCurrentSegmentLength = 0;
-
-                    // We need to offset next path length to account for the segment we've just
-                    // skipped.
-                    mOffsetLength = length - accLength;
-                    return;
-                }
-                accLength += segmentLength;
-                getShapeEndPoint(mTypes[mNextIndex], mCoordinates[mNextIndex], lastPoint);
-            }
-        }
-
-        /**
-         * Returns the current segment up to certain length. If the current segment is shorter than
-         * length, then the whole segment is returned. The segment coordinates are copied into the
-         * coords array.
-         *
-         * @return the segment type
-         */
-        public int currentSegment(@NonNull float[] coords, float length) {
-            int type = currentSegment(coords);
-            // If the length is greater than the current segment length, no need to find
-            // the cut point. Same if this is a SEG_MOVETO.
-            if (mCurrentSegmentLength <= length || type == SEG_MOVETO) {
-                return type;
-            }
-
-            float t = length / getCurrentSegmentLength();
-
-            // We find at which offset the end point is located within the coords array and set
-            // a new end point to cut the segment short
-            switch (type) {
-                case SEG_CUBICTO:
-                case SEG_QUADTO:
-                    float[] curve = new float[8];
-                    curve[0] = mLastPoint[0];
-                    curve[1] = mLastPoint[1];
-                    System.arraycopy(coords, 0, curve, 2, coords.length);
-                    if (type == SEG_CUBICTO) {
-                        cubicCurveSegment(curve, 0f, t);
-                    } else {
-                        quadCurveSegment(curve, 0f, t);
-                    }
-                    System.arraycopy(curve, 2, coords, 0, coords.length);
-                    break;
-                default:
-                    float[] point = new float[2];
-                    getPointAtLength(type, coords, mLastPoint[0], mLastPoint[1], t, point);
-                    coords[0] = point[0];
-                    coords[1] = point[1];
-            }
-
-            return type;
-        }
-
-        /**
-         * Returns the total length of the path
-         */
-        public float getTotalLength() {
-            return mTotalLength;
-        }
-    }
-}
diff --git a/bridge/src/com/android/layoutlib/bridge/util/ChoreographerCallbacks.java b/bridge/src/com/android/layoutlib/bridge/util/ChoreographerCallbacks.java
new file mode 100644
index 0000000..9d5830b
--- /dev/null
+++ b/bridge/src/com/android/layoutlib/bridge/util/ChoreographerCallbacks.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.util;
+
+import com.android.ide.common.rendering.api.ILayoutLog;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import android.os.SystemClock_Delegate;
+import android.util.Pair;
+import android.util.TimeUtils;
+import android.view.Choreographer.FrameCallback;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Manages {@link android.view.Choreographer} callbacks. Keeps track of the currently active
+ * callbacks and allows to execute callbacks if their time is due.
+ */
+public class ChoreographerCallbacks {
+    // Simple wrapper around ArrayList to be able to use protected removeRange method
+    private static class RangeList<T> extends ArrayList<T> {
+        private void removeFrontElements(int n) {
+            removeRange(0, n);
+        }
+    }
+
+    private final RangeList<Pair<Object, Long>> mCallbacks = new RangeList<>();
+
+    public void add(Object action, long delayMillis) {
+        synchronized (mCallbacks) {
+            int idx = 0;
+            final long now = SystemClock_Delegate.uptimeMillis();
+            final long dueTime = now + delayMillis;
+            while (idx < mCallbacks.size()) {
+                if (mCallbacks.get(idx).second > dueTime) {
+                    break;
+                } else {
+                    ++idx;
+                }
+            }
+            mCallbacks.add(idx, Pair.create(action, dueTime));
+        }
+    }
+
+    public void remove(Object action) {
+        synchronized (mCallbacks) {
+            mCallbacks.removeIf(el -> el.first == action);
+        }
+    }
+
+    public void execute(long currentTimeMs, @NotNull ILayoutLog logger) {
+        final long currentTimeNanos = currentTimeMs * TimeUtils.NANOS_PER_MS;
+        List<Pair<Object, Long>> toExecute;
+        synchronized (mCallbacks) {
+            int idx = 0;
+            while (idx < mCallbacks.size()) {
+                if (mCallbacks.get(idx).second > currentTimeMs) {
+                    break;
+                } else {
+                    ++idx;
+                }
+            }
+            toExecute = new ArrayList<>(mCallbacks.subList(0, idx));
+            mCallbacks.removeFrontElements(idx);
+        }
+
+        // We run the callbacks outside of the synchronized block to avoid deadlocks caused by
+        // callbacks calling back into ChoreographerCallbacks.
+        toExecute.forEach(p -> executeSafely(p.first, currentTimeNanos, logger));
+    }
+
+    public void clear() {
+        synchronized (mCallbacks) {
+            mCallbacks.clear();
+        }
+    }
+
+    private static void executeSafely(@NotNull Object action, long frameTimeNanos,
+            @NotNull ILayoutLog logger) {
+        try {
+            if (action instanceof FrameCallback) {
+                FrameCallback callback = (FrameCallback) action;
+                callback.doFrame(frameTimeNanos);
+            } else if (action instanceof Runnable) {
+                Runnable runnable = (Runnable) action;
+                runnable.run();
+            } else {
+                logger.error(ILayoutLog.TAG_BROKEN,
+                        "Unexpected action as Choreographer callback", (Object) null, null);
+            }
+        } catch (Throwable t) {
+            logger.error(ILayoutLog.TAG_BROKEN, "Failed executing Choreographer callback", t,
+                    null, null);
+        }
+    }
+}
diff --git a/bridge/src/com/android/layoutlib/bridge/util/DynamicIdMap.java b/bridge/src/com/android/layoutlib/bridge/util/DynamicIdMap.java
index 16aa058..4a5c6e6 100644
--- a/bridge/src/com/android/layoutlib/bridge/util/DynamicIdMap.java
+++ b/bridge/src/com/android/layoutlib/bridge/util/DynamicIdMap.java
@@ -17,9 +17,9 @@
 package com.android.layoutlib.bridge.util;
 
 import com.android.resources.ResourceType;
-import com.android.utils.Pair;
 
 import android.annotation.NonNull;
+import android.util.Pair;
 import android.util.SparseArray;
 
 import java.util.HashMap;
@@ -51,7 +51,7 @@
      */
     @NonNull
     public Integer getId(ResourceType type, String name) {
-        return getId(Pair.of(type, name));
+        return getId(Pair.create(type, name));
     }
 
     /**
diff --git a/bridge/src/com/android/layoutlib/bridge/util/HandlerMessageQueue.java b/bridge/src/com/android/layoutlib/bridge/util/HandlerMessageQueue.java
new file mode 100644
index 0000000..e173717
--- /dev/null
+++ b/bridge/src/com/android/layoutlib/bridge/util/HandlerMessageQueue.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.util;
+
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
+
+import android.os.Handler;
+import android.util.Pair;
+
+import java.util.LinkedList;
+import java.util.WeakHashMap;
+
+/**
+ * A queue that stores {@link Runnable}s associated with the corresponding {@link Handler}s.
+ * {@link Runnable}s get automatically released when the corresponding {@link Handler} gets
+ * collected by the garbage collector. All {@link Runnable}s are queued in a single virtual queue
+ * with respect to their corresponding uptime (the time when they should be executed).
+ */
+public class HandlerMessageQueue {
+    private final WeakHashMap<Handler, LinkedList<Pair<Long, Runnable>>> runnablesMap =
+            new WeakHashMap<>();
+
+    /**
+     * Adds a {@link Runnable} associated with the {@link Handler} to be executed at
+     * particular time
+     * @param h handler associated with the {@link Runnable}
+     * @param uptimeMillis time in milliseconds the {@link Runnable} to be executed
+     * @param r {@link Runnable} to be added
+     */
+    public void add(@NotNull Handler h, long uptimeMillis, @NotNull Runnable r) {
+        LinkedList<Pair<Long, Runnable>> runnables = runnablesMap.computeIfAbsent(h,
+                k -> new LinkedList<>());
+
+        int idx = 0;
+        while (idx < runnables.size()) {
+            if (runnables.get(idx).first <= uptimeMillis) {
+                idx++;
+            } else {
+                break;
+            }
+        }
+        runnables.add(idx, Pair.create(uptimeMillis, r));
+    }
+
+    private static class HandlerWrapper {
+        public Handler handler;
+    }
+
+    /**
+     * Removes from the queue and returns the {@link Runnable} with the smallest uptime if it
+     * is less than the one passed as a parameter or null if such runnable does not exist.
+     * @param uptimeMillis
+     * @return the {@link Runnable} from the queue
+     */
+    @Nullable
+    public Runnable extractFirst(long uptimeMillis) {
+        final HandlerWrapper w = new HandlerWrapper();
+        runnablesMap.forEach((h, l) -> {
+            if (!l.isEmpty()) {
+                long currentUptime = l.getFirst().first;
+                if (currentUptime <= uptimeMillis) {
+                    if (w.handler == null || currentUptime <
+                            runnablesMap.get(w.handler).getFirst().first) {
+                        w.handler = h;
+                    }
+                }
+            }
+        });
+        if (w.handler != null) {
+            return runnablesMap.get(w.handler).pollFirst().second;
+        }
+        return null;
+    }
+
+    /**
+     * @return true is queue has no runnables left
+     */
+    public boolean isNotEmpty() {
+        return runnablesMap.values().stream().anyMatch(l -> !l.isEmpty());
+    }
+
+    /**
+     * @return number of runnables in the queue
+     */
+    public int size() {
+        return runnablesMap.values().stream().mapToInt(LinkedList::size).sum();
+    }
+
+    /**
+     * Completely clears the entire queue
+     */
+    public void clear() {
+        runnablesMap.clear();
+    }
+}
diff --git a/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java b/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
index 75e4a2b..a50090b 100644
--- a/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
+++ b/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
@@ -33,13 +33,21 @@
 public class NinePatchInputStream extends InputStream {
     private final InputStream mDelegate;
     private boolean mFakeMarkSupport = true;
+    private final String mPath;
 
     public NinePatchInputStream(File file) throws FileNotFoundException {
         mDelegate = new FileInputStream(file);
+        mPath = file.getPath();
     }
 
-    public NinePatchInputStream(@NotNull InputStream stream) {
+    public NinePatchInputStream(@NotNull InputStream stream, @NotNull String path) {
         mDelegate = stream;
+        mPath = path;
+    }
+
+    @NotNull
+    public String getPath() {
+        return mPath;
     }
 
     @Override
diff --git a/bridge/src/dalvik/system/VMRuntime_Delegate.java b/bridge/src/dalvik/system/VMRuntime_Delegate.java
index 648d2be..2fe1015 100644
--- a/bridge/src/dalvik/system/VMRuntime_Delegate.java
+++ b/bridge/src/dalvik/system/VMRuntime_Delegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -26,59 +26,14 @@
  */
 public class VMRuntime_Delegate {
 
-    // Copied from libcore/libdvm/src/main/java/dalvik/system/VMRuntime
     @LayoutlibDelegate
     /*package*/ static Object newUnpaddedArray(VMRuntime runtime, Class<?> componentType,
             int minLength) {
-        // Dalvik has 32bit pointers, the array header is 16bytes plus 4bytes for dlmalloc,
-        // allocations are 8byte aligned so having 4bytes of array data avoids padding.
-        if (!componentType.isPrimitive()) {
-            int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
-            return java.lang.reflect.Array.newInstance(componentType, size);
-        } else if (componentType == char.class) {
-            int bytes = 20 + (2 * minLength);
-            int alignedUpBytes = (bytes + 7) & -8;
-            int dataBytes = alignedUpBytes - 20;
-            int size = dataBytes / 2;
-            return new char[size];
-        } else if (componentType == int.class) {
-            int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
-            return new int[size];
-        } else if (componentType == byte.class) {
-            int bytes = 20 + minLength;
-            int alignedUpBytes = (bytes + 7) & -8;
-            int dataBytes = alignedUpBytes - 20;
-            int size = dataBytes;
-            return new byte[size];
-        } else if (componentType == boolean.class) {
-            int bytes = 20 + minLength;
-            int alignedUpBytes = (bytes + 7) & -8;
-            int dataBytes = alignedUpBytes - 20;
-            int size = dataBytes;
-            return new boolean[size];
-        } else if (componentType == short.class) {
-            int bytes = 20 + (2 * minLength);
-            int alignedUpBytes = (bytes + 7) & -8;
-            int dataBytes = alignedUpBytes - 20;
-            int size = dataBytes / 2;
-            return new short[size];
-        } else if (componentType == float.class) {
-            int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
-            return new float[size];
-        } else if (componentType == long.class) {
-            return new long[minLength];
-        } else if (componentType == double.class) {
-            return new double[minLength];
-        } else {
-            assert componentType == void.class;
-            throw new IllegalArgumentException("Can't allocate an array of void");
-        }
+        return VMRuntimeCommonHelper.newUnpaddedArray(runtime, componentType, minLength);
     }
 
     @LayoutlibDelegate
     /*package*/ static int getNotifyNativeInterval() {
-        // This cannot return 0, otherwise it is responsible for triggering an exception
-        // whenever trying to use a NativeAllocationRegistry with size 0
-        return 1;
+        return VMRuntimeCommonHelper.getNotifyNativeInterval();
     }
 }
diff --git a/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java b/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
index 141c728..857b4fe 100644
--- a/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
+++ b/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
@@ -51,22 +51,6 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void registerNativeAllocation(long size) {
-        NativeAllocationRegistry.registerNativeAllocation_Original(size);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static Runnable registerNativeAllocation(NativeAllocationRegistry registry,
-            Object referent,
-            long nativePtr) {
-        // Mark the object as already "natively" tracked.
-        // This allows the DelegateManager to dispose objects without waiting
-        // for an explicit call when the referent does not exist anymore.
-        sManager.markAsNativeAllocation(referent, nativePtr);
-        return registry.registerNativeAllocation_Original(referent, nativePtr);
-    }
-
-    @LayoutlibDelegate
     /*package*/ static void applyFreeFunction(long freeFunction, long nativePtr) {
         // This method MIGHT run in the context of the finalizer thread. If the delegate method
         // crashes, it could bring down the VM. That's why we catch all the exceptions and ignore
@@ -75,6 +59,8 @@
             NativeAllocationRegistry_Delegate delegate = sManager.getDelegate(freeFunction);
             if (delegate != null) {
                 delegate.mFinalizer.free(nativePtr);
+            } else if (freeFunction != 0) {
+                nativeApplyFreeFunction(freeFunction, nativePtr);
             }
         } catch (Throwable ignore) {
         }
@@ -83,4 +69,6 @@
     public interface FreeFunction {
         void free(long nativePtr);
     }
+
+    private static native void nativeApplyFreeFunction(long freeFunction, long nativePtr);
 }
diff --git a/bridge/tests/Android.bp b/bridge/tests/Android.bp
index 7beb94d..4aec3ad 100644
--- a/bridge/tests/Android.bp
+++ b/bridge/tests/Android.bp
@@ -31,6 +31,7 @@
         "kxml2-2.3.0",
         "layoutlib_api-prebuilt",
         "tools-common-prebuilt",
+        "ninepatch-prebuilt",
         "sdk-common",
         "junit",
         "guava",
diff --git a/bridge/tests/res/com/android/layoutlib/testdata/compiled.9.png b/bridge/tests/res/com/android/layoutlib/testdata/compiled.9.png
new file mode 100644
index 0000000..a5f5f3b
--- /dev/null
+++ b/bridge/tests/res/com/android/layoutlib/testdata/compiled.9.png
Binary files differ
diff --git a/bridge/tests/res/com/android/layoutlib/testdata/non_compiled.9.png b/bridge/tests/res/com/android/layoutlib/testdata/non_compiled.9.png
new file mode 100644
index 0000000..9d52f40
--- /dev/null
+++ b/bridge/tests/res/com/android/layoutlib/testdata/non_compiled.9.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$drawable.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$drawable.class
index 5ac9e72..0292770 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$drawable.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$drawable.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$font.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$font.class
index 6fe46c5..5a596a2 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$font.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$font.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$id.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$id.class
index 15e3e52..aafc5b4 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$id.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$id.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$integer.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$integer.class
index 64df0c3..def2f60 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$integer.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$integer.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$layout.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$layout.class
index c0bb96d..6786cf2 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$layout.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$layout.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$menu.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$menu.class
index afeb357..252030e 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$menu.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$menu.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$string.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$string.class
index c8d5e0b..823ba90 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$string.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$string.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$style.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$style.class
index ccfecc8..4dd0af6 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$style.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$style.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/ThemableWidget.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/ThemableWidget.class
index 5171dad..5500abe 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/ThemableWidget.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/ThemableWidget.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/a11y_test1.png b/bridge/tests/res/testApp/MyApplication/golden-mac/a11y_test1.png
new file mode 100644
index 0000000..067a193
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/a11y_test1.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/activity.png b/bridge/tests/res/testApp/MyApplication/golden-mac/activity.png
new file mode 100644
index 0000000..0dbe45f
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/activity.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon.png b/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon.png
new file mode 100644
index 0000000..efcd49e
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_circle.png b/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_circle.png
new file mode 100644
index 0000000..f85ef1b
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_circle.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_rounded_corners.png b/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_rounded_corners.png
new file mode 100644
index 0000000..3bb7055
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_rounded_corners.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_squircle.png b/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_squircle.png
new file mode 100644
index 0000000..f3b3a08
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_squircle.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/allwidgets.png b/bridge/tests/res/testApp/MyApplication/golden-mac/allwidgets.png
new file mode 100644
index 0000000..d99df26
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/allwidgets.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/allwidgets_tab.png b/bridge/tests/res/testApp/MyApplication/golden-mac/allwidgets_tab.png
new file mode 100644
index 0000000..5ddd9d2
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/allwidgets_tab.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/animated_vector.png b/bridge/tests/res/testApp/MyApplication/golden-mac/animated_vector.png
new file mode 100644
index 0000000..b4bed24
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/animated_vector.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/animated_vector_1.png b/bridge/tests/res/testApp/MyApplication/golden-mac/animated_vector_1.png
new file mode 100644
index 0000000..37863c7
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/animated_vector_1.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/array_check.png b/bridge/tests/res/testApp/MyApplication/golden-mac/array_check.png
new file mode 100644
index 0000000..db6e934
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/array_check.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/asset.png b/bridge/tests/res/testApp/MyApplication/golden-mac/asset.png
new file mode 100644
index 0000000..9e58720
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/asset.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/auto-scale-image.png b/bridge/tests/res/testApp/MyApplication/golden-mac/auto-scale-image.png
new file mode 100644
index 0000000..7644183
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/auto-scale-image.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/button_resize.png b/bridge/tests/res/testApp/MyApplication/golden-mac/button_resize.png
new file mode 100644
index 0000000..dc81204
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/button_resize.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/button_resize2.png b/bridge/tests/res/testApp/MyApplication/golden-mac/button_resize2.png
new file mode 100644
index 0000000..9b79576
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/button_resize2.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/canvas.png b/bridge/tests/res/testApp/MyApplication/golden-mac/canvas.png
new file mode 100644
index 0000000..ec503a4
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/canvas.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/color_interpolation.png b/bridge/tests/res/testApp/MyApplication/golden-mac/color_interpolation.png
new file mode 100644
index 0000000..c816e57
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/color_interpolation.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/context_theme_wrapper.png b/bridge/tests/res/testApp/MyApplication/golden-mac/context_theme_wrapper.png
new file mode 100644
index 0000000..63578fa
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/context_theme_wrapper.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/dialog.png b/bridge/tests/res/testApp/MyApplication/golden-mac/dialog.png
new file mode 100644
index 0000000..2f029e5
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/dialog.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/expand_horz_layout.png b/bridge/tests/res/testApp/MyApplication/golden-mac/expand_horz_layout.png
new file mode 100644
index 0000000..f179977
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/expand_horz_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/expand_vert_layout.png b/bridge/tests/res/testApp/MyApplication/golden-mac/expand_vert_layout.png
new file mode 100644
index 0000000..269b7ac
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/expand_vert_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/font_test.png b/bridge/tests/res/testApp/MyApplication/golden-mac/font_test.png
new file mode 100644
index 0000000..e036c16
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/font_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners.png b/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners.png
new file mode 100644
index 0000000..fc68ac1
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners_translucent.png b/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners_translucent.png
new file mode 100644
index 0000000..2aae93b
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners_translucent.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners_translucent_land.png b/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners_translucent_land.png
new file mode 100644
index 0000000..dae944a
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners_translucent_land.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/gradient_alpha_drawable.png b/bridge/tests/res/testApp/MyApplication/golden-mac/gradient_alpha_drawable.png
new file mode 100644
index 0000000..8892bcf
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/gradient_alpha_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/gradient_colors.png b/bridge/tests/res/testApp/MyApplication/golden-mac/gradient_colors.png
new file mode 100644
index 0000000..d5b7c10
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/gradient_colors.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/justified_inter_word.png b/bridge/tests/res/testApp/MyApplication/golden-mac/justified_inter_word.png
new file mode 100644
index 0000000..4807821
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/justified_inter_word.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/justified_none.png b/bridge/tests/res/testApp/MyApplication/golden-mac/justified_none.png
new file mode 100644
index 0000000..e083346
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/justified_none.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/large_shadows_test.png b/bridge/tests/res/testApp/MyApplication/golden-mac/large_shadows_test.png
new file mode 100644
index 0000000..a79ad55
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/large_shadows_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/many_line_breaks.png b/bridge/tests/res/testApp/MyApplication/golden-mac/many_line_breaks.png
new file mode 100644
index 0000000..ea5750c
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/many_line_breaks.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/ninepatch_background.png b/bridge/tests/res/testApp/MyApplication/golden-mac/ninepatch_background.png
new file mode 100644
index 0000000..014df65
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/ninepatch_background.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/ninepatch_drawable.png b/bridge/tests/res/testApp/MyApplication/golden-mac/ninepatch_drawable.png
new file mode 100644
index 0000000..211594d
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/ninepatch_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/non-styled_resources.png b/bridge/tests/res/testApp/MyApplication/golden-mac/non-styled_resources.png
new file mode 100644
index 0000000..6490b26
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/non-styled_resources.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/normal_layout.png b/bridge/tests/res/testApp/MyApplication/golden-mac/normal_layout.png
new file mode 100644
index 0000000..f60ecb0
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/normal_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/ondraw_crash.png b/bridge/tests/res/testApp/MyApplication/golden-mac/ondraw_crash.png
new file mode 100644
index 0000000..8633a25
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/ondraw_crash.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/onmeasure_crash.png b/bridge/tests/res/testApp/MyApplication/golden-mac/onmeasure_crash.png
new file mode 100644
index 0000000..5559a89
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/onmeasure_crash.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/render_effect.png b/bridge/tests/res/testApp/MyApplication/golden-mac/render_effect.png
new file mode 100644
index 0000000..7a90998
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/render_effect.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/rtl_ltr.png b/bridge/tests/res/testApp/MyApplication/golden-mac/rtl_ltr.png
new file mode 100644
index 0000000..6a451a2
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/rtl_ltr.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/scrolled.png b/bridge/tests/res/testApp/MyApplication/golden-mac/scrolled.png
new file mode 100644
index 0000000..97bf039
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/scrolled.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/shadow_scrollview_test.png b/bridge/tests/res/testApp/MyApplication/golden-mac/shadow_scrollview_test.png
new file mode 100644
index 0000000..b1a3b26
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/shadow_scrollview_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/shadow_sizes_test.png b/bridge/tests/res/testApp/MyApplication/golden-mac/shadow_sizes_test.png
new file mode 100644
index 0000000..3bc9b4c
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/shadow_sizes_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/shadows_test.png b/bridge/tests/res/testApp/MyApplication/golden-mac/shadows_test.png
new file mode 100644
index 0000000..c8dea1b
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/shadows_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/shadows_test_rounded_edge.png b/bridge/tests/res/testApp/MyApplication/golden-mac/shadows_test_rounded_edge.png
new file mode 100644
index 0000000..8bac13b
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/shadows_test_rounded_edge.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/shrunk_layout.png b/bridge/tests/res/testApp/MyApplication/golden-mac/shrunk_layout.png
new file mode 100644
index 0000000..e2eb120
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/shrunk_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity-old-theme.png b/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity-old-theme.png
new file mode 100644
index 0000000..d782cd1
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity-old-theme.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity.png b/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity.png
new file mode 100644
index 0000000..efb7ac3
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity_noactionbar.png b/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity_noactionbar.png
new file mode 100644
index 0000000..36350a0
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity_noactionbar.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/textclock.png b/bridge/tests/res/testApp/MyApplication/golden-mac/textclock.png
new file mode 100644
index 0000000..108380c
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/textclock.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/translate_test.png b/bridge/tests/res/testApp/MyApplication/golden-mac/translate_test.png
new file mode 100644
index 0000000..032089f
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/translate_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/transparent_drawable.png b/bridge/tests/res/testApp/MyApplication/golden-mac/transparent_drawable.png
new file mode 100644
index 0000000..68ccf2f
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/transparent_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/typed_arrays.png b/bridge/tests/res/testApp/MyApplication/golden-mac/typed_arrays.png
new file mode 100644
index 0000000..e3f4df7
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/typed_arrays.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable.png b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable.png
new file mode 100644
index 0000000..ecc4497
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_91383.png b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_91383.png
new file mode 100644
index 0000000..1d4ac39
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_91383.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_gradient.png b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_gradient.png
new file mode 100644
index 0000000..0268d9c
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_gradient.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_radial_gradient.png b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_radial_gradient.png
new file mode 100644
index 0000000..a27a48e
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_radial_gradient.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_with_tint_in_image_view.png b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_with_tint_in_image_view.png
new file mode 100644
index 0000000..48f0f05
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_with_tint_in_image_view.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_with_tint_itself.png b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_with_tint_itself.png
new file mode 100644
index 0000000..92855d1
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_with_tint_itself.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/view_boundaries.png b/bridge/tests/res/testApp/MyApplication/golden-mac/view_boundaries.png
new file mode 100644
index 0000000..da770e1
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/view_boundaries.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/view_stub.png b/bridge/tests/res/testApp/MyApplication/golden-mac/view_stub.png
new file mode 100644
index 0000000..b66ff2d
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/view_stub.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/window_background.png b/bridge/tests/res/testApp/MyApplication/golden-mac/window_background.png
new file mode 100644
index 0000000..1d25725
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/window_background.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/activity.png b/bridge/tests/res/testApp/MyApplication/golden/activity.png
index 2d55e8f..0dbe45f 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/activity.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/activity.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon.png b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon.png
index eccd6ea..efcd49e 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_circle.png b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_circle.png
index b98b38b..f85ef1b 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_circle.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_circle.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_rounded_corners.png b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_rounded_corners.png
index 44d2161..3bb7055 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_rounded_corners.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_rounded_corners.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_squircle.png b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_squircle.png
index 3361cc9..f3b3a08 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_squircle.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_squircle.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png b/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
index 8d64d47..d99df26 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png b/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
index 7559d54..5ddd9d2 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png b/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png
index 072df30..b4bed24 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png b/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png
index 348a0f4..37863c7 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/array_check.png b/bridge/tests/res/testApp/MyApplication/golden/array_check.png
index e4be842..db6e934 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/array_check.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/array_check.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/asset.png b/bridge/tests/res/testApp/MyApplication/golden/asset.png
index 3a00361..9e58720 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/asset.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/asset.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/auto-scale-image.png b/bridge/tests/res/testApp/MyApplication/golden/auto-scale-image.png
index f6ff623..7644183 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/auto-scale-image.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/auto-scale-image.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/button_resize.png b/bridge/tests/res/testApp/MyApplication/golden/button_resize.png
new file mode 100644
index 0000000..dc81204
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/button_resize.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/button_resize2.png b/bridge/tests/res/testApp/MyApplication/golden/button_resize2.png
new file mode 100644
index 0000000..9b79576
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/button_resize2.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/canvas.png b/bridge/tests/res/testApp/MyApplication/golden/canvas.png
index a267b07..55f84c0 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/canvas.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/canvas.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/color_interpolation.png b/bridge/tests/res/testApp/MyApplication/golden/color_interpolation.png
index 2d90bc3..c816e57 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/color_interpolation.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/color_interpolation.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/context_theme_wrapper.png b/bridge/tests/res/testApp/MyApplication/golden/context_theme_wrapper.png
index 5627108..63578fa 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/context_theme_wrapper.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/context_theme_wrapper.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/dialog.png b/bridge/tests/res/testApp/MyApplication/golden/dialog.png
new file mode 100644
index 0000000..2f029e5
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/dialog.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png b/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png
index 0ae96a5..f179977 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/expand_vert_layout.png b/bridge/tests/res/testApp/MyApplication/golden/expand_vert_layout.png
index 2f0b937..269b7ac 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/expand_vert_layout.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/expand_vert_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/font_test.png b/bridge/tests/res/testApp/MyApplication/golden/font_test.png
index b3db624..e036c16 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/font_test.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/font_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/four_corners.png b/bridge/tests/res/testApp/MyApplication/golden/four_corners.png
index e79c860..fc68ac1 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/four_corners.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/four_corners.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png b/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png
index 7129944..2aae93b 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png b/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png
index 0e5336f..dae944a 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/gradient_alpha_drawable.png b/bridge/tests/res/testApp/MyApplication/golden/gradient_alpha_drawable.png
index c5e6e40..8892bcf 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/gradient_alpha_drawable.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/gradient_alpha_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/gradient_colors.png b/bridge/tests/res/testApp/MyApplication/golden/gradient_colors.png
index 137eda8..d5b7c10 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/gradient_colors.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/gradient_colors.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/justified_inter_word.png b/bridge/tests/res/testApp/MyApplication/golden/justified_inter_word.png
new file mode 100644
index 0000000..4807821
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/justified_inter_word.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/justified_none.png b/bridge/tests/res/testApp/MyApplication/golden/justified_none.png
new file mode 100644
index 0000000..e083346
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/justified_none.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/large_shadows_test.png b/bridge/tests/res/testApp/MyApplication/golden/large_shadows_test.png
new file mode 100644
index 0000000..a79ad55
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/large_shadows_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/large_shadows_test_high_quality.png b/bridge/tests/res/testApp/MyApplication/golden/large_shadows_test_high_quality.png
deleted file mode 100644
index 17bb713..0000000
--- a/bridge/tests/res/testApp/MyApplication/golden/large_shadows_test_high_quality.png
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/many_line_breaks.png b/bridge/tests/res/testApp/MyApplication/golden/many_line_breaks.png
index bfe7ce0..ea5750c 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/many_line_breaks.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/many_line_breaks.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/ninepatch_background.png b/bridge/tests/res/testApp/MyApplication/golden/ninepatch_background.png
index b3ac436..014df65 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/ninepatch_background.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/ninepatch_background.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/ninepatch_drawable.png b/bridge/tests/res/testApp/MyApplication/golden/ninepatch_drawable.png
new file mode 100644
index 0000000..211594d
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/ninepatch_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/non-styled_resources.png b/bridge/tests/res/testApp/MyApplication/golden/non-styled_resources.png
new file mode 100644
index 0000000..6490b26
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/non-styled_resources.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/normal_layout.png b/bridge/tests/res/testApp/MyApplication/golden/normal_layout.png
index f1d406e..f60ecb0 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/normal_layout.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/normal_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/ondraw_crash.png b/bridge/tests/res/testApp/MyApplication/golden/ondraw_crash.png
index 79aa3a8..8633a25 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/ondraw_crash.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/ondraw_crash.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/onmeasure_crash.png b/bridge/tests/res/testApp/MyApplication/golden/onmeasure_crash.png
index bac8f8e..5559a89 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/onmeasure_crash.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/onmeasure_crash.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/render_effect.png b/bridge/tests/res/testApp/MyApplication/golden/render_effect.png
new file mode 100644
index 0000000..7a90998
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/render_effect.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.png b/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.png
index 1690785..40bb188 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/scrolled.png b/bridge/tests/res/testApp/MyApplication/golden/scrolled.png
index 27227ea..97bf039 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/scrolled.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/scrolled.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test.png b/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test.png
new file mode 100644
index 0000000..2f1544f
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test_high_quality.png b/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test_high_quality.png
deleted file mode 100644
index 716cd08..0000000
--- a/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test_high_quality.png
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test.png b/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test.png
new file mode 100644
index 0000000..301f40f
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test_high_quality.png b/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test_high_quality.png
deleted file mode 100644
index a4ff637..0000000
--- a/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test_high_quality.png
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png b/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png
index 21f5ee1..c8dea1b 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality.png b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality.png
deleted file mode 100644
index 1e10963..0000000
--- a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality.png
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality_rounded_edge.png b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality_rounded_edge.png
deleted file mode 100644
index 25ba535..0000000
--- a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality_rounded_edge.png
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_no_shadow.png b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_no_shadow.png
deleted file mode 100644
index 0492491..0000000
--- a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_no_shadow.png
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_rounded_edge.png b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_rounded_edge.png
new file mode 100644
index 0000000..d9672f1
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_rounded_edge.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shrunk_layout.png b/bridge/tests/res/testApp/MyApplication/golden/shrunk_layout.png
index fe6fc16..e2eb120 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/shrunk_layout.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/shrunk_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png b/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png
index 01adbd7..d782cd1 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png b/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png
index 4f62a5d..efb7ac3 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png b/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png
index aee806f..36350a0 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/textclock.png b/bridge/tests/res/testApp/MyApplication/golden/textclock.png
index e33047d..108380c 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/textclock.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/textclock.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/translate_test.png b/bridge/tests/res/testApp/MyApplication/golden/translate_test.png
index c031e41..032089f 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/translate_test.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/translate_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/transparent_drawable.png b/bridge/tests/res/testApp/MyApplication/golden/transparent_drawable.png
new file mode 100644
index 0000000..68ccf2f
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/transparent_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/typed_arrays.png b/bridge/tests/res/testApp/MyApplication/golden/typed_arrays.png
index 5e13aac..e3f4df7 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/typed_arrays.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/typed_arrays.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png
index 88f72c6..ecc4497 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png
index 5e27b09..1d4ac39 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.png
index 89da005..0268d9c 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_multi_line_of_path_data.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_multi_line_of_path_data.png
deleted file mode 100644
index 6422056..0000000
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_multi_line_of_path_data.png
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.png
index a3dd935..a27a48e 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_in_image_view.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_in_image_view.png
index dc8bd06..48f0f05 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_in_image_view.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_in_image_view.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_itself.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_itself.png
index 9e13c62..92855d1 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_itself.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_itself.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/view_boundaries.png b/bridge/tests/res/testApp/MyApplication/golden/view_boundaries.png
index b9a5001..da770e1 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/view_boundaries.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/view_boundaries.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/view_stub.png b/bridge/tests/res/testApp/MyApplication/golden/view_stub.png
index 7e32c13..b66ff2d 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/view_stub.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/view_stub.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/window_background.png b/bridge/tests/res/testApp/MyApplication/golden/window_background.png
new file mode 100644
index 0000000..1d25725
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/window_background.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/DialogView.java b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/DialogView.java
new file mode 100644
index 0000000..983b96c
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/DialogView.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 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.test.myapplication.widgets;
+
+import android.app.AlertDialog.Builder;
+import android.app.Dialog;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+public class DialogView extends View {
+    private Dialog dialog;
+
+    public DialogView(Context context) {
+        super(context);
+        init(context);
+    }
+
+    public DialogView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(context);
+    }
+
+    public DialogView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        init(context);
+    }
+
+    private void init(Context context) {
+        Builder builder = new Builder(context);
+        dialog = builder.setMessage("My message")
+                .setTitle("My title")
+                .setPositiveButton("Ok", (dialog, id) -> {})
+                .setNegativeButton("Cancel", (dialog, id) -> {})
+                .create();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // This creates the dialog view and adds it to the hierarchy before
+        // measuring happens.
+        dialog.show();
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+}
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/ninepatch.9.png b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/ninepatch.9.png
index fb3660e..c0bbc81 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/ninepatch.9.png
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/ninepatch.9.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/uncompiled_ninepatch.9.png b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/uncompiled_ninepatch.9.png
new file mode 100644
index 0000000..fb3660e
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/uncompiled_ninepatch.9.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/ninepatch_drawable.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/ninepatch_drawable.xml
new file mode 100644
index 0000000..c14388d
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/ninepatch_drawable.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ Copyright (C) 2019 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.
+  -->
+
+<nine-patch
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/uncompiled_ninepatch" />
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/theme_attribute_drawable.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/theme_attribute_drawable.xml
new file mode 100644
index 0000000..c3bfe66
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/theme_attribute_drawable.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ Copyright (C) 2021 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.
+  -->
+
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="?android:attr/colorAccent" />
+</layer-list>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/transparent_drawable.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/transparent_drawable.xml
new file mode 100644
index 0000000..e7cbc06
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/transparent_drawable.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="#55FF0000"/>
+    <size
+        android:width="48dp"
+        android:height="48dp"/>
+</shape>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_scrolling_view_test.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_scrolling_view_test.xml
new file mode 100644
index 0000000..f8ef145
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_scrolling_view_test.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                xmlns:app="http://schemas.android.com/apk/res-auto"
+                xmlns:tools="http://schemas.android.com/tools"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+
+        android:text="vertical_scrollview_example"
+        android:id="@+id/textView"
+        android:layout_gravity="center_horizontal"
+        android:layout_centerHorizontal="true"
+        android:layout_alignParentTop="true" />
+
+    <ScrollView
+        android:layout_marginTop="30dp"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:id="@+id/scrollView">
+
+
+    </ScrollView>
+
+</RelativeLayout>
+
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_switch_text_contrast.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_switch_text_contrast.xml
new file mode 100644
index 0000000..db408f1
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_switch_text_contrast.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 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"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#FFFFFFFF">
+    <!-- Low contrast, with slightly transparent text -->
+    <Switch
+        android:text="Switch"
+        android:textColor="#3000FFFF"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minHeight="48dp"
+        android:id="@+id/switch1"
+        tools:layout_editor_absoluteY="191dp"
+        tools:layout_editor_absoluteX="104dp" />
+</LinearLayout>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/fonts_test.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/fonts_test.xml
index c63b211..67869c8 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/fonts_test.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/fonts_test.xml
@@ -23,7 +23,8 @@
         android:layout_height="wrap_content"
         android:text="MONOSPACE"
         android:textSize="50sp"
-        android:fontFamily="monospace"/>
+        android:fontFamily="monospace"
+        android:rotationX="45"/>
 
     <Space
         android:layout_width="wrap_content"
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/justified_inter_word.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/justified_inter_word.xml
new file mode 100644
index 0000000..6e9f183
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/justified_inter_word.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+  android:layout_width="match_parent" android:layout_height="match_parent">
+
+  <TextView
+    android:id="@+id/justified"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:justificationMode="inter_word"
+    android:text="@string/large_text"/>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/justified_none.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/justified_none.xml
new file mode 100644
index 0000000..ffd757e
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/justified_none.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+  android:layout_width="match_parent" android:layout_height="match_parent">
+
+  <TextView
+    android:id="@+id/justified"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:justificationMode="none"
+    android:text="@string/large_text"/>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml
index ff14ce0..568279b 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml
@@ -16,9 +16,11 @@
         android:layout_gravity="center"
         />
     <com.android.layoutlib.test.myapplication.widgets.CustomDate
+        android:firstDayOfWeek="1"
         android:layout_width="100dp"
         android:layout_height="wrap_content"/>
     <com.android.layoutlib.test.myapplication.widgets.CustomCalendar
+        android:firstDayOfWeek="1"
         android:layout_width="200dp"
         android:layout_gravity="center_horizontal"
         android:layout_height="200dp"/>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/values/strings.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/values/strings.xml
index 2b7083b..f4ff361 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/values/strings.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/values/strings.xml
@@ -4,5 +4,94 @@
     <string name="app_name">My Application</string>
     <string name="hello_world">Hello world!</string>
     <string name="action_settings">Settings</string>
+    <string name="large_text">
+        "Material is the metaphor.\n\n"
+
+        "A material metaphor is the unifying theory of a rationalized space and a system of motion."
+        "The material is grounded in tactile reality, inspired by the study of paper and ink, yet "
+        "technologically advanced and open to imagination and magic.\n"
+        "Surfaces and edges of the material provide visual cues that are grounded in reality. The "
+        "use of familiar tactile attributes helps users quickly understand affordances. Yet the "
+        "flexibility of the material creates new affordances that supercede those in the physical "
+        "world, without breaking the rules of physics.\n"
+        "The fundamentals of light, surface, and movement are key to conveying how objects move, "
+        "interact, and exist in space and in relation to each other. Realistic lighting shows "
+        "seams, divides space, and indicates moving parts.\n\n"
+
+        "Bold, graphic, intentional.\n\n"
+
+        "The foundational elements of print based design typography, grids, space, scale, color, "
+        "and use of imagery guide visual treatments. These elements do far more than please the "
+        "eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge "
+        "imagery, large scale typography, and intentional white space create a bold and graphic "
+        "interface that immerse the user in the experience.\n"
+        "An emphasis on user actions makes core functionality immediately apparent and provides "
+        "waypoints for the user.\n\n"
+
+        "Motion provides meaning.\n\n"
+
+        "Motion respects and reinforces the user as the prime mover. Primary user actions are "
+        "inflection points that initiate motion, transforming the whole design.\n"
+        "All action takes place in a single environment. Objects are presented to the user without "
+        "breaking the continuity of experience even as they transform and reorganize.\n"
+        "Motion is meaningful and appropriate, serving to focus attention and maintain continuity. "
+        "Feedback is subtle yet clear. Transitions are efficient yet coherent.\n\n"
+
+        "3D world.\n\n"
+
+        "The material environment is a 3D space, which means all objects have x, y, and z "
+        "dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the "
+        "positive z-axis extending towards the viewer. Every sheet of material occupies a single "
+        "position along the z-axis and has a standard 1dp thickness.\n"
+        "On the web, the z-axis is used for layering and not for perspective. The 3D world is "
+        "emulated by manipulating the y-axis.\n\n"
+
+        "Light and shadow.\n\n"
+
+        "Within the material environment, virtual lights illuminate the scene. Key lights create "
+        "directional shadows, while ambient light creates soft shadows from all angles.\n"
+        "Shadows in the material environment are cast by these two light sources. In Android "
+        "development, shadows occur when light sources are blocked by sheets of material at "
+        "various positions along the z-axis. On the web, shadows are depicted by manipulating the "
+        "y-axis only. The following example shows the card with a height of 6dp.\n\n"
+
+        "Resting elevation.\n\n"
+
+        "All material objects, regardless of size, have a resting elevation, or default elevation "
+        "that does not change. If an object changes elevation, it should return to its resting "
+        "elevation as soon as possible.\n\n"
+
+        "Component elevations.\n\n"
+
+        "The resting elevation for a component type is consistent across apps (e.g., FAB elevation "
+        "does not vary from 6dp in one app to 16dp in another app).\n"
+        "Components may have different resting elevations across platforms, depending on the depth "
+        "of the environment (e.g., TV has a greater depth than mobile or desktop).\n\n"
+
+        "Responsive elevation and dynamic elevation offsets.\n\n"
+
+        "Some component types have responsive elevation, meaning they change elevation in response "
+        "to user input (e.g., normal, focused, and pressed) or system events. These elevation "
+        "changes are consistently implemented using dynamic elevation offsets.\n"
+        "Dynamic elevation offsets are the goal elevation that a component moves towards, relative "
+        "to the component’s resting state. They ensure that elevation changes are consistent "
+        "across actions and component types. For example, all components that lift on press have "
+        "the same elevation change relative to their resting elevation.\n"
+        "Once the input event is completed or cancelled, the component will return to its resting "
+        "elevation.\n\n"
+
+        "Avoiding elevation interference.\n\n"
+
+        "Components with responsive elevations may encounter other components as they move between "
+        "their resting elevations and dynamic elevation offsets. Because material cannot pass "
+        "through other material, components avoid interfering with one another any number of ways, "
+        "whether on a per component basis or using the entire app layout.\n"
+        "On a component level, components can move or be removed before they cause interference. "
+        "For example, a floating action button (FAB) can disappear or move off screen before a "
+        "user picks up a card, or it can move if a snackbar appears.\n"
+        "On the layout level, design your app layout to minimize opportunities for interference. "
+        "For example, position the FAB to one side of stream of a cards so the FAB won’t interfere "
+        "when a user tries to pick up one of cards.\n\n"
+    </string>
 
 </resources>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
index 9e84a53..7b338d1 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
@@ -11,4 +11,9 @@
         <item name="android:layout_height">150dp</item>
     </style>
 
+    <style name="WindowBackgroundTheme" parent="android:Theme.Material.Light.DarkActionBar">
+        <item name="android:colorAccent">#ffff0000</item>
+        <item name="android:windowBackground">@drawable/theme_attribute_drawable</item>
+    </style>
+
 </resources>
diff --git a/bridge/tests/run_tests.sh b/bridge/tests/run_tests.sh
index 3c68921..de2ed79 100755
--- a/bridge/tests/run_tests.sh
+++ b/bridge/tests/run_tests.sh
@@ -12,9 +12,40 @@
 STUDIO_JDK=${SCRIPT_DIR}"/../../../../prebuilts/jdk/jdk11/linux-x86"
 MISC_COMMON=${SCRIPT_DIR}"/../../../../prebuilts/misc/common"
 OUT_INTERMEDIATES=${SCRIPT_DIR}"/../../../../out/soong/.intermediates"
+NATIVE_LIBRARIES=${SCRIPT_DIR}"/../../../../out/host/linux-x86/lib64/"
+SDK=${SCRIPT_DIR}"/../../../../out/host/linux-x86/sdk/sdk*/android-sdk*"
+SDK_REPO=${SCRIPT_DIR}"/../../../../out/host/linux-x86/sdk-repo"
+FONT_DIR=${SCRIPT_DIR}"/../../../../out/host/common/obj/PACKAGING/fonts_intermediates"
+ICU_DATA_PATH=${SCRIPT_DIR}"/../../../../out/host/linux-x86/com.android.i18n/etc/icu/icudt70l.dat"
+TMP_DIR=$(mktemp -d)
+PLATFORM=${TMP_DIR}/"android"
+
+# Copy resources to a temp directory
+cp -r ${SDK}/platforms/android* ${PLATFORM}
+
+# Unzip build-tools to access aapt2
+mkdir ${TMP_DIR}/build-tools
+unzip -q ${SDK_REPO}/sdk-repo-linux-build-tools.zip -d ${TMP_DIR}/build-tools
+
+# Compile 9-patch files
+mkdir ${TMP_DIR}/compiled
+mkdir ${TMP_DIR}/manifest
+echo \
+'<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.google.android.layoutlib" />' \
+> ${TMP_DIR}/manifest/AndroidManifest.xml
+find ${SDK}/platforms/android*/data/res -name "*.9.png" -print0 | xargs -0 ${TMP_DIR}/build-tools/android-*/aapt2 compile -o ${TMP_DIR}/compiled/
+find ${TMP_DIR}/compiled -name "*.flat" -print0 | xargs -0 -s 1000000 ${TMP_DIR}/build-tools/android-*/aapt2 link -o ${TMP_DIR}/compiled.apk --manifest ${TMP_DIR}/manifest/AndroidManifest.xml -R
+unzip -q ${TMP_DIR}/compiled.apk -d ${TMP_DIR}
+for f in ${TMP_DIR}/res/*; do mv "$f" "${f/-v4/}";done
+cp -RL ${TMP_DIR}/res ${PLATFORM}/data
 
 # Run layoutlib tests
 ${STUDIO_JDK}/bin/java -ea \
+    -Dnative.lib.path=${NATIVE_LIBRARIES} \
+    -Dfont.dir=${FONT_DIR} \
+    -Dicu.data.path=${ICU_DATA_PATH} \
+    -Dplatform.dir=${PLATFORM} \
     -Dtest_res.dir=${SCRIPT_DIR}/res \
     -Dtest_failure.dir=${OUT_DIR}/${FAILURE_DIR} \
     -cp ${MISC_COMMON}/tools-common/tools-common-prebuilt.jar:${MISC_COMMON}/ninepatch/ninepatch-prebuilt.jar:${MISC_COMMON}/sdk-common/sdk-common.jar:${MISC_COMMON}/kxml2/kxml2-2.3.0.jar:${MISC_COMMON}/layoutlib_api/layoutlib_api-prebuilt.jar:${OUT_INTERMEDIATES}/prebuilts/tools/common/m2/trove-prebuilt/linux_glibc_common/combined/trove-prebuilt.jar:${OUT_INTERMEDIATES}/external/junit/junit/linux_glibc_common/javac/junit.jar:${OUT_INTERMEDIATES}/external/guava/guava-jre/linux_glibc_common/javac/guava-jre.jar:${OUT_INTERMEDIATES}/external/hamcrest/hamcrest-core/hamcrest/linux_glibc_common/javac/hamcrest.jar:${OUT_INTERMEDIATES}/external/mockito/mockito/linux_glibc_common/combined/mockito.jar:${OUT_INTERMEDIATES}/external/objenesis/objenesis/linux_glibc_common/javac/objenesis.jar:${OUT_INTERMEDIATES}/frameworks/layoutlib/bridge/layoutlib/linux_glibc_common/withres/layoutlib.jar:${OUT_INTERMEDIATES}/frameworks/layoutlib/temp_layoutlib/linux_glibc_common/gen/temp_layoutlib.jar:${OUT_INTERMEDIATES}/frameworks/layoutlib/bridge/tests/layoutlib-tests/linux_glibc_common/withres/layoutlib-tests.jar \
@@ -33,4 +64,8 @@
     mv ${OUT_DIR}/${FAILURE_ZIP} ${DIST_DIR}
 fi
 
+# Clean
+rm -rf ${TMP_DIR}
+rm -rf ${OUT_DIR}/${FAILURE_DIR}
+
 exit ${test_exit_code}
diff --git a/bridge/tests/run_tests_mac.sh b/bridge/tests/run_tests_mac.sh
new file mode 100755
index 0000000..0649f25
--- /dev/null
+++ b/bridge/tests/run_tests_mac.sh
@@ -0,0 +1,82 @@
+#!/bin/bash
+
+# There is no macOS build of the SDK anymore
+# Do not run layoutlib tests
+exit 0
+
+readonly OUT_DIR="$1"
+readonly DIST_DIR="$2"
+readonly BUILD_NUMBER="$3"
+
+readonly SCRIPT_DIR="$(dirname "$0")"
+
+readonly FAILURE_DIR=layoutlib-test-failures
+readonly FAILURE_ZIP=layoutlib-test-failures.zip
+
+STUDIO_JDK=${SCRIPT_DIR}"/../../../../prebuilts/jdk/jdk11/darwin-x86"
+MISC_COMMON=${SCRIPT_DIR}"/../../../../prebuilts/misc/common"
+OUT_INTERMEDIATES=${SCRIPT_DIR}"/../../../../out/soong/.intermediates"
+NATIVE_LIBRARIES=${SCRIPT_DIR}"/../../../../out/host/darwin-x86/lib64/"
+SDK=${SCRIPT_DIR}"/../../../../out/host/darwin-x86/sdk/sdk*/android-sdk*"
+SDK_REPO=${SCRIPT_DIR}"/../../../../out/soong/host/linux-x86/sdk-repo"
+FONT_DIR=${SCRIPT_DIR}"/../../../../out/host/common/obj/PACKAGING/fonts_intermediates"
+ICU_DATA_PATH=${SCRIPT_DIR}"/../../../../out/host/darwin-x86/com.android.i18n/etc/icu/icudt69l.dat"
+TMP_DIR=$(mktemp -d -t tmp)
+PLATFORM=${TMP_DIR}/"android"
+
+# Copy resources to a temp directory
+cp -r ${SDK}/platforms/android* ${PLATFORM}
+
+# Unzip build-tools to access aapt2
+mkdir ${TMP_DIR}/build-tools
+unzip -q ${SDK_REPO}/sdk-repo-linux-build-tools.zip -d ${TMP_DIR}/build-tools
+
+# Compile 9-patch files
+mkdir ${TMP_DIR}/compiled
+mkdir ${TMP_DIR}/manifest
+echo \
+'<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.google.android.layoutlib" />' \
+> ${TMP_DIR}/manifest/AndroidManifest.xml
+for f in ${SDK}/platforms/android*/data/res/*
+do
+    find $f -name "*.9.png" -print0 | xargs -0 ${TMP_DIR}/build-tools/android-*/aapt2 compile -o ${TMP_DIR}/compiled/
+    find ${TMP_DIR}/compiled -name "*.flat" -print0 | xargs -0 ${TMP_DIR}/build-tools/android-*/aapt2 link -o ${TMP_DIR}/compiled.apk --manifest ${TMP_DIR}/manifest/AndroidManifest.xml -R
+    if [[ -f "${TMP_DIR}/compiled.apk" ]]; then
+        unzip -qo ${TMP_DIR}/compiled.apk -d ${TMP_DIR}
+        rm -r ${TMP_DIR}/compiled/*
+        rm ${TMP_DIR}/compiled.apk
+    fi
+done
+for f in ${TMP_DIR}/res/*; do mv "$f" "${f/-v4/}";done
+cp -RL ${TMP_DIR}/res ${PLATFORM}/data
+
+# Run layoutlib tests
+${STUDIO_JDK}/bin/java -ea \
+    -Dnative.lib.path=${NATIVE_LIBRARIES} \
+    -Dfont.dir=${FONT_DIR} \
+    -Dicu.data.path=${ICU_DATA_PATH} \
+    -Dplatform.dir=${PLATFORM} \
+    -Dtest_res.dir=${SCRIPT_DIR}/res \
+    -Dtest_failure.dir=${OUT_DIR}/${FAILURE_DIR} \
+    -cp ${MISC_COMMON}/tools-common/tools-common-prebuilt.jar:${MISC_COMMON}/ninepatch/ninepatch-prebuilt.jar:${MISC_COMMON}/sdk-common/sdk-common.jar:${MISC_COMMON}/kxml2/kxml2-2.3.0.jar:${MISC_COMMON}/layoutlib_api/layoutlib_api-prebuilt.jar:${OUT_INTERMEDIATES}/prebuilts/tools/common/m2/trove-prebuilt/darwin_common/combined/trove-prebuilt.jar:${OUT_INTERMEDIATES}/external/junit/junit/darwin_common/javac/junit.jar:${OUT_INTERMEDIATES}/external/guava/guava-jre/darwin_common/javac/guava-jre.jar:${OUT_INTERMEDIATES}/external/hamcrest/hamcrest-core/hamcrest/darwin_common/javac/hamcrest.jar:${OUT_INTERMEDIATES}/external/mockito/mockito/darwin_common/combined/mockito.jar:${OUT_INTERMEDIATES}/external/objenesis/objenesis/darwin_common/javac/objenesis.jar:${OUT_INTERMEDIATES}/frameworks/layoutlib/bridge/layoutlib/darwin_common/withres/layoutlib.jar:${OUT_INTERMEDIATES}/frameworks/layoutlib/temp_layoutlib/darwin_common/gen/temp_layoutlib.jar:${OUT_INTERMEDIATES}/frameworks/layoutlib/bridge/tests/layoutlib-tests/darwin_common/withres/layoutlib-tests.jar \
+    org.junit.runner.JUnitCore \
+    com.android.layoutlib.bridge.intensive.Main
+
+test_exit_code=$?
+
+# Create zip of all failure screenshots
+if [[ -d "${OUT_DIR}/${FAILURE_DIR}" ]]; then
+    zip -q -j -r ${OUT_DIR}/${FAILURE_ZIP} ${OUT_DIR}/${FAILURE_DIR}
+fi
+
+# Move failure zip to dist directory if specified
+if [[ -d "${DIST_DIR}" ]] && [[ -e "${OUT_DIR}/${FAILURE_ZIP}" ]]; then
+    mv ${OUT_DIR}/${FAILURE_ZIP} ${DIST_DIR}
+fi
+
+# Clean
+rm -rf ${TMP_DIR}
+rm -rf ${OUT_DIR}/${FAILURE_DIR}
+
+exit ${test_exit_code}
diff --git a/bridge/tests/src/android/content/res/BridgeTypedArrayTest.java b/bridge/tests/src/android/content/res/BridgeTypedArrayTest.java
index 0be3423..3975927 100644
--- a/bridge/tests/src/android/content/res/BridgeTypedArrayTest.java
+++ b/bridge/tests/src/android/content/res/BridgeTypedArrayTest.java
@@ -38,6 +38,7 @@
     @Test
     public void getType() {
         assertEquals(TYPE_NULL, BridgeTypedArray.getType(null));
+        assertEquals(TYPE_STRING, BridgeTypedArray.getType(""));
         assertEquals(TYPE_REFERENCE, BridgeTypedArray.getType("@drawable/my_drawable"));
         assertEquals(TYPE_ATTRIBUTE, BridgeTypedArray.getType("?attr/colorPrimary"));
         assertEquals(TYPE_INT_BOOLEAN, BridgeTypedArray.getType("true"));
@@ -50,6 +51,7 @@
         assertEquals(TYPE_INT_COLOR_ARGB8, BridgeTypedArray.getType("#1f34dc28"));
         assertEquals(TYPE_STRING, BridgeTypedArray.getType("#notacolor"));
         assertEquals(TYPE_DIMENSION, BridgeTypedArray.getType("16dp"));
+        assertEquals(TYPE_DIMENSION, BridgeTypedArray.getType(".16dp"));
         assertEquals(TYPE_STRING, BridgeTypedArray.getType("16notaunit"));
         assertEquals(TYPE_INT_DEC, BridgeTypedArray.getType("98543"));
         assertEquals(TYPE_FLOAT, BridgeTypedArray.getType("43.364"));
diff --git a/bridge/tests/src/android/graphics/Color_DelegateTest.java b/bridge/tests/src/android/graphics/Color_DelegateTest.java
deleted file mode 100644
index 1b9c13e..0000000
--- a/bridge/tests/src/android/graphics/Color_DelegateTest.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2018 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 junit.framework.TestCase;
-
-public class Color_DelegateTest extends TestCase {
-
-    public void testRGBToHSV() {
-        float[] hsv = new float[3];
-        Color_Delegate.nativeRGBToHSV(14, 203, 49, hsv);
-        assertTrue(131.1111 - hsv[0] < 0.001);
-        assertTrue(0.9310 - hsv[1] < 0.001);
-        assertTrue(0.7961 - hsv[2] < 0.001);
-    }
-
-    public void testHSVToColor() {
-        assertEquals(2077003642,
-                Color_Delegate.nativeHSVToColor(123, new float[]{15.0f, 0.4f, 0.8f}));
-        assertEquals(603979776,
-                Color_Delegate.nativeHSVToColor(36, new float[]{15.0f, 25.0f, -17.0f}));
-    }
-}
diff --git a/bridge/tests/src/android/graphics/Matrix_DelegateTest.java b/bridge/tests/src/android/graphics/Matrix_DelegateTest.java
deleted file mode 100644
index 0886132..0000000
--- a/bridge/tests/src/android/graphics/Matrix_DelegateTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import java.util.Arrays;
-
-import junit.framework.TestCase;
-
-/**
- *
- */
-public class Matrix_DelegateTest extends TestCase {
-
-    public void testIdentity() {
-        Matrix m1 = new Matrix();
-
-        assertTrue(m1.isIdentity());
-
-        m1.setValues(new float[]{1, 0, 0, 0, 1, 0, 0, 0, 1});
-        assertTrue(m1.isIdentity());
-    }
-
-    public void testCopyConstructor() {
-        Matrix m1 = new Matrix();
-        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 testInvert() {
-        Matrix m1 = new Matrix();
-        Matrix inverse = new Matrix();
-        m1.setValues(new float[]{1, 2, 3, 4, 5, 6, 7, 8, 9});
-        assertFalse(m1.invert(inverse));
-
-        m1.setValues(new float[]{3, 5, 6, 2, 5, 7, 4, 8, 2});
-        m1.invert(inverse);
-        float[] values = new float[9];
-        inverse.getValues(values);
-
-        assertTrue(Arrays.equals(values,
-                new float[]{1.0952381f, -0.9047619f, -0.11904762f, -0.5714286f, 0.42857143f,
-                        0.21428572f, 0.0952381f, 0.0952381f, -0.11904762f}));
-    }
-}
diff --git a/bridge/tests/src/android/util/imagepool/ImagePoolHelperTest.java b/bridge/tests/src/android/util/imagepool/ImagePoolHelperTest.java
deleted file mode 100644
index 25aa466..0000000
--- a/bridge/tests/src/android/util/imagepool/ImagePoolHelperTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2018 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.imagepool;
-
-import org.junit.Test;
-
-import android.util.imagepool.Bucket.BucketCreationMetaData;
-import android.util.imagepool.ImagePool.Image.Orientation;
-
-import java.awt.image.BufferedImage;
-import java.lang.ref.SoftReference;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-public class ImagePoolHelperTest {
-
-    @Test
-    public void testGetBufferedImage() {
-        int width = 10;
-        int height = 10;
-        int numberOfCopiesInBucket = 10;
-        int maxCacheSize = width * height * 4 * 5; // can fit 5 width | height buffer
-        Bucket bucket = new Bucket();
-        BucketCreationMetaData metaData = new BucketCreationMetaData(
-                width, height, BufferedImage.TYPE_INT_ARGB, numberOfCopiesInBucket, Orientation
-                .NONE, maxCacheSize);
-        ImagePoolStats stats = new ImagePoolStatsProdImpl();
-
-        assertNotNull(ImagePoolHelper.getBufferedImage(bucket, metaData, stats));
-    }
-
-    @Test
-    public void testGetBufferedImageRecurse() {
-        int width = 10;
-        int height = 10;
-        int numberOfCopiesToRequestInBucket = 1;
-        int numberOfCopiesInBucket = 10;
-        int maxCacheSize = width * height * 4 * numberOfCopiesToRequestInBucket;
-
-        Bucket bucket = new Bucket();
-        for (int i = 0; i < numberOfCopiesInBucket; i++) {
-            bucket.mBufferedImageRef.add(new SoftReference<>(null));
-        }
-        BucketCreationMetaData metaData = new BucketCreationMetaData(
-                width, height, BufferedImage.TYPE_INT_ARGB, numberOfCopiesToRequestInBucket, Orientation
-                .NONE, maxCacheSize);
-        ImagePoolStats stats = new ImagePoolStatsProdImpl();
-
-        assertNotNull(ImagePoolHelper.getBufferedImage(bucket, metaData, stats));
-    }
-
-    @Test
-    public void testRecurseThenHitCacheLimit() {
-        int width = 10;
-        int height = 10;
-        int numberOfCopiesToRequestInBucket = 1;
-        int numberOfCopiesInBucket = 10;
-        int maxCacheSize = width * height * 4 * numberOfCopiesToRequestInBucket / 2;
-
-        Bucket bucket = new Bucket();
-        for (int i = 0; i < numberOfCopiesInBucket; i++) {
-            bucket.mBufferedImageRef.add(new SoftReference<>(null));
-        }
-        BucketCreationMetaData metaData = new BucketCreationMetaData(
-                width, height, BufferedImage.TYPE_INT_ARGB, numberOfCopiesToRequestInBucket, Orientation
-                .NONE, maxCacheSize);
-        ImagePoolStats stats = new ImagePoolStatsProdImpl();
-
-        assertNull(ImagePoolHelper.getBufferedImage(bucket, metaData, stats));
-    }
-
-    @Test
-    public void testBucketHasImageToReturn() {
-        int width = 10;
-        int height = 10;
-        int numberOfCopiesToRequestInBucket = 1;
-        int numberOfCopiesInBucket = 10;
-        int maxCacheSize = width * height * 4 * numberOfCopiesToRequestInBucket / 2;
-        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
-
-        Bucket bucket = new Bucket();
-        for (int i = 0; i < numberOfCopiesInBucket; i++) {
-            bucket.mBufferedImageRef.add(new SoftReference<>(null));
-        }
-        bucket.mBufferedImageRef.add(new SoftReference<>(image));
-        BucketCreationMetaData metaData = new BucketCreationMetaData(
-                width, height, BufferedImage.TYPE_INT_ARGB, numberOfCopiesToRequestInBucket, Orientation
-                .NONE, maxCacheSize);
-        ImagePoolStats stats = new ImagePoolStatsProdImpl();
-
-        assertNotNull(ImagePoolHelper.getBufferedImage(bucket, metaData, stats));
-    }
-}
\ No newline at end of file
diff --git a/bridge/tests/src/android/util/imagepool/ImagePoolImplTest.java b/bridge/tests/src/android/util/imagepool/ImagePoolImplTest.java
deleted file mode 100644
index 9761097..0000000
--- a/bridge/tests/src/android/util/imagepool/ImagePoolImplTest.java
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * Copyright (C) 2018 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.imagepool;
-
-import org.junit.Ignore;
-import org.junit.Test;
-
-import android.util.imagepool.ImagePool.Image;
-import android.util.imagepool.ImagePool.ImagePoolPolicy;
-
-import java.awt.image.BufferedImage;
-import java.lang.ref.SoftReference;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-public class ImagePoolImplTest {
-
-    private static final long TIMEOUT_SEC = 3;
-
-    @Test
-    public void testImagePoolInstance() {
-        ImagePool pool1 = ImagePoolProvider.get();
-        ImagePool pool2 = ImagePoolProvider.get();
-        assertNotNull(pool1);
-        assertNotNull(pool2);
-        assertEquals(pool1, pool2);
-    }
-
-
-    @Test
-    public void testImageDispose() throws InterruptedException {
-        int width = 700;
-        int height = 800;
-        int type = BufferedImage.TYPE_INT_ARGB;
-        CountDownLatch countDownLatch = new CountDownLatch(1);
-        ImagePoolImpl pool = getSimpleSingleBucketPool(width, height);
-        Image img1 = pool.acquire(width, height, type,
-                bufferedImage -> countDownLatch.countDown());
-        BufferedImage img = getImg(img1);
-        assertNotNull(img);
-        img1 = null;
-
-        // ensure dispose actually loses buffered image link so it can be gc'd
-        gc();
-        assertTrue(countDownLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS));
-    }
-    @Test
-    public void testImageDisposeFromFunction() throws InterruptedException {
-        int width = 700;
-        int height = 800;
-        int type = BufferedImage.TYPE_INT_ARGB;
-        CountDownLatch cd = new CountDownLatch(1);
-        ImagePoolImpl pool = getSimpleSingleBucketPool(width, height);
-
-        BufferedImage img = createImageAndReturnBufferedImage(pool, width, height, type, cd);
-        assertNotNull(img);
-
-        // ensure dispose actually loses buffered image link so it can be gc'd
-        gc();
-        assertTrue(cd.await(TIMEOUT_SEC, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void testImageDisposedAndRecycled() throws InterruptedException {
-        int width = 700;
-        int height = 800;
-        int bucketWidth = 800;
-        int bucketHeight = 800;
-        int variant = 1;
-        int type = BufferedImage.TYPE_INT_ARGB;
-        ImagePoolImpl pool = new ImagePoolImpl(new ImagePoolPolicy(
-                new int[]{bucketWidth, bucketHeight},
-                new int[]{1, 1},
-                bucketHeight * bucketWidth * 4 * 3));
-
-        // acquire first image and draw something.
-        BufferedImage bufferedImageForImg1;
-        final CountDownLatch countDownLatch1 = new CountDownLatch(1);
-        {
-            Image img1 = pool.acquire(width, height, type,
-                    bufferedImage -> countDownLatch1.countDown());
-            bufferedImageForImg1 = getImg(img1);
-            img1 = null; // this is still needed.
-        }
-        // dispose
-        gc();
-        assertTrue(countDownLatch1.await(TIMEOUT_SEC, TimeUnit.SECONDS));
-
-        // ensure dispose actually loses buffered image link so it can be gc'd
-        assertNotNull(bufferedImageForImg1);
-        assertEquals(bufferedImageForImg1.getWidth(), bucketWidth);
-        assertEquals(bufferedImageForImg1.getHeight(), bucketHeight);
-
-        // get 2nd image with the same spec
-        final CountDownLatch countDownLatch2 = new CountDownLatch(1);
-        BufferedImage bufferedImageForImg2;
-        {
-            Image img2 = pool.acquire(width - variant, height - variant, type,
-                    bufferedImage -> countDownLatch2.countDown());
-            bufferedImageForImg2 = getImg(img2);
-            assertEquals(bufferedImageForImg1, bufferedImageForImg2);
-            img2 = null;
-        }
-        // dispose
-        gc();
-        assertTrue(countDownLatch2.await(TIMEOUT_SEC, TimeUnit.SECONDS));
-
-        // ensure that we're recycling previously created buffered image.
-        assertNotNull(bufferedImageForImg1);
-        assertNotNull(bufferedImageForImg2);
-    }
-
-
-    @Test
-    public void testBufferedImageReleased() throws InterruptedException {
-        int width = 700;
-        int height = 800;
-        int bucketWidth = 800;
-        int bucketHeight = 800;
-        ImagePoolImpl pool = new ImagePoolImpl(new ImagePoolPolicy(
-                new int[]{bucketWidth, bucketHeight},
-                new int[]{1, 1},
-                bucketWidth * bucketWidth * 4 * 2));
-        CountDownLatch countDownLatch = new CountDownLatch(1);
-        Image image1 = pool.acquire(width, height, BufferedImage.TYPE_INT_ARGB,
-                bufferedImage -> countDownLatch.countDown());
-        BufferedImage internalPtr = getImg(image1);
-        // dispose
-        image1 = null;
-        gc();
-        assertTrue(countDownLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS));
-
-        // Simulate BufferedBitmaps being gc'd. Bucket filled with null soft refs.
-        for (Bucket bucket : ((ImagePoolImpl) pool).mPool.values()) {
-            bucket.mBufferedImageRef.clear();
-            bucket.mBufferedImageRef.add(new SoftReference<>(null));
-            bucket.mBufferedImageRef.add(new SoftReference<>(null));
-        }
-
-        assertNotEquals(internalPtr,
-                getImg(pool.acquire(width, height, BufferedImage.TYPE_INT_ARGB)));
-    }
-
-    @Test
-    public void testPoolWidthHeightNotBigEnough() {
-        int width = 1000;
-        int height = 1000;
-        int bucketWidth = 999;
-        int bucketHeight = 800;
-        ImagePool pool = new ImagePoolImpl(
-                new ImagePoolPolicy(new int[]{bucketWidth, bucketHeight}, new int[]{1, 1},
-                        bucketWidth * bucketWidth * 4 * 2));
-        ImageImpl image = (ImageImpl) pool.acquire(width, height, BufferedImage.TYPE_INT_ARGB);
-
-        assertEquals(getTooBigForPoolCount(pool), 1);
-    }
-
-    @Test
-    public void testSizeNotBigEnough() {
-        int width = 500;
-        int height = 500;
-        int bucketWidth = 800;
-        int bucketHeight = 800;
-        ImagePoolImpl pool = new ImagePoolImpl(
-                new ImagePoolPolicy(new int[]{bucketWidth, bucketHeight}, new int[]{1, 1},
-                        bucketWidth * bucketWidth)); // cache not big enough.
-        ImageImpl image = (ImageImpl) pool.acquire(width, height, BufferedImage.TYPE_INT_ARGB);
-
-        assertEquals(getTooBigForPoolCount(pool), 1);
-        assertEquals(image.getWidth(), width);
-        assertEquals(image.getHeight(), height);
-    }
-
-    @Test
-    public void testImageMultipleCopies() throws InterruptedException {
-        int width = 700;
-        int height = 800;
-        int bucketWidth = 800;
-        int bucketHeight = 800;
-        int type = BufferedImage.TYPE_INT_ARGB;
-        ImagePoolImpl pool = new ImagePoolImpl(new ImagePoolPolicy(
-                new int[]{bucketWidth, bucketHeight},
-                new int[]{2, 2},
-                bucketHeight * bucketWidth * 4 * 4));
-
-        // create 1, and 2 different instances.
-        final CountDownLatch cd1 = new CountDownLatch(1);
-        Image img1 = pool.acquire(width, height, type, bufferedImage -> cd1.countDown());
-        BufferedImage bufferedImg1 = getImg(img1);
-
-        Image img2 = pool.acquire(width, height, type);
-        BufferedImage bufferedImg2 = getImg(img2);
-
-        assertNotEquals(bufferedImg1, bufferedImg2);
-
-        // disposing img1. Since # of copies == 2, this buffer should be recycled.
-        img1 = null;
-        gc();
-        cd1.await(TIMEOUT_SEC, TimeUnit.SECONDS);
-
-        // Ensure bufferedImg1 is recycled in newly acquired img3.
-        Image img3 = pool.acquire(width, height, type);
-        BufferedImage bufferedImage3 = getImg(img3);
-        assertNotEquals(bufferedImg2, bufferedImage3);
-        assertEquals(bufferedImg1, bufferedImage3);
-    }
-
-    @Ignore("b/132614809")
-    @Test
-    public void testPoolDispose() throws InterruptedException {
-        int width = 700;
-        int height = 800;
-        int bucketWidth = 800;
-        int bucketHeight = 800;
-        int type = BufferedImage.TYPE_INT_ARGB;
-
-        // Pool barely enough for 1 image.
-        ImagePoolImpl pool = new ImagePoolImpl(new ImagePoolPolicy(
-                new int[]{bucketWidth, bucketHeight},
-                new int[]{2, 2},
-                bucketHeight * bucketWidth * 4));
-
-        // create 1, and 2 different instances.
-        final CountDownLatch cd1 = new CountDownLatch(1);
-        Image img1 = pool.acquire(width, height, type, bufferedImage -> cd1.countDown());
-        BufferedImage bufferedImg1 = getImg(img1);
-        assertEquals(getAllocatedTotalBytes(pool), bucketWidth * bucketHeight * 4);
-        assertEquals(getTooBigForPoolCount(pool), 0);
-
-        // Release the img1.
-        img1 = null;
-        gc();
-        cd1.await(TIMEOUT_SEC, TimeUnit.SECONDS);
-
-        // Dispose pool.
-        pool.dispose();
-        assertEquals(getAllocatedTotalBytes(pool), 0);
-
-        // Request the same sized image as previous.
-        // If the pool was not disposed, this would return the image with bufferedImg1.
-        Image img2 = pool.acquire(width, height, type);
-        BufferedImage bufferedImg2 = getImg(img2);
-        assertEquals(getAllocatedTotalBytes(pool), bucketWidth * bucketHeight * 4);
-        assertEquals(getTooBigForPoolCount(pool), 0);
-
-        // Pool disposed before. No buffered image should be recycled.
-        assertNotEquals(img1, img2);
-        assertNotEquals(bufferedImg1, bufferedImg2);
-    }
-
-    private static BufferedImage createImageAndReturnBufferedImage(ImagePoolImpl pool, int width,
-            int height
-            , int type, CountDownLatch cd) {
-        Image img1 = pool.acquire(width, height, type, bufferedImage -> cd.countDown());
-        return getImg(img1);
-        // At this point img1 should have no reference, causing finalizable to trigger
-    }
-
-    private static ImagePoolImpl getSimpleSingleBucketPool(int width, int height) {
-
-        int bucketWidth = Math.max(width, height);
-        int bucketHeight = Math.max(width, height);
-        return new ImagePoolImpl(new ImagePoolPolicy(
-                new int[]{bucketWidth, bucketHeight},
-                new int[]{1, 1},
-                bucketHeight * bucketWidth * 4 * 3));
-    }
-
-    // Try to force a gc round
-    private static void gc() {
-        System.gc();
-        System.gc();
-        System.gc();
-    }
-
-    private static int getTooBigForPoolCount(ImagePool pool) {
-        return ((ImagePoolStatsProdImpl) ((ImagePoolImpl) pool).mImagePoolStats).mTooBigForPoolCount;
-    }
-
-    private static long getAllocatedTotalBytes(ImagePool pool) {
-        return ((ImagePoolStatsProdImpl) ((ImagePoolImpl) pool).mImagePoolStats).mAllocateTotalBytes;
-    }
-
-    private static BufferedImage getImg(Image image) {
-        return ((ImageImpl) image).mImg;
-    }
-}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java b/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
index 8c69e3e..60db306 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
@@ -17,7 +17,8 @@
 package com.android.layoutlib.bridge;
 
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-import com.android.tools.layoutlib.create.CreateInfo;
+
+import com.android.tools.layoutlib.create.NativeConfig;
 
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -29,7 +30,7 @@
 /**
  * Tests that native delegate classes implement all the required methods.
  *
- * This looks at {@link CreateInfo#DELEGATE_CLASS_NATIVES} to get the list of classes that
+ * This looks at {@link NativeConfig#DELEGATE_CLASS_NATIVES} to get the list of classes that
  * have their native methods reimplemented through a delegate.
  *
  * Since the reimplemented methods are not native anymore, we look for the annotation
@@ -45,7 +46,7 @@
 
     public void testNativeDelegates() {
 
-        final String[] classes = CreateInfo.DELEGATE_CLASS_NATIVES;
+        final String[] classes = NativeConfig.DELEGATE_CLASS_NATIVES;
         mErrors.clear();
         for (String clazz : classes) {
             String targetClassName = clazz.replace('$', '_') + "_Delegate";
@@ -55,7 +56,7 @@
     }
 
     public void testMethodDelegates() {
-        final String[] methods = CreateInfo.DELEGATE_METHODS;
+        final String[] methods = NativeConfig.DELEGATE_METHODS;
         mErrors.clear();
         for (String methodName : methods) {
             // extract the class name
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/android/BitmapTest.java b/bridge/tests/src/com/android/layoutlib/bridge/android/BitmapTest.java
new file mode 100644
index 0000000..67d21e9
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/android/BitmapTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.android;
+
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.intensive.RenderTestBase;
+import com.android.ninepatch.NinePatch;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+
+import static org.junit.Assert.assertNotNull;
+
+public class BitmapTest extends RenderTestBase {
+    @BeforeClass
+    public static void setUp() {
+        Bridge.prepareThread();
+    }
+
+    @Test
+    public void testNinePatchChunk() throws IOException {
+        InputStream compiled =
+                getClass().getResourceAsStream("/com/android/layoutlib/testdata/compiled.9.png");
+        Bitmap compiledBitmap = BitmapFactory.decodeStream(compiled, null, null);
+
+        InputStream nonCompiled = getClass().getResourceAsStream(
+                "/com/android/layoutlib/testdata/non_compiled.9.png");
+        NinePatch ninePatch = NinePatch.load(nonCompiled, true, false);
+
+        Assert.assertArrayEquals(compiledBitmap.getNinePatchChunk(), ninePatch.getChunk().getSerializedChunk());
+    }
+
+    @Test
+    public void testNativeBitmap() {
+        InputStream compiled =
+                getClass().getResourceAsStream("/com/android/layoutlib/testdata/compiled.9.png");
+        Bitmap compiledBitmap = BitmapFactory.decodeStream(compiled, null, null);
+        assertNotNull(compiledBitmap);
+        Buffer buffer = ByteBuffer.allocate(compiledBitmap.getByteCount());
+        compiledBitmap.copyPixelsToBuffer(buffer);
+        buffer.rewind();
+        compiledBitmap.copyPixelsFromBuffer(buffer);
+    }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java b/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java
index d877110..5266c57 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java
@@ -17,13 +17,14 @@
 package com.android.layoutlib.bridge.android;
 
 import com.android.ide.common.rendering.api.SessionParams;
+import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.RenderAction;
 import com.android.layoutlib.bridge.impl.RenderActionTestUtil;
 import com.android.layoutlib.bridge.intensive.RenderTestBase;
-import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
 import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
 import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
 
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 import android.R.attr;
@@ -34,9 +35,15 @@
 import android.util.DisplayMetrics;
 import android.view.ContextThemeWrapper;
 
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 public class BridgeContextTest extends RenderTestBase {
+    @BeforeClass
+    public static void setUp() {
+        Bridge.prepareThread();
+    }
+
     @Test
     public void basic() throws ClassNotFoundException {
         // Setup
@@ -56,9 +63,9 @@
         Configuration configuration = RenderAction.getConfiguration(params);
         BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
                 params.getAssets(), params.getLayoutlibCallback(), configuration,
-                params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
+                params.getTargetSdkVersion(), params.isRtlSupported());
 
-        context.initResources();
+        context.initResources(params.getAssets());
         BridgeContext oldContext = RenderActionTestUtil.setBridgeContext(context);
         try {
             Context themeContext = new ContextThemeWrapper(context, style.Theme_Material);
@@ -103,9 +110,9 @@
         Configuration configuration = RenderAction.getConfiguration(params);
         BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
                 params.getAssets(), params.getLayoutlibCallback(), configuration,
-                params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
+                params.getTargetSdkVersion(), params.isRtlSupported());
 
-        context.initResources();
+        context.initResources(params.getAssets());
         BridgeContext oldContext = RenderActionTestUtil.setBridgeContext(context);
         try {
             Context themeContext = new ContextThemeWrapper(context, style.Theme_Material);
@@ -119,7 +126,26 @@
             RenderActionTestUtil.setBridgeContext(oldContext);
             context.disposeResources();
         }
+    }
 
+    @Test
+    public void noExceptionForCustomService() throws ClassNotFoundException {
+        LayoutPullParser parser = LayoutPullParser.createFromPath("/empty.xml");
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material", false)
+                .build();
+        DisplayMetrics metrics = new DisplayMetrics();
+        Configuration configuration = RenderAction.getConfiguration(params);
+        BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
+                params.getAssets(), params.getLayoutlibCallback(), configuration,
+                params.getTargetSdkVersion(), params.isRtlSupported());
 
+        assertNull(context.getSystemService("my_custom_service"));
+        sRenderMessages.removeIf(message -> message.equals("Service my_custom_service was not found or is unsupported"));
     }
 }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java b/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
index 1a66c94..f8fd565 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
@@ -58,27 +58,18 @@
         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());
     }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/impl/ResourceHelperTest.java b/bridge/tests/src/com/android/layoutlib/bridge/impl/ResourceHelperTest.java
index 4e18f07..aedd845 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/impl/ResourceHelperTest.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/impl/ResourceHelperTest.java
@@ -18,6 +18,21 @@
 
 import org.junit.Test;
 
+import android.content.res.StringBlock;
+import android.text.SpannedString;
+import android.text.TextUtils.TruncateAt;
+import android.text.style.AbsoluteSizeSpan;
+import android.text.style.BulletSpan;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.StrikethroughSpan;
+import android.text.style.StyleSpan;
+import android.text.style.SubscriptSpan;
+import android.text.style.SuperscriptSpan;
+import android.text.style.TypefaceSpan;
+import android.text.style.URLSpan;
+import android.text.style.UnderlineSpan;
+
 import static org.junit.Assert.*;
 
 public class ResourceHelperTest {
@@ -48,4 +63,40 @@
         assertEquals(0x12aabbcc, ResourceHelper.getColor("#12AABBCC"));
         assertEquals(0x12345, ResourceHelper.getColor("#12345"));
     }
+
+    @Test
+    public void testParseHtml() {
+        CharSequence parsed = ResourceHelper.parseHtml("Text <b>bold</b> " +
+                "<i>italic</i> <u>underline</u> <tt>monospace</tt> " +
+                "<big>big</big> <small>small</small> " +
+                "<sup>superscript</sup> <sub>subscript</sub> <strike>strike</strike> " +
+                "<li>bullet</li> <marquee>marquee</marquee> " +
+                "<a href=\"http://link.com\">link</a> " +
+                "<font face=\"serif\" color=\"#ff0000\" height=\"20\" size=\"8\">font</font> " +
+                "<em>fake italic</em> <del>fake strike</del> " +
+                "End text");
+        Class<?>[] classes = {StyleSpan.class, StyleSpan.class, UnderlineSpan.class,
+                TypefaceSpan.class, RelativeSizeSpan.class, RelativeSizeSpan.class,
+                SuperscriptSpan.class, SubscriptSpan.class, StrikethroughSpan.class,
+                BulletSpan.class, TruncateAt.class, URLSpan.class, StringBlock.Height.class,
+                AbsoluteSizeSpan.class, ForegroundColorSpan.class, TypefaceSpan.class};
+        int[] starts = {5, 10, 17, 27, 37, 41, 47, 59, 69, 0, 83, 91, 0, 96, 96, 96};
+        int[] ends = {9, 16, 26, 36, 40, 46, 58, 68, 75, 133, 90, 95, 133, 100, 100, 100};
+        SpannedString spanned = (SpannedString)parsed;
+        assertEquals("Text bold " +
+                "italic underline monospace " +
+                "big small " +
+                "superscript subscript strike " +
+                "bullet marquee " +
+                "link " +
+                "font " +
+                "fake italic fake strike " +
+                "End text", spanned.toString());
+        Object[] spans = spanned.getSpans(0, spanned.length(), Object.class);
+        for (int i =0; i < spans.length; i++) {
+            assertEquals(classes[i], spans[i].getClass());
+            assertEquals(starts[i], spanned.getSpanStart(spans[i]));
+            assertEquals(ends[i], spanned.getSpanEnd(spans[i]));
+        }
+    }
 }
\ No newline at end of file
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index be57b88..2c69f3a 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -18,12 +18,14 @@
 
 import com.android.layoutlib.bridge.BridgeRenderSessionTest;
 import com.android.layoutlib.bridge.TestDelegates;
+import com.android.layoutlib.bridge.android.BitmapTest;
 import com.android.layoutlib.bridge.android.BridgeContextTest;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParserTest;
 import com.android.layoutlib.bridge.impl.LayoutParserWrapperTest;
 import com.android.layoutlib.bridge.impl.ResourceHelperTest;
 import com.android.tools.idea.validator.LayoutValidatorTests;
-import com.android.tools.idea.validator.accessibility.AccessibilityValidatorTests;
+import com.android.tools.idea.validator.ValidatorResultTests;
+import com.android.tools.idea.validator.AccessibilityValidatorTests;
 
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
@@ -31,11 +33,7 @@
 
 import android.content.res.BridgeTypedArrayTest;
 import android.content.res.Resources_DelegateTest;
-import android.graphics.Color_DelegateTest;
-import android.graphics.Matrix_DelegateTest;
 import android.util.BridgeXmlPullAttributesTest;
-import android.util.imagepool.ImagePoolHelperTest;
-import android.util.imagepool.ImagePoolImplTest;
 
 /**
  * Suite used by the layoutlib build system
@@ -44,11 +42,10 @@
 @SuiteClasses({
         RenderTests.class, LayoutParserWrapperTest.class,
         BridgeXmlBlockParserTest.class, BridgeXmlPullAttributesTest.class,
-        Matrix_DelegateTest.class, TestDelegates.class,
-        BridgeRenderSessionTest.class, ResourceHelperTest.class, BridgeContextTest.class,
-        Resources_DelegateTest.class, Color_DelegateTest.class, ImagePoolHelperTest.class,
-        ImagePoolImplTest.class, HighQualityShadowsRenderTests.class,
-        LayoutValidatorTests.class, AccessibilityValidatorTests.class, BridgeTypedArrayTest.class
+        TestDelegates.class, BridgeRenderSessionTest.class, ResourceHelperTest.class,
+        BridgeContextTest.class, Resources_DelegateTest.class, ShadowsRenderTests.class,
+        LayoutValidatorTests.class, AccessibilityValidatorTests.class, BridgeTypedArrayTest.class,
+        ValidatorResultTests.class, BitmapTest.class
 })
 public class Main {
 }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
index 67bb7af..7054f2b 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
@@ -25,9 +25,9 @@
 import com.android.ide.common.resources.deprecated.ResourceItem;
 import com.android.ide.common.resources.deprecated.ResourceRepository;
 import com.android.ide.common.resources.deprecated.TestFolderWrapper;
+import com.android.internal.lang.System_Delegate;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.RenderParamsFlags;
-import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
 import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
 import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
@@ -35,8 +35,6 @@
 import com.android.layoutlib.bridge.intensive.util.ModuleClassLoader;
 import com.android.layoutlib.bridge.intensive.util.SessionParamsBuilder;
 import com.android.layoutlib.bridge.intensive.util.TestAssetRepository;
-import com.android.layoutlib.bridge.intensive.util.TestUtils;
-import com.android.tools.layoutlib.java.System_Delegate;
 import com.android.utils.ILogger;
 
 import org.junit.AfterClass;
@@ -48,6 +46,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.view.Choreographer;
 
 import java.awt.image.BufferedImage;
 import java.io.File;
@@ -56,10 +55,10 @@
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Locale;
 import java.util.concurrent.TimeUnit;
 
 import com.google.android.collect.Lists;
-import com.google.common.collect.ImmutableMap;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
@@ -99,9 +98,16 @@
          */
         void beforeDisposed(RenderSession session);
     }
+
+    private static final String NATIVE_LIB_PATH_PROPERTY = "native.lib.path";
+    private static final String FONT_DIR_PROPERTY = "font.dir";
+    private static final String ICU_DATA_PATH_PROPERTY = "icu.data.path";
     private static final String PLATFORM_DIR_PROPERTY = "platform.dir";
     private static final String RESOURCE_DIR_PROPERTY = "test_res.dir";
 
+    private static final String NATIVE_LIB_DIR_PATH;
+    private static final String FONT_DIR;
+    private static final String ICU_DATA_PATH;
     protected static final String PLATFORM_DIR;
     private static final String TEST_RES_DIR;
     /** Location of the app to test inside {@link #TEST_RES_DIR} */
@@ -128,6 +134,10 @@
                     PLATFORM_DIR_PROPERTY, System.getProperty(PLATFORM_DIR_PROPERTY)));
         }
 
+        NATIVE_LIB_DIR_PATH = getNativeLibDirPath();
+        FONT_DIR = getFontDir();
+        ICU_DATA_PATH = getIcuDataPath();
+
         TEST_RES_DIR = getTestResDir();
         if (TEST_RES_DIR == null) {
             fail(String.format("System property %1$s.dir not properly set. The value is %2$s",
@@ -147,8 +157,56 @@
         }
     };
 
+    @Rule
+    public TestWatcher sMemoryLeakChecker = new TestWatcher() {
+        @Override
+        protected void succeeded(Description description) {
+            for (int i = Choreographer.CALLBACK_INPUT; i <= Choreographer.CALLBACK_COMMIT; ++i) {
+                if (Choreographer.getInstance().mCallbackQueues[i].mHead != null) {
+                    fail("Memory leak: leftover frame callbacks are detected in Choreographer");
+                }
+            }
+        }
+    };
+
     protected ClassLoader mDefaultClassLoader;
 
+    private static String getNativeLibDirPath() {
+        String nativeLibDirPath = System.getProperty(NATIVE_LIB_PATH_PROPERTY);
+        if (nativeLibDirPath != null) {
+            File nativeLibDir = new File(nativeLibDirPath);
+            if (nativeLibDir.isDirectory()) {
+                nativeLibDirPath = nativeLibDir.getAbsolutePath();
+            } else {
+                nativeLibDirPath = null;
+            }
+        }
+        if (nativeLibDirPath == null) {
+            nativeLibDirPath = PLATFORM_DIR + "/../../../../../lib64/";
+        }
+        return nativeLibDirPath;
+    }
+
+    private static String getFontDir() {
+        String fontDir = System.getProperty(FONT_DIR_PROPERTY);
+        if (fontDir == null) {
+            // The fonts are built into out/host/common/obj/PACKAGING/fonts_intermediates
+            // as specified in build/make/core/layoutlib_fonts.mk, and PLATFORM_DIR is
+            // out/host/[arch]/sdk/sdk*/android-sdk*/platforms/android*
+            fontDir = PLATFORM_DIR +
+                    "/../../../../../../common/obj/PACKAGING/fonts_intermediates";
+        }
+        return fontDir;
+    }
+
+    private static String getIcuDataPath() {
+        String icuDataPath = System.getProperty(ICU_DATA_PATH_PROPERTY);
+        if (icuDataPath == null) {
+            icuDataPath = PLATFORM_DIR + "/../../../../../com.android.i18n/etc/icu/icudt70l.dat";
+        }
+        return icuDataPath;
+    }
+
     private static String getPlatformDir() {
         String platformDir = System.getProperty(PLATFORM_DIR_PROPERTY);
         if (platformDir != null && !platformDir.isEmpty() && new File(platformDir).isDirectory()) {
@@ -318,15 +376,12 @@
                 };
         sProjectResources.loadResources();
 
-        // The fonts are built into out/host/common/obj/PACKAGING/sdk-fonts_intermediates as specified in
-        // build/make/core/sdk_font.mk, and PLATFORM_DIR is out/host/[arch]/sdk/sdk*/android-sdk*/platforms/android*
-        File fontLocation = new File(PLATFORM_DIR,
-                "../../../../../../common/obj/PACKAGING/sdk-fonts_intermediates");
+        File fontLocation = new File(FONT_DIR);
         File buildProp = new File(PLATFORM_DIR, "build.prop");
         File attrs = new File(res, "values" + File.separator + "attrs.xml");
         sBridge = new Bridge();
-        sBridge.init(ConfigGenerator.loadProperties(buildProp), fontLocation, null, null,
-                ConfigGenerator.getEnumMap(attrs), getLayoutLog());
+        sBridge.init(ConfigGenerator.loadProperties(buildProp), fontLocation, NATIVE_LIB_DIR_PATH,
+                ICU_DATA_PATH, ConfigGenerator.getEnumMap(attrs), getLayoutLog());
         Bridge.getLock().lock();
         try {
             Bridge.setLog(getLayoutLog());
@@ -342,11 +397,6 @@
         sProjectResources = null;
         sLogger = null;
         sBridge = null;
-
-        TestUtils.gc();
-
-        System.out.println("Objects still linked from the DelegateManager:");
-        DelegateManager.dump(System.out);
     }
 
     @NonNull
@@ -399,7 +449,14 @@
      */
     protected static void verify(@NonNull String goldenImageName, @NonNull BufferedImage image) {
         try {
-            String goldenImagePath = APP_TEST_DIR + "/golden/" + goldenImageName;
+            boolean isMac = System.getProperty("os.name").toLowerCase(Locale.US).contains("mac");
+            String goldenImagePath = APP_TEST_DIR;
+            if (isMac) {
+                goldenImagePath += "/golden-mac/";
+            } else {
+                goldenImagePath += "/golden/";
+            }
+            goldenImagePath += goldenImageName;
             ImageUtils.requireSimilar(goldenImagePath, image);
         } catch (IOException e) {
             getLogger().error(e, e.getMessage());
@@ -430,14 +487,15 @@
     @Nullable
     protected static RenderResult renderAndVerify(SessionParams params, String goldenFileName)
             throws ClassNotFoundException {
-        return RenderTestBase.renderAndVerify(params, goldenFileName, -1);
+        return RenderTestBase.renderAndVerify(params, goldenFileName, TimeUnit.SECONDS.toNanos(2));
     }
 
     protected static ILayoutLog getLayoutLog() {
         if (sLayoutLibLog == null) {
             sLayoutLibLog = new ILayoutLog() {
                 @Override
-                public void warning(String tag, String message, Object cookie, Object data) {
+                public void warning(@Nullable String tag, @NonNull String message, @Nullable Object viewCookie,
+                        @Nullable Object data) {
                     System.out.println("Warning " + tag + ": " + message);
                     failWithMsg(message);
                 }
@@ -454,20 +512,26 @@
                 }
 
                 @Override
-                public void error(String tag, String message, Object cookie, Object data) {
+                public void error(@Nullable String tag, @NonNull String message, @Nullable Object viewCookie,
+                        @Nullable Object data) {
                     System.out.println("Error " + tag + ": " + message);
                     failWithMsg(message);
                 }
 
                 @Override
-                public void error(String tag, String message, Throwable throwable, Object cookie,
-                        Object data) {
+                public void error(@Nullable String tag, @NonNull String message, @Nullable Throwable throwable,
+                        @Nullable Object viewCookie, @Nullable Object data) {
                     System.out.println("Error " + tag + ": " + message);
                     if (throwable != null) {
                         throwable.printStackTrace();
                     }
                     failWithMsg(message);
                 }
+
+                @Override
+                public void logAndroidFramework(int priority, String tag, String message) {
+                    System.out.println("Android framework message " + tag + ": " + message);
+                }
             };
         }
         return sLayoutLibLog;
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
index 303c247..91c79c9 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
@@ -20,11 +20,13 @@
 import com.android.ide.common.rendering.api.ResourceNamespace;
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValueImpl;
+import com.android.ide.common.rendering.api.Result;
 import com.android.ide.common.rendering.api.SessionParams;
 import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
 import com.android.ide.common.rendering.api.ViewInfo;
 import com.android.ide.common.rendering.api.XmlParserFactory;
 import com.android.internal.R;
+import com.android.internal.lang.System_Delegate;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.RenderParamsFlags;
 import com.android.layoutlib.bridge.impl.ParserFactory;
@@ -39,6 +41,7 @@
 import com.android.resources.ResourceType;
 
 import org.junit.After;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.kxml2.io.KXmlParser;
 import org.xmlpull.v1.XmlPullParser;
@@ -55,6 +58,8 @@
 import android.util.DisplayMetrics;
 import android.util.StateSet;
 import android.util.TypedValue;
+import android.widget.Button;
+import android.widget.LinearLayout;
 
 import java.awt.BasicStroke;
 import java.awt.Graphics2D;
@@ -161,22 +166,14 @@
                 .setParser(parser)
                 .setConfigGenerator(ConfigGenerator.NEXUS_5)
                 .setCallback(layoutLibCallback)
-                .disableShadows()
                 .build();
 
         renderAndVerify(params, "allwidgets.png");
-
-        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
-        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
     }
 
     @Test
     public void testArrayCheck() throws ClassNotFoundException, FileNotFoundException {
         renderAndVerify("array_check.xml", "array_check.png", false);
-
-        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
-        sRenderMessages.removeIf(
-                message -> message.equals("Font$Builder.nAddAxis is not supported."));
     }
 
     @Test
@@ -189,13 +186,8 @@
                 .setParser(parser)
                 .setConfigGenerator(ConfigGenerator.NEXUS_7_2012)
                 .setCallback(layoutLibCallback)
-                .disableShadows()
                 .build();
         renderAndVerify(params, "allwidgets_tab.png");
-
-        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
-        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
-        sRenderMessages.removeIf(message -> message.equals("Font$Builder.nAddAxis is not supported."));
     }
 
     @Test
@@ -545,9 +537,39 @@
     }
 
     /**
+     * Test a vector drawable which is transparent.
+     */
+    @Test
+    public void testTransparentDrawable() throws ClassNotFoundException {
+        // Create the layout pull parser.
+        LayoutPullParser parser = LayoutPullParser.createFromString(
+                "<ImageView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "    android:layout_height=\"fill_parent\"\n" +
+                        "    android:layout_width=\"fill_parent\"\n" +
+                        "    android:src=\"@drawable/transparent_drawable\" />");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .disableDecoration()
+                .setTransparentBackground()
+                .build();
+
+        renderAndVerify(params, "transparent_drawable.png",
+                TimeUnit.SECONDS.toNanos(2));
+    }
+
+    /**
      * Test a vector drawable that uses trimStart and trimEnd. It also tests all the primitives
      * for vector drawables (lines, moves and cubic and quadratic curves).
      */
+    @Ignore("This test does not make sense in layoutlib anymore, test in Studio")
     @Test
     public void testVectorDrawableHasMultipleLineInPathData() throws ClassNotFoundException {
         // Create the layout pull parser.
@@ -764,6 +786,7 @@
         assertEquals(90, rootLayout.getChildren().get(5).getChildren().get(0).getLeft());
         assertEquals(-270, rootLayout.getChildren().get(5).getChildren().get(0).getBottom());
         assertEquals(690, rootLayout.getChildren().get(5).getChildren().get(0).getRight());
+        session.dispose();
 
         // Do a full render pass
         parser = createParserFromPath("scrolled.xml");
@@ -803,7 +826,7 @@
         Configuration configuration = RenderAction.getConfiguration(params);
         BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
                 params.getAssets(), params.getLayoutlibCallback(), configuration,
-                params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
+                params.getTargetSdkVersion(), params.isRtlSupported());
         Resources resources = Resources_Delegate.initSystem(context, assetManager, metrics,
                 configuration, params.getLayoutlibCallback());
         // Test
@@ -845,7 +868,7 @@
         Configuration configuration = RenderAction.getConfiguration(params);
         BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
                 params.getAssets(), params.getLayoutlibCallback(), configuration,
-                params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
+                params.getTargetSdkVersion(), params.isRtlSupported());
         Resources resources = Resources_Delegate.initSystem(context, assetManager, metrics,
                 configuration, params.getLayoutlibCallback());
 
@@ -870,8 +893,6 @@
     public void testFonts() throws ClassNotFoundException, FileNotFoundException {
         // TODO: styles seem to be broken in TextView
         renderAndVerify("fonts_test.xml", "font_test.png", false);
-        sRenderMessages.removeIf(
-                message -> message.equals("Font$Builder.nAddAxis is not supported."));
     }
 
     @Test
@@ -958,7 +979,7 @@
         BridgeContext mContext =
                 new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
                         params.getAssets(), params.getLayoutlibCallback(), configuration,
-                        params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
+                        params.getTargetSdkVersion(), params.isRtlSupported());
 
         TypedValue outValue = new TypedValue();
         mContext.resolveThemeAttribute(android.R.attr.colorPrimary, outValue, true);
@@ -1036,8 +1057,8 @@
         BridgeContext mContext =
                 new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
                         params.getAssets(), params.getLayoutlibCallback(), configuration,
-                        params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
-        mContext.initResources();
+                        params.getTargetSdkVersion(), params.isRtlSupported());
+        mContext.initResources(params.getAssets());
         BridgeContext oldContext = RenderActionTestUtil.setBridgeContext(mContext);
 
         try {
@@ -1088,44 +1109,6 @@
     }
 
     @Test
-    public void testShadowFlagsNoShadows() throws Exception {
-        LayoutPullParser parser = createParserFromPath("shadows_test.xml");
-        LayoutLibTestCallback layoutLibCallback =
-                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
-        layoutLibCallback.initResources();
-        SessionParams params = getSessionParamsBuilder()
-                .setParser(parser)
-                .setConfigGenerator(ConfigGenerator.NEXUS_5)
-                .setCallback(layoutLibCallback)
-                .disableDecoration()
-                .disableShadows()
-                .build();
-
-        renderAndVerify(params, "shadows_test_no_shadow.png");
-        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
-        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
-    }
-
-    @Test
-    public void testRectangleShadow() throws Exception {
-        LayoutPullParser parser = createParserFromPath("shadows_test.xml");
-        LayoutLibTestCallback layoutLibCallback =
-                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
-        layoutLibCallback.initResources();
-        SessionParams params = getSessionParamsBuilder()
-                .setParser(parser)
-                .setConfigGenerator(ConfigGenerator.NEXUS_5)
-                .setCallback(layoutLibCallback)
-                .disableDecoration()
-                .disableHighQualityShadows()
-                .build();
-
-        renderAndVerify(params, "shadows_test.png");
-        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
-        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
-    }
-
-    @Test
     public void testResourcesGetIdentifier() throws Exception {
         // Setup
         // Create the layout pull parser for our resources (empty.xml can not be part of the test
@@ -1145,7 +1128,7 @@
         Configuration configuration = RenderAction.getConfiguration(params);
         BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
                 params.getAssets(), params.getLayoutlibCallback(), configuration,
-                params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
+                params.getTargetSdkVersion(), params.isRtlSupported());
         Resources resources = Resources_Delegate.initSystem(context, assetManager, metrics,
                 configuration, params.getLayoutlibCallback());
         Integer id =
@@ -1186,7 +1169,12 @@
      */
     @Test
     public void test9PatchNoDPIBackground() throws Exception {
-        String layout =
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        String layoutCompiled =
                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
                         "    android:layout_width=\"match_parent\"\n" +
                         "    android:layout_height=\"match_parent\"\n" +
@@ -1203,12 +1191,7 @@
                         "        android:text=\"Button\" />\n"
                         + "</LinearLayout>";
 
-        LayoutPullParser parser = LayoutPullParser.createFromString(layout);
-        // Create LayoutLibCallback.
-        LayoutLibTestCallback layoutLibCallback =
-                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
-        layoutLibCallback.initResources();
-
+        LayoutPullParser parser = LayoutPullParser.createFromString(layoutCompiled);
         SessionParams params = getSessionParamsBuilder()
                 .setParser(parser)
                 .setCallback(layoutLibCallback)
@@ -1217,6 +1200,33 @@
                 .build();
 
         renderAndVerify(params, "ninepatch_background.png");
+
+        String layoutNonCompiled =
+                "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "    android:layout_width=\"match_parent\"\n" +
+                        "    android:layout_height=\"match_parent\"\n" +
+                        "    android:background=\"@drawable/uncompiled_ninepatch\"\n" +
+                        "    android:layout_margin=\"20dp\"\n" +
+                        "    android:orientation=\"vertical\">\n\n" +
+                        "    <Button\n" +
+                        "        android:layout_width=\"wrap_content\"\n" +
+                        "        android:layout_height=\"wrap_content\"\n" +
+                        "        android:text=\"Button\" />\n\n" +
+                        "    <Button\n" +
+                        "        android:layout_width=\"wrap_content\"\n" +
+                        "        android:layout_height=\"wrap_content\"\n" +
+                        "        android:text=\"Button\" />\n"
+                        + "</LinearLayout>";
+
+        parser = LayoutPullParser.createFromString(layoutNonCompiled);
+        params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .build();
+
+        renderAndVerify(params, "ninepatch_background.png");
     }
 
     @Test
@@ -1547,7 +1557,6 @@
                         new BufferedImage(width / 10, height / 10,
                         BufferedImage.TYPE_INT_ARGB))
                 .setFlag(RenderParamsFlags.FLAG_KEY_RESULT_IMAGE_AUTO_SCALE, true)
-                .disableShadows()
                 .build();
 
         renderAndVerify(params, "auto-scale-image.png");
@@ -1638,6 +1647,12 @@
     }
 
     @Test
+    public void testJustified() throws ClassNotFoundException, FileNotFoundException {
+        renderAndVerify("justified_inter_word.xml", "justified_inter_word.png", false);
+        renderAndVerify("justified_none.xml", "justified_none.png", false);
+    }
+
+    @Test
     public void testManyLineBreaks() throws Exception {
         String layout =
                 "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
@@ -1681,20 +1696,30 @@
     }
 
     @Test
-    public void testHighQualityShadowWidgetWithScroll() throws Exception {
-        LayoutPullParser parser = createParserFromPath("shadows_scrollview.xml");
+    public void testNinePatchDrawable() throws Exception {
+        String layout =
+                "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "              android:padding=\"16dp\"\n" +
+                        "              android:orientation=\"horizontal\"\n" +
+                        "              android:layout_width=\"fill_parent\"\n" +
+                        "              android:layout_height=\"fill_parent\">\n" +
+                        "    <ImageView\n" +
+                        "             android:layout_height=\"fill_parent\"\n" +
+                        "             android:layout_width=\"fill_parent\"\n" +
+                        "             android:src=\"@drawable/ninepatch_drawable\" />\n" +
+                        "</LinearLayout>\n";
+        LayoutPullParser parser = LayoutPullParser.createFromString(layout);
+        // Create LayoutLibCallback.
         LayoutLibTestCallback layoutLibCallback =
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
         SessionParams params = getSessionParamsBuilder()
                 .setParser(parser)
                 .setCallback(layoutLibCallback)
+                .disableDecoration()
                 .build();
 
-        renderAndVerify(params, "shadow_scrollview_test_high_quality.png");
-        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
-        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
-        sRenderMessages.removeIf(message -> message.equals("Font$Builder.nAddAxis is not supported."));
+        renderAndVerify(params, "ninepatch_drawable.png");
     }
 
     @Test
@@ -1776,4 +1801,204 @@
 
         renderAndVerify(params, "textclock.png");
     }
+
+    @Test
+    public void testChangeSize() throws ClassNotFoundException {
+        final String layout =
+                "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "              android:orientation=\"vertical\"\n" +
+                        "              android:layout_width=\"wrap_content\"\n" +
+                        "              android:layout_height=\"wrap_content\">\n" +
+                        "    <Button\n" +
+                        "             android:layout_height=\"50dp\"\n" +
+                        "             android:layout_width=\"100dp\"\n" +
+                        "             android:text=\"Hello\" />\n" +
+                        "</LinearLayout>\n";
+
+        // Create the layout pull parser.
+        LayoutPullParser parser = LayoutPullParser.createFromString(layout);
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setRenderingMode(RenderingMode.SHRINK)
+                .disableDecoration()
+                .build();
+
+        System_Delegate.setBootTimeNanos(TimeUnit.MILLISECONDS.toNanos(871732800000L));
+        System_Delegate.setNanosTime(TimeUnit.MILLISECONDS.toNanos(871732800000L));
+        RenderSession session = sBridge.createSession(params);
+
+        try {
+            session.setElapsedFrameTimeNanos(TimeUnit.SECONDS.toNanos(2));
+
+            if (!session.getResult().isSuccess()) {
+                getLogger().error(session.getResult().getException(),
+                        session.getResult().getErrorMessage());
+            }
+            else {
+                // Render the session with a timeout of 50s.
+                Result renderResult = session.render(50000);
+                if (!renderResult.isSuccess()) {
+                    getLogger().error(session.getResult().getException(),
+                            session.getResult().getErrorMessage());
+                }
+            }
+
+            BufferedImage resultImage = session.getImage();
+
+            assertNotNull(resultImage);
+            verify("button_resize.png", resultImage);
+
+            Object viewObject = session.getRootViews().get(0)
+                    .getChildren().get(0).getViewObject();
+
+            Button btn = (Button) viewObject;
+            btn.setLayoutParams(new LinearLayout.LayoutParams(300, 300));
+
+            Result renderResult = session.render(50000);
+            if (!renderResult.isSuccess()) {
+                getLogger().error(session.getResult().getException(),
+                        session.getResult().getErrorMessage());
+            }
+
+            resultImage = session.getImage();
+
+            assertNotNull(resultImage);
+            verify("button_resize2.png", resultImage);
+        } finally {
+            session.dispose();
+        }
+    }
+
+    /**
+     * Tests that theme attributes are not resolved when using Resources_Delegate.obtainAttributes
+     * <p/>
+     * http://b/175943371
+     */
+    @Test
+    public void testNonStyledResources() throws ClassNotFoundException {
+        // Create the layout pull parser.
+        LayoutPullParser parser = LayoutPullParser.createFromString(
+                "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "              android:padding=\"16dp\"\n" +
+                        "              android:background=\"#999\"" +
+                        "              android:orientation=\"horizontal\"\n" +
+                        "              android:layout_width=\"match_parent\"\n" +
+                        "              android:layout_height=\"match_parent\">\n" +
+                        "    <com.android.layoutlib.bridge.test.widgets.CustomImageView\n" +
+                        "        android:layout_width=\"100dp\"\n" +
+                        "        android:layout_height=\"100dp\"/>\n" +
+                        "</LinearLayout>");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .disableDecoration()
+                .build();
+
+        renderAndVerify(params, "non-styled_resources.png",
+                TimeUnit.SECONDS.toNanos(2));
+    }
+
+    @Test
+    public void testRenderEffect() throws ClassNotFoundException {
+        // Create the layout pull parser.
+        LayoutPullParser parser = LayoutPullParser.createFromString(
+                "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "              android:padding=\"16dp\"\n" +
+                        "              android:background=\"#999\"" +
+                        "              android:orientation=\"horizontal\"\n" +
+                        "              android:layout_width=\"match_parent\"\n" +
+                        "              android:layout_height=\"match_parent\">\n" +
+                        "    <com.android.layoutlib.bridge.test.widgets.BlurryImageView\n" +
+                        "        android:layout_width=\"100dp\"\n" +
+                        "        android:layout_height=\"100dp\"/>\n" +
+                        "</LinearLayout>");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .disableDecoration()
+                .build();
+
+        renderAndVerify(params, "render_effect.png",
+                TimeUnit.SECONDS.toNanos(2));
+    }
+
+    @Test
+    public void testDialog() throws Exception {
+        String layout =
+                "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "              android:padding=\"16dp\"\n" +
+                        "              android:orientation=\"horizontal\"\n" +
+                        "              android:layout_width=\"fill_parent\"\n" +
+                        "              android:layout_height=\"fill_parent\">\n" +
+                        "    <com.android.layoutlib.test.myapplication.widgets.DialogView\n" +
+                        "             android:layout_height=\"wrap_content\"\n" +
+                        "             android:layout_width=\"wrap_content\" />\n" +
+                        "</LinearLayout>\n";
+        LayoutPullParser parser = LayoutPullParser.createFromString(layout);
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .disableDecoration()
+                .build();
+
+        renderAndVerify(params, "dialog.png",
+                TimeUnit.SECONDS.toNanos(2));
+    }
+
+    @Test
+    public void testWindowBackgroundWithThemeAttribute() throws Exception {
+        String layout =
+                "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "              android:padding=\"16dp\"\n" +
+                        "              android:orientation=\"horizontal\"\n" +
+                        "              android:layout_width=\"fill_parent\"\n" +
+                        "              android:layout_height=\"fill_parent\">\n" +
+                        "    <TextView\n" +
+                        "             android:layout_height=\"wrap_content\"\n" +
+                        "             android:layout_width=\"wrap_content\"\n" +
+                        "             android:text=\"Hello World!\" />\n" +
+                        "</LinearLayout>\n";
+        LayoutPullParser parser = LayoutPullParser.createFromString(layout);
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("WindowBackgroundTheme", true)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .disableDecoration()
+                .build();
+
+        renderAndVerify(params, "window_background.png",
+                TimeUnit.SECONDS.toNanos(2));
+    }
 }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/HighQualityShadowsRenderTests.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/ShadowsRenderTests.java
similarity index 69%
rename from bridge/tests/src/com/android/layoutlib/bridge/intensive/HighQualityShadowsRenderTests.java
rename to bridge/tests/src/com/android/layoutlib/bridge/intensive/ShadowsRenderTests.java
index 3582860..5c997dc 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/HighQualityShadowsRenderTests.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/ShadowsRenderTests.java
@@ -24,14 +24,14 @@
 import org.junit.After;
 import org.junit.Test;
 
-public class HighQualityShadowsRenderTests extends RenderTestBase {
+public class ShadowsRenderTests extends RenderTestBase {
     @After
     public void afterTestCase() {
         com.android.layoutlib.bridge.test.widgets.HookWidget.reset();
     }
 
     @Test
-    public void testHighQualityRectangleShadow() throws Exception {
+    public void testRectangleShadow() throws Exception {
         LayoutPullParser parser = createParserFromPath("shadows_test.xml");
         LayoutLibTestCallback layoutLibCallback =
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
@@ -43,10 +43,7 @@
                 .disableDecoration()
                 .build();
 
-        renderAndVerify(params, "shadows_test_high_quality.png");
-        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
-        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
-        sRenderMessages.removeIf(message -> message.equals("Font$Builder.nAddAxis is not supported."));
+        renderAndVerify(params, "shadows_test.png");
     }
 
     @Test
@@ -61,10 +58,7 @@
                 .setCallback(layoutLibCallback)
                 .build();
 
-        renderAndVerify(params, "shadows_test_high_quality_rounded_edge.png");
-        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
-        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
-        sRenderMessages.removeIf(message -> message.equals("Font$Builder.nAddAxis is not supported."));
+        renderAndVerify(params, "shadows_test_rounded_edge.png");
     }
 
     @Test
@@ -80,9 +74,7 @@
                 .disableDecoration()
                 .build();
 
-        renderAndVerify(params, "large_shadows_test_high_quality.png");
-        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
-        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
+        renderAndVerify(params, "large_shadows_test.png");
     }
 
     @Test
@@ -97,9 +89,7 @@
                 .setCallback(layoutLibCallback)
                 .build();
 
-        renderAndVerify(params, "shadow_sizes_test_high_quality.png");
-        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
-        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
+        renderAndVerify(params, "shadow_sizes_test.png");
     }
 
     @Test
@@ -113,9 +103,6 @@
                 .setCallback(layoutLibCallback)
                 .build();
 
-        renderAndVerify(params, "shadow_scrollview_test_high_quality.png");
-        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
-        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
-        sRenderMessages.removeIf(message -> message.equals("Font$Builder.nAddAxis is not supported."));
+        renderAndVerify(params, "shadow_scrollview_test.png");
     }
 }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
index 9faa0a6..8dd7ec7 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
@@ -16,7 +16,6 @@
 
 package com.android.layoutlib.bridge.intensive.setup;
 
-import com.android.SdkConstants;
 import com.android.ide.common.rendering.api.ActionBarCallback;
 import com.android.ide.common.rendering.api.AdapterBinding;
 import com.android.ide.common.rendering.api.ILayoutPullParser;
@@ -60,8 +59,6 @@
     private final ActionBarCallback mActionBarCallback = new ActionBarCallback();
     private final ClassLoader mModuleClassLoader;
     private String mAdaptiveIconMaskPath;
-    private boolean mSetUseShadow = true;
-    private boolean mHighShadowQuality = true;
 
     public LayoutLibTestCallback(ILogger logger, ClassLoader classLoader) {
         mLog = logger;
@@ -188,6 +185,11 @@
     }
 
     @Override
+    public String getResourcePackage() {
+        return PACKAGE_NAME;
+    }
+
+    @Override
     public Class<?> findClass(String name) throws ClassNotFoundException {
         return mModuleClassLoader.loadClass(name);
     }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java
index b1801c6..a8adf95 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java
@@ -18,10 +18,8 @@
 
 import android.annotation.NonNull;
 
-import java.awt.AlphaComposite;
 import java.awt.Color;
 import java.awt.Graphics;
-import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.IOException;
@@ -29,7 +27,6 @@
 
 import javax.imageio.ImageIO;
 
-import static java.awt.RenderingHints.*;
 import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
 import static java.io.File.separatorChar;
 import static org.junit.Assert.assertEquals;
@@ -47,28 +44,22 @@
  */
 public class ImageUtils {
     /**
-     * Normally, this test will fail when there is a missing thumbnail. However, when
+     * Normally, this test will fail when there is a missing golden image. However, when
      * you create creating a new test, it's useful to be able to turn this off such that
-     * you can generate all the missing thumbnails in one go, rather than having to run
-     * the test repeatedly to get to each new render assertion generating its thumbnail.
+     * you can generate all the missing golden images in one go, rather than having to run
+     * the test repeatedly to get to each new render assertion generating its golden images.
      */
-    private static final boolean FAIL_ON_MISSING_THUMBNAIL = true;
-
-    private static final int THUMBNAIL_SIZE = 1000;
+    private static final boolean FAIL_ON_MISSING_GOLDEN = true;
 
     private static final double MAX_PERCENT_DIFFERENCE = 0.1;
 
     public static void requireSimilar(@NonNull String relativePath, @NonNull BufferedImage image)
             throws IOException {
-        int maxDimension = Math.max(image.getWidth(), image.getHeight());
-        double scale = THUMBNAIL_SIZE / (double)maxDimension;
-        BufferedImage thumbnail = scale(image, scale, scale);
-
         InputStream is = ImageUtils.class.getClassLoader().getResourceAsStream(relativePath);
         if (is == null) {
-            String message = "Unable to load golden thumbnail: " + relativePath + "\n";
-            message = saveImageAndAppendMessage(thumbnail, message, relativePath);
-            if (FAIL_ON_MISSING_THUMBNAIL) {
+            String message = "Unable to load golden image: " + relativePath + "\n";
+            message = saveImageAndAppendMessage(image, message, relativePath);
+            if (FAIL_ON_MISSING_GOLDEN) {
                 fail(message);
             } else {
                 System.out.println(message);
@@ -77,7 +68,7 @@
         else {
             try {
                 BufferedImage goldenImage = ImageIO.read(is);
-                assertImageSimilar(relativePath, goldenImage, thumbnail, MAX_PERCENT_DIFFERENCE);
+                assertImageSimilar(relativePath, goldenImage, image, MAX_PERCENT_DIFFERENCE);
             } finally {
                 is.close();
             }
@@ -192,114 +183,7 @@
     }
 
     /**
-     * Resize the given image
-     *
-     * @param source the image to be scaled
-     * @param xScale x scale
-     * @param yScale y scale
-     * @return the scaled image
-     */
-    @NonNull
-    public static BufferedImage scale(@NonNull BufferedImage source, double xScale, double yScale) {
-
-        int sourceWidth = source.getWidth();
-        int sourceHeight = source.getHeight();
-        int destWidth = Math.max(1, (int) (xScale * sourceWidth));
-        int destHeight = Math.max(1, (int) (yScale * sourceHeight));
-        int imageType = source.getType();
-        if (imageType == BufferedImage.TYPE_CUSTOM) {
-            imageType = BufferedImage.TYPE_INT_ARGB;
-        }
-        if (xScale > 0.5 && yScale > 0.5) {
-            BufferedImage scaled =
-                    new BufferedImage(destWidth, destHeight, imageType);
-            Graphics2D g2 = scaled.createGraphics();
-            g2.setComposite(AlphaComposite.Src);
-            g2.setColor(new Color(0, true));
-            g2.fillRect(0, 0, destWidth, destHeight);
-            if (xScale == 1 && yScale == 1) {
-                g2.drawImage(source, 0, 0, null);
-            } else {
-                setRenderingHints(g2);
-                g2.drawImage(source, 0, 0, destWidth, destHeight, 0, 0, sourceWidth, sourceHeight,
-                        null);
-            }
-            g2.dispose();
-            return scaled;
-        } else {
-            // When creating a thumbnail, using the above code doesn't work very well;
-            // you get some visible artifacts, especially for text. Instead use the
-            // technique of repeatedly scaling the image into half; this will cause
-            // proper averaging of neighboring pixels, and will typically (for the kinds
-            // of screen sizes used by this utility method in the layout editor) take
-            // about 3-4 iterations to get the result since we are logarithmically reducing
-            // the size. Besides, each successive pass in operating on much fewer pixels
-            // (a reduction of 4 in each pass).
-            //
-            // However, we may not be resizing to a size that can be reached exactly by
-            // successively diving in half. Therefore, once we're within a factor of 2 of
-            // the final size, we can do a resize to the exact target size.
-            // However, we can get even better results if we perform this final resize
-            // up front. Let's say we're going from width 1000 to a destination width of 85.
-            // The first approach would cause a resize from 1000 to 500 to 250 to 125, and
-            // then a resize from 125 to 85. That last resize can distort/blur a lot.
-            // Instead, we can start with the destination width, 85, and double it
-            // successfully until we're close to the initial size: 85, then 170,
-            // then 340, and finally 680. (The next one, 1360, is larger than 1000).
-            // So, now we *start* the thumbnail operation by resizing from width 1000 to
-            // width 680, which will preserve a lot of visual details such as text.
-            // Then we can successively resize the image in half, 680 to 340 to 170 to 85.
-            // We end up with the expected final size, but we've been doing an exact
-            // divide-in-half resizing operation at the end so there is less distortion.
-
-            int iterations = 0; // Number of halving operations to perform after the initial resize
-            int nearestWidth = destWidth; // Width closest to source width that = 2^x, x is integer
-            int nearestHeight = destHeight;
-            while (nearestWidth < sourceWidth / 2) {
-                nearestWidth *= 2;
-                nearestHeight *= 2;
-                iterations++;
-            }
-
-            BufferedImage scaled = new BufferedImage(nearestWidth, nearestHeight, imageType);
-
-            Graphics2D g2 = scaled.createGraphics();
-            setRenderingHints(g2);
-            g2.drawImage(source, 0, 0, nearestWidth, nearestHeight, 0, 0, sourceWidth, sourceHeight,
-                    null);
-            g2.dispose();
-
-            sourceWidth = nearestWidth;
-            sourceHeight = nearestHeight;
-            source = scaled;
-
-            for (int iteration = iterations - 1; iteration >= 0; iteration--) {
-                int halfWidth = sourceWidth / 2;
-                int halfHeight = sourceHeight / 2;
-                scaled = new BufferedImage(halfWidth, halfHeight, imageType);
-                g2 = scaled.createGraphics();
-                setRenderingHints(g2);
-                g2.drawImage(source, 0, 0, halfWidth, halfHeight, 0, 0, sourceWidth, sourceHeight,
-                        null);
-                g2.dispose();
-
-                sourceWidth = halfWidth;
-                sourceHeight = halfHeight;
-                source = scaled;
-                iterations--;
-            }
-            return scaled;
-        }
-    }
-
-    private static void setRenderingHints(@NonNull Graphics2D g2) {
-        g2.setRenderingHint(KEY_INTERPOLATION,VALUE_INTERPOLATION_BILINEAR);
-        g2.setRenderingHint(KEY_RENDERING, VALUE_RENDER_QUALITY);
-        g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
-    }
-
-    /**
-     * Directory where to write the thumbnails and deltas.
+     * Directory where to write the generated image and deltas.
      */
     @NonNull
     private static File getFailureDir() {
@@ -318,7 +202,7 @@
     }
 
     /**
-     * Saves the generated thumbnail image and appends the info message to an initial message
+     * Saves the generated golden image and appends the info message to an initial message
      */
     @NonNull
     private static String saveImageAndAppendMessage(@NonNull BufferedImage image,
@@ -329,7 +213,7 @@
             assertTrue(deleted);
         }
         ImageIO.write(image, "PNG", output);
-        initialMessage += "Thumbnail for current rendering stored at " + output.getPath();
+        initialMessage += "Golden image for current rendering stored at " + output.getPath();
 //        initialMessage += "\nRun the following command to accept the changes:\n";
 //        initialMessage += String.format("mv %1$s %2$s", output.getPath(),
 //                ImageUtils.class.getResource(relativePath).getPath());
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ModuleClassLoader.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ModuleClassLoader.java
index da360f3..d52fdcf 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ModuleClassLoader.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ModuleClassLoader.java
@@ -17,6 +17,7 @@
 package com.android.layoutlib.bridge.intensive.util;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -49,7 +50,11 @@
         if (clazz == null) {
             String path = name.replace('.', '/').concat(".class");
             try {
-                byte[] b = Streams.readFully(getResourceAsStream(myModuleRoot + path));
+                InputStream classStream = getResourceAsStream(myModuleRoot + path);
+                if (classStream == null) {
+                    throw new IOException("Cannot find resource stream for class " + name);
+                }
+                byte[] b = Streams.readFully(classStream);
                 clazz = defineClass(name, b, 0, b.length);
                 mClasses.put(name, clazz);
             } catch (IOException ignore) {
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java
index 396880e..35ade34 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java
@@ -61,10 +61,9 @@
     private AssetRepository mAssetRepository = null;
     private boolean mDecor = true;
     private IImageFactory mImageFactory = null;
-    private boolean enableShadows = true;
-    private boolean highQualityShadows = true;
     private boolean enableLayoutValidator = false;
     private boolean enableLayoutValidatorImageCheck = false;
+    private boolean transparentBackground = false;
 
     @NonNull
     public SessionParamsBuilder setParser(@NonNull LayoutPullParser layoutParser) {
@@ -166,18 +165,6 @@
     }
 
     @NonNull
-    public SessionParamsBuilder disableShadows() {
-        this.enableShadows = false;
-        return this;
-    }
-
-    @NonNull
-    public SessionParamsBuilder disableHighQualityShadows() {
-        this.highQualityShadows = false;
-        return this;
-    }
-
-    @NonNull
     public SessionParamsBuilder enableLayoutValidation() {
         this.enableLayoutValidator = true;
         return this;
@@ -188,6 +175,12 @@
         this.enableLayoutValidatorImageCheck = true;
         return this;
     }
+    
+    @NonNull
+    public SessionParamsBuilder setTransparentBackground() {
+        this.transparentBackground = true;
+        return this;
+    }
 
     @NonNull
     public SessionParams build() {
@@ -210,8 +203,6 @@
         SessionParams params = new SessionParams(mLayoutParser, mRenderingMode, mProjectKey /* for
         caching */, mConfigGenerator.getHardwareConfig(), resourceResolver, mLayoutlibCallback,
                 mMinSdk, mTargetSdk, mLayoutLog);
-        params.setFlag(RenderParamsFlags.FLAG_ENABLE_SHADOW, enableShadows);
-        params.setFlag(RenderParamsFlags.FLAG_RENDER_HIGH_QUALITY_SHADOW, highQualityShadows);
         params.setFlag(RenderParamsFlags.FLAG_ENABLE_LAYOUT_VALIDATOR, enableLayoutValidator);
         params.setFlag(
                 RenderParamsFlags.FLAG_ENABLE_LAYOUT_VALIDATOR_IMAGE_CHECK,
@@ -227,6 +218,10 @@
             params.setForceNoDecor();
         }
 
+        if (transparentBackground) {
+            params.setTransparentBackground();
+        }
+
         return params;
     }
 }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/BlurryImageView.java b/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/BlurryImageView.java
new file mode 100644
index 0000000..a2268b9
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/BlurryImageView.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 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.test.widgets;
+
+import android.R;
+import android.content.Context;
+import android.graphics.RenderEffect;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+public class BlurryImageView extends ImageView {
+    public BlurryImageView(Context context) {
+        super(context);
+        init(context);
+    }
+
+    public BlurryImageView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(context);
+    }
+
+    public BlurryImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(context);
+    }
+
+    public BlurryImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init(context);
+    }
+
+    private void init(Context context) {
+        Drawable drawable = context.getResources().getDrawable(R.drawable.star_big_on, null);
+        setImageDrawable(drawable);
+        setRenderEffect(RenderEffect.createBlurEffect(20f, 20f, Shader.TileMode.MIRROR));
+    }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/CustomImageView.java b/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/CustomImageView.java
new file mode 100644
index 0000000..d1326c8
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/CustomImageView.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 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.test.widgets;
+
+import android.R;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+public class CustomImageView extends ImageView {
+    public CustomImageView(Context context) {
+        super(context);
+        init(context);
+    }
+
+    public CustomImageView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(context);
+    }
+
+    public CustomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(context);
+    }
+
+    public CustomImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init(context);
+    }
+
+    private void init(Context context) {
+        Drawable drawable = context.getResources().getDrawable(R.drawable.ic_lock_idle_alarm, null);
+        setImageDrawable(drawable);
+    }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/util/ChoreographerCallbacksTest.java b/bridge/tests/src/com/android/layoutlib/bridge/util/ChoreographerCallbacksTest.java
new file mode 100644
index 0000000..68e7006
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/util/ChoreographerCallbacksTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.util;
+
+import com.android.ide.common.rendering.api.ILayoutLog;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.Choreographer.FrameCallback;
+
+import java.util.ArrayList;
+
+public class ChoreographerCallbacksTest {
+
+    private static class ValidatingLogger implements ILayoutLog {
+        @Override
+        public void error(@Nullable String tag, @NonNull String message,
+                @Nullable Object viewCookie, @Nullable Object data) {
+            errorMessages.add(message);
+        }
+
+        @Override
+        public void error(@Nullable String tag, @NonNull String message,
+                @Nullable Throwable throwable, @Nullable Object viewCookie, @Nullable Object data) {
+            errorMessages.add(message);
+        }
+
+        private final ArrayList<String> errorMessages = new ArrayList<>();
+    }
+
+    private final ValidatingLogger logger = new ValidatingLogger();
+
+    @Before
+    public void setUp() {
+        logger.errorMessages.clear();
+    }
+
+    @Test
+    public void testAddAndExecuteInOrder() {
+        ChoreographerCallbacks callbacks = new ChoreographerCallbacks();
+        ArrayList<Integer> order = new ArrayList<>();
+
+        callbacks.add((Runnable) () -> order.add(2), 200);
+        callbacks.add((FrameCallback) frameTimeNanos -> order.add(1), 100);
+        callbacks.execute(200, logger);
+
+        Assert.assertArrayEquals(order.toArray(), new Object[] { 1, 2 });
+        Assert.assertTrue(logger.errorMessages.isEmpty());
+    }
+
+    @Test
+    public void testAddAndExecuteOnlyDue() {
+        ChoreographerCallbacks callbacks = new ChoreographerCallbacks();
+        ArrayList<Integer> order = new ArrayList<>();
+
+        callbacks.add((Runnable) () -> order.add(2), 200);
+        callbacks.add((FrameCallback) frameTimeNanos -> order.add(1), 100);
+        callbacks.execute(100, logger);
+
+        Assert.assertArrayEquals(order.toArray(), new Object[] { 1 });
+        Assert.assertTrue(logger.errorMessages.isEmpty());
+    }
+
+    @Test
+    public void testRemove() {
+        ChoreographerCallbacks callbacks = new ChoreographerCallbacks();
+        ArrayList<Integer> order = new ArrayList<>();
+
+        Runnable runnable = () -> order.add(2);
+        callbacks.add(runnable, 200);
+        callbacks.add((FrameCallback) frameTimeNanos -> order.add(1), 100);
+        callbacks.remove(runnable);
+        callbacks.execute(200, logger);
+
+        Assert.assertArrayEquals(order.toArray(), new Object[] { 1 });
+        Assert.assertTrue(logger.errorMessages.isEmpty());
+    }
+
+    @Test
+    public void testErrorIfUnknownCallbackType() {
+        ChoreographerCallbacks callbacks = new ChoreographerCallbacks();
+
+        callbacks.add(new Object(), 100);
+        callbacks.execute(200, logger);
+
+        Assert.assertFalse(logger.errorMessages.isEmpty());
+        Assert.assertEquals(logger.errorMessages.get(0), "Unexpected action as Choreographer callback");
+    }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/util/HandlerMessageQueueTest.java b/bridge/tests/src/com/android/layoutlib/bridge/util/HandlerMessageQueueTest.java
new file mode 100644
index 0000000..f7b091e
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/util/HandlerMessageQueueTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.util;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Looper_Accessor;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class HandlerMessageQueueTest {
+
+    @Before
+    public void setUp() {
+        Looper.prepareMainLooper();
+    }
+
+    @Test
+    public void testSingleHandler_SingleRunnable() {
+        HandlerMessageQueue q = new HandlerMessageQueue();
+        Handler h1 = new Handler();
+        Runnable r1 = () -> {};
+
+        assertFalse(q.isNotEmpty());
+
+        q.add(h1, 100, r1);
+
+        assertTrue(q.isNotEmpty());
+        assertNull(q.extractFirst(0));
+        assertEquals(q.extractFirst(100), r1);
+        assertFalse(q.isNotEmpty());
+        assertNull(q.extractFirst(100));
+    }
+
+    @Test
+    public void testSingleHandler_MultipleRunnables() {
+        HandlerMessageQueue q = new HandlerMessageQueue();
+        Handler h1 = new Handler();
+        Runnable r1 = () -> {};
+        Runnable r2 = () -> {};
+        Runnable r3 = () -> {};
+
+        q.add(h1, 100, r1);
+        q.add(h1, 100, r2);
+        q.add(h1, 50, r3);
+
+        assertEquals(q.extractFirst(100), r3);
+        assertEquals(q.extractFirst(100), r1);
+        assertTrue(q.isNotEmpty());
+        assertEquals(q.extractFirst(100), r2);
+        assertFalse(q.isNotEmpty());
+    }
+
+    @Test
+    public void testMultipleHandlers() {
+        HandlerMessageQueue q = new HandlerMessageQueue();
+        Handler h1 = new Handler();
+        Handler h2 = new Handler();
+        Runnable r1 = () -> {};
+        Runnable r2 = () -> {};
+        Runnable r3 = () -> {};
+
+        q.add(h1, 200, r1);
+        q.add(h2, 100, r2);
+        q.add(h1, 50, r3);
+
+        assertEquals(q.extractFirst(70), r3);
+        assertEquals(q.extractFirst(300), r2);
+        assertEquals(q.extractFirst(300), r1);
+        assertFalse(q.isNotEmpty());
+        assertNull(q.extractFirst(500));
+
+        q.add(h1, 400, r1);
+
+        assertTrue(q.isNotEmpty());
+        assertEquals(q.extractFirst(500), r1);
+        assertFalse(q.isNotEmpty());
+    }
+
+    @Test
+    public void testMultipleHandlers_Clear() {
+        HandlerMessageQueue q = new HandlerMessageQueue();
+        Handler h1 = new Handler();
+        Handler h2 = new Handler();
+        Runnable r1 = () -> {};
+        Runnable r2 = () -> {};
+        Runnable r3 = () -> {};
+
+        q.add(h1, 200, r1);
+        q.add(h2, 100, r2);
+        q.add(h1, 50, r3);
+
+        q.clear();
+
+        assertFalse(q.isNotEmpty());
+        assertNull(q.extractFirst(200));
+    }
+
+    @After
+    public void tearDown() {
+        Looper_Accessor.cleanupThread();
+    }
+}
diff --git a/bridge/tests/src/com/android/tools/idea/validator/accessibility/AccessibilityValidatorTests.java b/bridge/tests/src/com/android/tools/idea/validator/AccessibilityValidatorTests.java
similarity index 70%
rename from bridge/tests/src/com/android/tools/idea/validator/accessibility/AccessibilityValidatorTests.java
rename to bridge/tests/src/com/android/tools/idea/validator/AccessibilityValidatorTests.java
index 16ad4a2..4ab7863 100644
--- a/bridge/tests/src/com/android/tools/idea/validator/accessibility/AccessibilityValidatorTests.java
+++ b/bridge/tests/src/com/android/tools/idea/validator/AccessibilityValidatorTests.java
@@ -14,29 +14,29 @@
  * limitations under the License.
  */
 
-package com.android.tools.idea.validator.accessibility;
+package com.android.tools.idea.validator;
 
 import com.android.ide.common.rendering.api.RenderSession;
-import com.android.ide.common.rendering.api.SessionParams;
 import com.android.layoutlib.bridge.intensive.RenderTestBase;
 import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
 import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
 import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
 import com.android.layoutlib.bridge.intensive.util.SessionParamsBuilder;
-import com.android.tools.idea.validator.LayoutValidator;
-import com.android.tools.idea.validator.ValidatorData;
 import com.android.tools.idea.validator.ValidatorData.Issue;
 import com.android.tools.idea.validator.ValidatorData.Level;
 import com.android.tools.idea.validator.ValidatorData.Policy;
 import com.android.tools.idea.validator.ValidatorData.Type;
-import com.android.tools.idea.validator.ValidatorResult;
 
+import org.junit.Ignore;
 import org.junit.Test;
 
 import java.util.EnumSet;
 import java.util.List;
-import java.util.stream.Collectors;
 
+import com.google.android.apps.common.testing.accessibility.framework.uielement.DefaultCustomViewBuilderAndroid;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.ViewHierarchyElementAndroid;
+
+import static com.android.tools.idea.validator.ValidatorUtil.filter;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -57,6 +57,26 @@
 public class AccessibilityValidatorTests extends RenderTestBase {
 
     @Test
+    public void testPaused() throws Exception {
+        try {
+            LayoutValidator.setPaused(true);
+            render("a11y_test_dup_clickable_bounds.xml", session -> {
+                ValidatorResult result = getRenderResult(session);
+                List<Issue> dupBounds = filter(result.getIssues(), "DuplicateClickableBoundsCheck");
+
+                /**
+                 * Expects no errors since disabled. When enabled it should print
+                 * the same result as {@link #testDuplicateClickableBoundsCheck}
+                 */
+                ExpectedLevels expectedLevels = new ExpectedLevels();
+                expectedLevels.check(dupBounds);
+            });
+        } finally {
+            LayoutValidator.setPaused(false);
+        }
+    }
+
+    @Test
     public void testDuplicateClickableBoundsCheck() throws Exception {
         render("a11y_test_dup_clickable_bounds.xml", session -> {
             ValidatorResult result = getRenderResult(session);
@@ -91,6 +111,7 @@
             ExpectedLevels expectedLevels = new ExpectedLevels();
             expectedLevels.expectedVerboses = 3;
             expectedLevels.expectedWarnings = 1;
+            expectedLevels.expectedFixes = 0;
             expectedLevels.check(redundant);
         });
     }
@@ -133,6 +154,7 @@
             ExpectedLevels expectedLevels = new ExpectedLevels();
             expectedLevels.expectedVerboses = 1;
             expectedLevels.expectedErrors = 1;
+            expectedLevels.expectedFixes = 1;
             expectedLevels.check(speakableCheck);
 
             // Make sure no other errors in the system.
@@ -155,6 +177,31 @@
             expectedLevels.expectedErrors = 3;
             expectedLevels.expectedWarnings = 1; // This is true only if image is passed.
             expectedLevels.expectedVerboses = 2;
+            expectedLevels.expectedFixes = 4;
+            expectedLevels.check(textContrast);
+
+            // Make sure no other errors in the system.
+            textContrast = filter(textContrast, EnumSet.of(Level.ERROR));
+            List<Issue> filtered = filter(result.getIssues(), EnumSet.of(Level.ERROR));
+            checkEquals(filtered, textContrast);
+        });
+    }
+
+    /* TODO: {@link LayoutValidator::obtainCharacterLocations is false by default for now }*/
+    @Test
+    @Ignore
+    public void testSwitchTextContrastCheck() throws Exception {
+        render("a11y_test_switch_text_contrast.xml", session -> {
+            ValidatorResult result = getRenderResult(session);
+            List<Issue> textContrast = filter(result.getIssues(), "TextContrastCheck");
+
+            // ATF doesn't count alpha values in a Switch unless image is passed and the character
+            // locations are available.
+            ExpectedLevels expectedLevels = new ExpectedLevels();
+            expectedLevels.expectedErrors = 0;
+            expectedLevels.expectedWarnings = 1; // True only if character locations are available.
+            expectedLevels.expectedVerboses = 2;
+            expectedLevels.expectedFixes = 1;
             expectedLevels.check(textContrast);
 
             // Make sure no other errors in the system.
@@ -174,6 +221,7 @@
             ExpectedLevels expectedLevels = new ExpectedLevels();
             expectedLevels.expectedErrors = 3;
             expectedLevels.expectedVerboses = 3;
+            expectedLevels.expectedFixes = 3;
             expectedLevels.check(textContrast);
 
             // Make sure no other errors in the system.
@@ -202,6 +250,49 @@
     }
 
     @Test
+    public void testClassLoaderOverride() throws Exception {
+        final boolean[] overriddenClassLoaderCalled = {false};
+
+        // testAndroid will fail to find class - so to trigger LayoutlibCallback
+        DefaultCustomViewBuilderAndroid testAndroid = new DefaultCustomViewBuilderAndroid() {
+            @Override
+            public Class<?> getClassByName(
+                    ViewHierarchyElementAndroid view, String className) {
+                return null;
+            }
+        };
+        // Callback when CustomViewBuilderAndroid fails.
+        LayoutLibTestCallback testCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader) {
+                    @Override
+                    public Class<?> findClass(String name) throws ClassNotFoundException {
+                        if (name.contains("ImageView")) {
+                            // Make sure one of the view (ImageView) passes thru here
+                            overriddenClassLoaderCalled[0] = true;
+                        }
+                        return mDefaultClassLoader.loadClass(name);
+                    }
+                };
+        try {
+            ValidatorUtil.sDefaultCustomViewBuilderAndroid = testAndroid;
+            render("a11y_test_image_contrast.xml", session -> {
+                ValidatorResult result = getRenderResult(session);
+                List<Issue> imageContrast = filter(result.getIssues(), "ImageContrastCheck");
+
+                ExpectedLevels expectedLevels = new ExpectedLevels();
+                expectedLevels.expectedWarnings = 1;
+                expectedLevels.expectedVerboses = 1;
+                expectedLevels.check(imageContrast);
+
+                // Ensure that the check went thru the overridden class loader.
+                assertTrue(overriddenClassLoaderCalled[0]);
+            }, true, testCallback);
+        } finally {
+            ValidatorUtil.sDefaultCustomViewBuilderAndroid = new DefaultCustomViewBuilderAndroid();
+        }
+    }
+
+    @Test
     public void testImageContrastCheckNoImage() throws Exception {
         render("a11y_test_image_contrast.xml", session -> {
             ValidatorResult result = getRenderResult(session);
@@ -227,6 +318,7 @@
             ExpectedLevels expectedLevels = new ExpectedLevels();
             expectedLevels.expectedErrors = 5;
             expectedLevels.expectedVerboses = 1;
+            expectedLevels.expectedFixes = 5;
             expectedLevels.check(targetSizes);
 
             // Make sure no other errors in the system.
@@ -243,22 +335,13 @@
         }
     }
 
-    private List<Issue> filter(List<ValidatorData.Issue> results, EnumSet<Level> errors) {
-        return results.stream().filter(
-                issue -> errors.contains(issue.mLevel)).collect(Collectors.toList());
-    }
-
-    private List<Issue> filter(
-            List<ValidatorData.Issue> results, String sourceClass) {
-        return results.stream().filter(
-                issue -> sourceClass.equals(issue.mSourceClass)).collect(Collectors.toList());
-    }
-
     private ValidatorResult getRenderResult(RenderSession session) {
         Object validationData = session.getValidationData();
-        assertNotNull(validationData);
-        assertTrue(validationData instanceof ValidatorResult);
-        return (ValidatorResult) validationData;
+        assertTrue(validationData instanceof ValidatorHierarchy);
+
+        ValidatorResult result = ValidatorUtil.generateResults(LayoutValidator.DEFAULT_POLICY,
+                (ValidatorHierarchy) validationData);
+        return result;
     }
     private void render(String fileName, RenderSessionListener verifier) throws Exception {
         render(fileName, verifier, true);
@@ -268,13 +351,24 @@
             String fileName,
             RenderSessionListener verifier,
             boolean enableImageCheck) throws Exception {
+        render(
+                fileName,
+                verifier,
+                enableImageCheck,
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader));
+    }
+
+    private void render(
+            String fileName,
+            RenderSessionListener verifier,
+            boolean enableImageCheck,
+            LayoutLibTestCallback layoutLibCallback) throws Exception {
         LayoutValidator.updatePolicy(new Policy(
                 EnumSet.of(Type.ACCESSIBILITY, Type.RENDER),
                 EnumSet.of(Level.ERROR, Level.WARNING, Level.INFO, Level.VERBOSE)));
+        LayoutValidator.setObtainCharacterLocations(false);
 
         LayoutPullParser parser = createParserFromPath(fileName);
-        LayoutLibTestCallback layoutLibCallback =
-                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
         SessionParamsBuilder params = getSessionParamsBuilder()
                 .setParser(parser)
@@ -302,12 +396,15 @@
         public int expectedInfos = 0;
         // Number of verboses expected
         public int expectedVerboses = 0;
+        // Number of fixes expected
+        public int expectedFixes = 0;
 
         public void check(List<Issue> issues) {
             int errors = 0;
             int warnings = 0;
             int infos = 0;
             int verboses = 0;
+            int fixes = 0;
 
             for (Issue issue : issues) {
                 switch (issue.mLevel) {
@@ -324,12 +421,17 @@
                         verboses++;
                         break;
                 }
+
+                if (issue.mFix != null) {
+                    fixes ++;
+                }
             }
 
             assertEquals("Number of expected errors", expectedErrors, errors);
             assertEquals("Number of expected warnings",expectedWarnings, warnings);
             assertEquals("Number of expected infos", expectedInfos, infos);
             assertEquals("Number of expected verboses", expectedVerboses, verboses);
+            assertEquals("Number of expected fixes", expectedFixes, fixes);
 
             int size = expectedErrors + expectedWarnings + expectedInfos + expectedVerboses;
             assertEquals("expected size", size, issues.size());
diff --git a/bridge/tests/src/com/android/tools/idea/validator/LayoutValidatorTests.java b/bridge/tests/src/com/android/tools/idea/validator/LayoutValidatorTests.java
index ec91e17..723a2c0 100644
--- a/bridge/tests/src/com/android/tools/idea/validator/LayoutValidatorTests.java
+++ b/bridge/tests/src/com/android/tools/idea/validator/LayoutValidatorTests.java
@@ -21,14 +21,18 @@
 import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
 import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
 import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
+import com.android.tools.idea.validator.ValidatorData.CompoundFix;
 import com.android.tools.idea.validator.ValidatorData.Issue;
 import com.android.tools.idea.validator.ValidatorData.Level;
+import com.android.tools.idea.validator.ValidatorData.SetViewAttributeFix;
+
 import com.android.tools.idea.validator.ValidatorData.Type;
 
 import org.junit.Test;
 
 import android.view.View;
 
+import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -37,10 +41,24 @@
 import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 public class LayoutValidatorTests extends RenderTestBase {
 
+    private static final float SCALE_X_FOR_NEXUS_5 = 1.0f;
+    private static final float SCALE_Y_FOR_NEXUS_5 = 1.0f;
+
+    @Test
+    public void testEnsureDebuggingOff() {
+        assertFalse(LayoutValidator.shouldSaveCroppedImages());
+    }
+
+    @Test
+    public void testEnsureObtainCharacterLocation() {
+        assertFalse(LayoutValidator.obtainCharacterLocations());
+    }
+
     @Test
     public void testRenderAndVerify() throws Exception {
         LayoutPullParser parser = createParserFromPath("a11y_test1.xml");
@@ -61,30 +79,48 @@
     @Test
     public void testValidation() throws Exception {
         render(sBridge, generateParams(), -1, session -> {
-            ValidatorResult result = LayoutValidator
-                    .validate(((View) session.getRootViews().get(0).getViewObject()), null);
-            assertEquals(3, result.getIssues().size());
+            ValidatorResult result = LayoutValidator.validate(
+                    ((View) session.getRootViews().get(0).getViewObject()),
+                    null,
+                    SCALE_X_FOR_NEXUS_5,
+                    SCALE_Y_FOR_NEXUS_5);
+            assertEquals(30, result.getIssues().size());
+            ArrayList<Issue> errorIssues = new ArrayList<>();
             for (Issue issue : result.getIssues()) {
                 assertEquals(Type.ACCESSIBILITY, issue.mType);
-                assertEquals(Level.ERROR, issue.mLevel);
+                if (issue.mLevel == Level.ERROR) {
+                    errorIssues.add(issue);
+                }
             }
 
-            Issue first = result.getIssues().get(0);
+            Issue first = errorIssues.get(0);
             assertEquals("This item may not have a label readable by screen readers.",
                          first.mMsg);
             assertEquals("https://support.google.com/accessibility/android/answer/7158690",
                          first.mHelpfulUrl);
             assertEquals("SpeakableTextPresentCheck", first.mSourceClass);
+            assertTrue(first.mFix instanceof SetViewAttributeFix);
+            assertEquals("Set this item's android:contentDescription to a meaningful" +
+                            " non-empty string or resource reference.",
+                    first.mFix.getDescription());
 
-            Issue second = result.getIssues().get(1);
+            Issue second = errorIssues.get(1);
+            CompoundFix compoundFix = (CompoundFix) second.mFix;
             assertEquals("This item's size is 10dp x 10dp. Consider making this touch target " +
                             "48dp wide and 48dp high or larger.",
                          second.mMsg);
             assertEquals("https://support.google.com/accessibility/android/answer/7101858",
                          second.mHelpfulUrl);
             assertEquals("TouchTargetSizeCheck", second.mSourceClass);
+            assertTrue(compoundFix.mFixes.size() == 2);
+            assertEquals(
+                    "Set this item's android:layout_width to 48dp.",
+                    compoundFix.mFixes.get(0).getDescription());
+            assertEquals(
+                    "Set this item's android:layout_height to 48dp.",
+                    compoundFix.mFixes.get(1).getDescription());
 
-            Issue third = result.getIssues().get(2);
+            Issue third = errorIssues.get(2);
             assertEquals("The item's text contrast ratio is 1.00. This ratio is based on a text color " +
                             "of #000000 and background color of #000000. Consider increasing this item's" +
                             " text contrast ratio to 4.50 or greater.",
@@ -92,6 +128,9 @@
             assertEquals("https://support.google.com/accessibility/android/answer/7158390",
                          third.mHelpfulUrl);
             assertEquals("TextContrastCheck", third.mSourceClass);
+            assertTrue(third.mFix instanceof SetViewAttributeFix);
+            assertEquals("Set this item's android:textColor to #757575.",
+                    third.mFix.getDescription());
         });
     }
 
@@ -105,8 +144,14 @@
 
             render(sBridge, generateParams(), -1, session -> {
                 ValidatorResult result = LayoutValidator.validate(
-                        ((View) session.getRootViews().get(0).getViewObject()), null);
-                assertTrue(result.getIssues().isEmpty());
+                        ((View) session.getRootViews().get(0).getViewObject()),
+                        null,
+                        SCALE_X_FOR_NEXUS_5,
+                        SCALE_Y_FOR_NEXUS_5);
+
+                assertEquals(1, result.getIssues().size());
+                assertEquals("Hierarchy is not built yet.",
+                        result.getIssues().get(0).mMsg);
             });
         } finally {
             LayoutValidator.updatePolicy(LayoutValidator.DEFAULT_POLICY);
@@ -123,7 +168,10 @@
 
             render(sBridge, generateParams(), -1, session -> {
                 ValidatorResult result = LayoutValidator.validate(
-                        ((View) session.getRootViews().get(0).getViewObject()), null);
+                        ((View) session.getRootViews().get(0).getViewObject()),
+                        null,
+                        SCALE_X_FOR_NEXUS_5,
+                        SCALE_Y_FOR_NEXUS_5);
                 assertEquals(27, result.getIssues().size());
                 result.getIssues().forEach(issue ->assertEquals(Level.VERBOSE, issue.mLevel));
             });
@@ -150,7 +198,10 @@
 
             render(sBridge, generateParams(), -1, session -> {
                 ValidatorResult result = LayoutValidator.validate(
-                        ((View) session.getRootViews().get(0).getViewObject()), null);
+                        ((View) session.getRootViews().get(0).getViewObject()),
+                        null,
+                        SCALE_X_FOR_NEXUS_5,
+                        SCALE_Y_FOR_NEXUS_5);
                 assertEquals(1, result.getIssues().size());
                 Issue textCheck = result.getIssues().get(0);
                 assertEquals("The item's text contrast ratio is 1.00. This ratio is based on a text color " +
diff --git a/bridge/tests/src/com/android/tools/idea/validator/ValidatorResultTests.java b/bridge/tests/src/com/android/tools/idea/validator/ValidatorResultTests.java
new file mode 100644
index 0000000..618200c
--- /dev/null
+++ b/bridge/tests/src/com/android/tools/idea/validator/ValidatorResultTests.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2020 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.idea.validator;
+
+import com.android.tools.idea.validator.ValidatorData.Issue.IssueBuilder;
+import com.android.tools.idea.validator.ValidatorData.Level;
+import com.android.tools.idea.validator.ValidatorData.Type;
+import com.android.tools.idea.validator.ValidatorResult.Builder;
+import com.android.tools.idea.validator.ValidatorResult.Metric;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class ValidatorResultTests {
+    private static final long EPSILON = 10L;
+
+    @Test
+    public void testBuildingEmptyResult() {
+        ValidatorResult result = new ValidatorResult.Builder().build();
+        assertNotNull(result);
+        assertTrue(result.getIssues().isEmpty());
+        assertTrue(result.getSrcMap().isEmpty());
+        assertNotNull(result.getMetric());
+        assertEquals("Result containing 0 issues:\n", result.toString());
+    }
+
+    @Test
+    public void testBuildingResult() {
+        ValidatorResult.Builder builder = new ValidatorResult.Builder();
+        for (int i = 0; i < 3; i++) {
+            builder.mIssues.add(createIssueBuilder().setMsg("issue " + i).build());
+        }
+        assertEquals(
+                "Result containing 3 issues:\n" +
+                        " - [ERROR] issue 0\n" +
+                        " - [ERROR] issue 1\n" +
+                        " - [ERROR] issue 2\n",
+                builder.build().toString());
+    }
+
+    private static IssueBuilder createIssueBuilder() {
+        return new IssueBuilder()
+                .setCategory("category")
+                .setType(Type.ACCESSIBILITY)
+                .setMsg("msg")
+                .setLevel(Level.ERROR)
+                .setSourceClass("Source class");
+    }
+
+    @Ignore("b/172205439")
+    @Test
+    public void testMetricRecordHierarchyCreationTime() {
+        long expectedElapsed = 100;
+        Metric metric = new Builder().mMetric;
+
+        metric.startHierarchyCreationTimer();
+        try {
+            Thread.sleep(expectedElapsed);
+        } catch (Exception e) {
+            String msg = "Unexpected exception. ";
+            if (e.getMessage() == null) {
+                msg += e.getMessage();
+            }
+            fail(msg);
+        }
+        metric.recordHierarchyCreationTime();
+
+        long diff = Math.abs(expectedElapsed - metric.mHierarchyCreationMs);
+        System.out.println(diff);
+        assertTrue(diff < EPSILON);
+    }
+
+    @Test
+    public void testMetricToString() {
+        Metric metric = new Builder().mMetric;
+
+        metric.mErrorMessage = "TestError";
+        metric.mImageMemoryBytes = Long.MAX_VALUE;
+
+        assertEquals(
+                "Validation result metric: { hierarchy creation=0ms, image memory=9223372036gb }",
+                metric.toString());
+    }
+
+}
diff --git a/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java b/common/src/com/android/layoutlib/common/util/ReflectionUtils.java
similarity index 74%
rename from bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
rename to common/src/com/android/layoutlib/common/util/ReflectionUtils.java
index 00348ea..7c2bff0 100644
--- a/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
+++ b/common/src/com/android/layoutlib/common/util/ReflectionUtils.java
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.layoutlib.bridge.util;
+package com.android.layoutlib.common.util;
 
-import android.annotation.NonNull;
-import android.annotation.Nullable;
+import com.android.tools.layoutlib.annotations.NonNull;
+import com.android.tools.layoutlib.annotations.Nullable;
 
+import java.lang.reflect.Field;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -30,6 +31,16 @@
 public class ReflectionUtils {
 
     @NonNull
+    public static Method getMethod(@NonNull String className, @NonNull String name,
+            @Nullable Class<?>... params) throws ReflectionException {
+        try {
+            return getMethod(Class.forName(className), name, params);
+        } catch (ClassNotFoundException e) {
+            throw new ReflectionException(e);
+        }
+    }
+
+    @NonNull
     public static Method getMethod(@NonNull Class<?> clazz, @NonNull String name,
             @Nullable Class<?>... params) throws ReflectionException {
         try {
@@ -48,6 +59,19 @@
         return method;
     }
 
+    @NonNull
+    public static Object getFieldValue(@NonNull Class<?> clazz, Object object, @NonNull String name) throws ReflectionException {
+        try {
+            Field field = clazz.getDeclaredField(name);
+            field.setAccessible(true);
+            return field.get(object);
+        } catch (NoSuchFieldException e) {
+            throw new ReflectionException(e);
+        } catch (IllegalAccessException e) {
+            throw new ReflectionException(e);
+        }
+    }
+
     @Nullable
     public static Object invoke(@NonNull Method method, @Nullable Object object,
             @Nullable Object... args) throws ReflectionException {
@@ -60,6 +84,17 @@
         throw new ReflectionException(ex);
     }
 
+    @Nullable
+    public static Object invokeStatic(String className, String methodName, @Nullable Object... args)
+            throws ReflectionException {
+        try {
+            Method m = getMethod(Class.forName(className), methodName);
+            return invoke(m, null, args);
+        } catch (ClassNotFoundException e) {
+            throw new ReflectionException(e);
+        }
+    }
+
     /**
      * Check if the object is an instance of a class named {@code className}. This doesn't work
      * for interfaces.
@@ -86,16 +121,16 @@
 
     /**
      * Check if the object is an instance of any of the class named in {@code className} and
-     * returns the name of the parent class that matched. This doesn't work for interfaces.
+     * returns the parent class that matched. This doesn't work for interfaces.
      */
     @Nullable
-    public static String getParentClass(Object object, String[] classNames) {
-        Class superClass = object.getClass();
+    public static Class<?> getParentClass(Object object, String[] classNames) {
+        Class<?> superClass = object.getClass();
         while (superClass != null) {
             String name = superClass.getName();
             for (String className : classNames) {
                 if (name.equals(className)) {
-                    return className;
+                    return superClass;
                 }
             }
             superClass = superClass.getSuperclass();
@@ -103,6 +138,16 @@
         return null;
     }
 
+    /**
+     * Check if the object is an instance of any of the class named in {@code className} and
+     * returns the name of the parent class that matched. This doesn't work for interfaces.
+     */
+    @Nullable
+    public static String getParentClassName(Object object, String[] classNames) {
+        Class<?> superClass = getParentClass(object, classNames);
+        return superClass != null ? superClass.getName() : null;
+    }
+
     @NonNull
     public static Throwable getCause(@NonNull Throwable throwable) {
         Throwable cause = throwable.getCause();
diff --git a/common/src/com/android/tools/layoutlib/annotations/NonNull.java b/common/src/com/android/tools/layoutlib/annotations/NonNull.java
new file mode 100644
index 0000000..b48171b
--- /dev/null
+++ b/common/src/com/android/tools/layoutlib/annotations/NonNull.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Denotes a parameter or field can not be null.
+ * <p/>
+ * When decorating a method call parameter, this denotes the parameter can
+ * not be null.
+ * <p/>
+ * When decorating a method, this denotes the method can not return null.
+ * <p/>
+ * This is a marker annotation and it has no specific attributes.
+ */
+@Retention(RetentionPolicy.SOURCE)
+public @interface NonNull {
+}
diff --git a/common/src/com/android/tools/layoutlib/create/NativeConfig.java b/common/src/com/android/tools/layoutlib/create/NativeConfig.java
new file mode 100644
index 0000000..9c05df2
--- /dev/null
+++ b/common/src/com/android/tools/layoutlib/create/NativeConfig.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2019 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;
+
+/**
+ * Stores data needed for native JNI registration, and possibly the framework bytecode
+ * instrumentation.
+ */
+public class NativeConfig {
+
+    private NativeConfig() {}
+
+    public final static String[] DEFERRED_STATIC_INITIALIZER_CLASSES = new String [] {
+            "android.graphics.ColorSpace",
+            "android.graphics.FontFamily",
+            "android.graphics.Matrix",
+            "android.graphics.Path",
+            // Order is important! Fonts and FontFamily have to be initialized before Typeface
+            "android.graphics.fonts.Font",
+            "android.graphics.fonts.FontFamily$Builder",
+            "android.graphics.Typeface",
+            "android.graphics.text.PositionedGlyphs",
+            "android.graphics.text.LineBreaker",
+    };
+
+    public static final String[] DELEGATE_METHODS = new String[] {
+            "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
+            "android.content.res.AssetManager#createSystemAssetsInZygoteLocked",
+            "android.content.res.AssetManager#getAssignedPackageIdentifiers",
+            "android.content.res.AssetManager#nativeCreate",
+            "android.content.res.AssetManager#nativeDestroy",
+            "android.content.res.AssetManager#nativeThemeCreate",
+            "android.content.res.AssetManager#nativeGetThemeFreeFunction",
+            "android.content.res.Resources#getAnimation",
+            "android.content.res.Resources#getAttributeSetSourceResId",
+            "android.content.res.Resources#getBoolean",
+            "android.content.res.Resources#getColor",
+            "android.content.res.Resources#getColorStateList",
+            "android.content.res.Resources#getDimension",
+            "android.content.res.Resources#getDimensionPixelOffset",
+            "android.content.res.Resources#getDimensionPixelSize",
+            "android.content.res.Resources#getDrawable",
+            "android.content.res.Resources#getFloat",
+            "android.content.res.Resources#getFont",
+            "android.content.res.Resources#getIdentifier",
+            "android.content.res.Resources#getIntArray",
+            "android.content.res.Resources#getInteger",
+            "android.content.res.Resources#getLayout",
+            "android.content.res.Resources#getQuantityString",
+            "android.content.res.Resources#getQuantityText",
+            "android.content.res.Resources#getResourceEntryName",
+            "android.content.res.Resources#getResourceName",
+            "android.content.res.Resources#getResourcePackageName",
+            "android.content.res.Resources#getResourceTypeName",
+            "android.content.res.Resources#getString",
+            "android.content.res.Resources#getStringArray",
+            "android.content.res.Resources#getText",
+            "android.content.res.Resources#getTextArray",
+            "android.content.res.Resources#getValue",
+            "android.content.res.Resources#getValueForDensity",
+            "android.content.res.Resources#getXml",
+            "android.content.res.Resources#loadXmlResourceParser",
+            "android.content.res.Resources#obtainAttributes",
+            "android.content.res.Resources#openRawResource",
+            "android.content.res.Resources#openRawResourceFd",
+            "android.content.res.Resources#obtainTypedArray",
+            "android.content.res.Resources$Theme#obtainStyledAttributes",
+            "android.content.res.Resources$Theme#resolveAttribute",
+            "android.content.res.Resources$Theme#resolveAttributes",
+            "android.content.res.TypedArray#getValueAt",
+            "android.content.res.TypedArray#obtain",
+            "android.graphics.Canvas#getClipBounds",
+            "android.graphics.ImageDecoder#decodeBitmapImpl",
+            "android.graphics.Typeface#create",
+            "android.graphics.Typeface$Builder#createAssetUid",
+            "android.graphics.drawable.AdaptiveIconDrawable#<init>",
+            "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorUI#onDraw",
+            "android.graphics.drawable.AnimatedVectorDrawable#draw",
+            "android.graphics.drawable.DrawableInflater#inflateFromClass",
+            "android.graphics.drawable.NinePatchDrawable#getOpacity",
+            "android.graphics.fonts.Font$Builder#createBuffer",
+            "android.graphics.fonts.SystemFonts#getSystemFontConfigInternal",
+            "android.graphics.fonts.SystemFonts#mmap",
+            "android.os.Binder#getNativeBBinderHolder",
+            "android.os.Binder#getNativeFinalizer",
+            "android.os.Handler#sendMessageAtFrontOfQueue",
+            "android.os.Handler#sendMessageAtTime",
+            "android.os.HandlerThread#run",
+            "android.preference.Preference#getView",
+            "android.provider.DeviceConfig#getBoolean",
+            "android.provider.DeviceConfig#getFloat",
+            "android.provider.DeviceConfig#getInt",
+            "android.provider.DeviceConfig#getLong",
+            "android.provider.DeviceConfig#getString",
+            "android.text.format.DateFormat#is24HourFormat",
+            "android.util.Xml#newPullParser",
+            "android.view.Choreographer#getFrameTimeNanos",
+            "android.view.Choreographer#getRefreshRate",
+            "android.view.Choreographer#postCallbackDelayedInternal",
+            "android.view.Choreographer#removeCallbacksInternal",
+            "android.view.Display#getWindowManager",
+            "android.view.Display#updateDisplayInfoLocked",
+            "android.view.HandlerActionQueue#postDelayed",
+            "android.view.LayoutInflater#initPrecompiledViews",
+            "android.view.LayoutInflater#parseInclude",
+            "android.view.LayoutInflater#rInflate",
+            "android.view.MenuInflater#registerMenu",
+            "android.view.PointerIcon#loadResource",
+            "android.view.PointerIcon#registerDisplayListener",
+            "android.view.SurfaceControl#nativeCreateTransaction",
+            "android.view.SurfaceControl#nativeGetNativeTransactionFinalizer",
+            "android.view.TextureView#getTextureLayer",
+            "android.view.View#draw",
+            "android.view.View#dispatchDetachedFromWindow",
+            "android.view.View#getWindowToken",
+            "android.view.View#isInEditMode",
+            "android.view.View#layout",
+            "android.view.View#measure",
+            "android.view.ViewRootImpl#isInTouchMode",
+            "android.view.WindowManagerGlobal#getWindowManagerService",
+            "android.view.inputmethod.InputMethodManager#isInEditMode",
+            "android.widget.RemoteViews#getApplicationInfo",
+            "com.android.internal.util.XmlUtils#convertValueToInt",
+            "com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
+            "dalvik.system.VMRuntime#getNotifyNativeInterval",
+            "dalvik.system.VMRuntime#newUnpaddedArray",
+            "libcore.io.MemoryMappedFile#bigEndianIterator",
+            "libcore.io.MemoryMappedFile#close",
+            "libcore.io.MemoryMappedFile#mmapRO",
+            "libcore.util.NativeAllocationRegistry#applyFreeFunction",
+    };
+
+    public final static String[] DELEGATE_CLASS_NATIVES = new String[] {
+            "android.os.SystemClock",
+            "android.view.Display",
+            "libcore.icu.ICU",
+    };
+
+    /**
+     * The list of core classes to register with JNI
+     */
+    public final static String[] CORE_CLASS_NATIVES = new String[] {
+            "android.animation.PropertyValuesHolder",
+            "android.content.res.StringBlock",
+            "android.content.res.XmlBlock",
+            "android.media.ImageReader",
+            "android.media.PublicFormatUtils",
+            "android.os.SystemProperties",
+            "android.os.Trace",
+            "android.text.AndroidCharacter",
+            "android.util.Log",
+            "android.view.MotionEvent",
+            "android.view.Surface",
+            "com.android.internal.util.VirtualRefBasePtr",
+            "libcore.util.NativeAllocationRegistry_Delegate",
+    };
+
+    /**
+     * The list of graphics classes to register with JNI
+     */
+    public final static String[] GRAPHICS_CLASS_NATIVES = new String[] {
+            "android.graphics.Bitmap",
+            "android.graphics.BitmapFactory",
+            "android.graphics.ByteBufferStreamAdaptor",
+            "android.graphics.Camera",
+            "android.graphics.Canvas",
+            "android.graphics.CanvasProperty",
+            "android.graphics.ColorFilter",
+            "android.graphics.ColorSpace",
+            "android.graphics.CreateJavaOutputStreamAdaptor",
+            "android.graphics.DrawFilter",
+            "android.graphics.FontFamily",
+            "android.graphics.Graphics",
+            "android.graphics.HardwareRenderer",
+            "android.graphics.ImageDecoder",
+            "android.graphics.Interpolator",
+            "android.graphics.MaskFilter",
+            "android.graphics.Matrix",
+            "android.graphics.NinePatch",
+            "android.graphics.Paint",
+            "android.graphics.Path",
+            "android.graphics.PathEffect",
+            "android.graphics.PathMeasure",
+            "android.graphics.Picture",
+            "android.graphics.RecordingCanvas",
+            "android.graphics.Region",
+            "android.graphics.RenderEffect",
+            "android.graphics.RenderNode",
+            "android.graphics.Shader",
+            "android.graphics.Typeface",
+            "android.graphics.YuvImage",
+            "android.graphics.animation.NativeInterpolatorFactory",
+            "android.graphics.animation.RenderNodeAnimator",
+            "android.graphics.drawable.AnimatedVectorDrawable",
+            "android.graphics.drawable.VectorDrawable",
+            "android.graphics.fonts.Font",
+            "android.graphics.fonts.FontFamily",
+            "android.graphics.text.LineBreaker",
+            "android.graphics.text.MeasuredText",
+            "android.graphics.text.TextRunShaper",
+            "android.util.PathParser",
+    };
+}
diff --git a/create/Android.bp b/create/Android.bp
index 7cdea59..96622e9 100644
--- a/create/Android.bp
+++ b/create/Android.bp
@@ -29,5 +29,7 @@
         "asm-commons-9.2",
         "guava",
         "layoutlib-common",
+        "layoutlib_create-classpath",
+        "atf-prebuilt-371374941",
     ],
 }
diff --git a/create/create.iml b/create/create.iml
index f75fcd8..00abf5d 100644
--- a/create/create.iml
+++ b/create/create.iml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <module type="JAVA_MODULE" version="4">
-  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="true">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
     <exclude-output />
     <content url="file://$MODULE_DIR$">
       <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
@@ -36,5 +36,59 @@
     <orderEntry type="module" module-name="common" />
     <orderEntry type="library" name="guava" level="project" />
     <orderEntry type="library" scope="TEST" name="hamcrest" level="project" />
+    <orderEntry type="module-library" scope="RUNTIME">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/external/conscrypt/conscrypt/android_common/javac/conscrypt.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library" scope="RUNTIME">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/external/icu/android_icu4j/core-icu4j-for-host/android_common/withres/core-icu4j-for-host.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library" scope="RUNTIME">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/libcore/core-libart/android_common/javac/core-libart.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library" scope="RUNTIME">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/frameworks/base/framework-all/android_common/combined/framework-all.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library" scope="RUNTIME">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/frameworks/base/ext/android_common/withres/ext.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library" scope="RUNTIME">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/prebuilts/misc/common/atf/atf-prebuilt-jars-371374941/linux_glibc_common/combined/atf-prebuilt-jars-371374941.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
   </component>
 </module>
diff --git a/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
index b77708b..e5d1088 100644
--- a/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
+++ b/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
@@ -18,6 +18,7 @@
 
 import com.android.tools.layoutlib.annotations.NotNull;
 import com.android.tools.layoutlib.annotations.Nullable;
+import com.android.tools.layoutlib.create.ICreateInfo.MethodReplacer;
 
 import org.objectweb.asm.AnnotationVisitor;
 import org.objectweb.asm.Attribute;
@@ -43,10 +44,8 @@
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeMap;
-import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ForkJoinPool;
 import java.util.concurrent.ForkJoinTask;
-import java.util.concurrent.Future;
 import java.util.function.Consumer;
 import java.util.regex.Pattern;
 import java.util.zip.ZipEntry;
@@ -105,27 +104,28 @@
     private final String[] mIncludeFileGlobs;
     /** Internal names of classes that contain method calls that need to be rewritten. */
     private final Set<String> mReplaceMethodCallClasses = new HashSet<>();
+    /** Internal names of method calls that need to be rewritten. */
+    private final MethodReplacer[] mMethodReplacers;
 
     /**
      * Creates a new analyzer.
-     *
      * @param log The log output.
      * @param osJarPath The input source JARs to parse.
      * @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)
+*        ("*" does not matches dots whilst "**" does, "." and "$" are interpreted as-is)
      * @param includeFileGlobs Glob patterns of files which are kept as is. This is only for files
-     *        not ending in .class.
+     * @param methodReplacers names of method calls that need to be rewritten
      */
-    public AsmAnalyzer(Log log, List<String> osJarPath,
-            String[] deriveFrom, String[] includeGlobs, String[] excludedGlobs,
-            String[] includeFileGlobs) {
+    public AsmAnalyzer(Log log, List<String> osJarPath, String[] deriveFrom, String[] includeGlobs,
+            String[] excludedGlobs, String[] includeFileGlobs, MethodReplacer[] methodReplacers) {
         mLog = log;
         mOsSourceJar = osJarPath != null ? osJarPath : new ArrayList<>();
         mDeriveFrom = deriveFrom != null ? deriveFrom : new String[0];
         mIncludeGlobs = includeGlobs != null ? includeGlobs : new String[0];
         mExcludedGlobs = excludedGlobs != null ? excludedGlobs : new String[0];
         mIncludeFileGlobs = includeFileGlobs != null ? includeFileGlobs : new String[0];
+        mMethodReplacers = methodReplacers;
     }
 
     /**
@@ -488,7 +488,7 @@
             try {
                 // exclude classes that are part of the default JRE (the one executing this program)
                 if (className.startsWith("java.") || className.startsWith("sun.") ||
-                        getClass().getClassLoader().loadClass(className) != null) {
+                        getClass().getClassLoader().getParent().loadClass(className) != null) {
                     return;
                 }
             } catch (ClassNotFoundException e) {
@@ -773,6 +773,24 @@
                 // pass
             }
 
+            /**
+             * If a method some.package.Class.Method(args) is called from some.other.Class,
+             * @param owner some/package/Class
+             * @param name Method
+             * @param desc (args)returnType
+             * @param sourceClass some/other/Class
+             * @return if the method invocation needs to be replaced by some other class.
+             */
+            private boolean isReplacementNeeded(String owner, String name, String desc,
+                    String sourceClass) {
+                for (MethodReplacer replacer : mMethodReplacers) {
+                    if (replacer.isNeeded(owner, name, desc, sourceClass)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
             // instruction that invokes a method
             @Override
             public void visitMethodInsn(int opcode, String owner, String name, String desc,
@@ -785,7 +803,7 @@
 
 
                 // Check if method needs to replaced by a call to a different method.
-                if (ReplaceMethodCallsAdapter.isReplacementNeeded(owner, name, desc, mOwnerClass)) {
+                if (isReplacementNeeded(owner, name, desc, mOwnerClass)) {
                     mReplaceMethodCallClasses.add(mOwnerClass);
                 }
             }
diff --git a/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
index f468288..98055e3 100644
--- a/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
+++ b/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -18,6 +18,7 @@
 
 import com.android.tools.layoutlib.annotations.NotNull;
 import com.android.tools.layoutlib.create.AsmAnalyzer.Result;
+import com.android.tools.layoutlib.create.ICreateInfo.MethodReplacer;
 
 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.ClassVisitor;
@@ -52,6 +53,7 @@
     private Map<String, ClassReader> mKeep;
     /** All dependencies that must be completely stubbed. */
     private Map<String, ClassReader> mDeps;
+    private Map<String, ClassWriter> mDelegates = new HashMap<>();
     /** All files that are to be copied as-is. */
     private Map<String, InputStream> mCopyFiles;
     /** All classes where certain method calls need to be rewritten. */
@@ -77,8 +79,21 @@
     private final Map<String, ICreateInfo.InjectMethodRunnable> mInjectedMethodsMap;
     /** A map { FQCN => set { field names } } which should be promoted to public visibility */
     private final Map<String, Set<String>> mPromotedFields;
+    /** A map { FQCN => set { method names } } which should be promoted to public visibility */
+    private final Map<String, Set<String>> mPromotedMethods;
     /** A list of classes to be promoted to public visibility */
     private final Set<String> mPromotedClasses;
+    /** A set of classes for which NOT to delegate any native method */
+    private final Set<String> mKeepNativeClasses;
+
+    private final Set<String> mDelegateAllNative;
+    /** A set of classes for which to rename static initializers */
+    private Set<String> mRenameStaticInitializerClasses;
+
+    /** A Set of methods that should be intercepted and replaced **/
+    private final Set<MethodReplacer> mMethodReplacers;
+    private boolean mKeepAllNativeClasses;
+
 
     /**
      * Creates a new generator that can generate the output JAR with the stubbed classes.
@@ -183,10 +198,26 @@
         mPromotedFields = new HashMap<>();
         addToMap(createInfo.getPromotedFields(), mPromotedFields);
 
+        mPromotedMethods = new HashMap<>();
+        addToMap(createInfo.getPromotedMethods(), mPromotedMethods);
+
         mInjectedMethodsMap = createInfo.getInjectedMethodsMap();
 
         mPromotedClasses =
                 Arrays.stream(createInfo.getPromotedClasses()).collect(Collectors.toSet());
+
+        mKeepAllNativeClasses = createInfo.shouldKeepAllNativeClasses();
+
+        mKeepNativeClasses =
+                Arrays.stream(createInfo.getKeepClassNatives()).collect(Collectors.toSet());
+
+        mDelegateAllNative =
+                Arrays.stream(createInfo.getDelegateClassNativesToNatives()).collect(Collectors.toSet());
+
+        mMethodReplacers = Arrays.stream(createInfo.getMethodReplacers()).collect(Collectors.toSet());
+
+        mRenameStaticInitializerClasses =
+                Arrays.stream(createInfo.getDeferredStaticInitializerClasses()).collect(Collectors.toSet());
     }
 
     /**
@@ -259,6 +290,13 @@
             all.put(name, b);
         }
 
+        for (Entry<String, ClassWriter> entry : mDelegates.entrySet()) {
+            ClassWriter value = entry.getValue();
+            value.visitEnd();
+            String name = classNameToEntryPath(entry.getKey());
+            all.put(name, value.toByteArray());
+        }
+
         for (Entry<String, InputStream> entry : mCopyFiles.entrySet()) {
             try {
                 byte[] b = inputStreamToByteArray(entry.getValue());
@@ -329,12 +367,12 @@
 
         // Rewrite the new class from scratch, without reusing the constant pool from the
         // original class reader.
-        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 
         ClassVisitor cv = cw;
 
         if (mReplaceMethodCallsClasses.contains(className)) {
-            cv = new ReplaceMethodCallsAdapter(cv, className);
+            cv = new ReplaceMethodCallsAdapter(mMethodReplacers, cv, className);
         }
 
         cv = new RefactorClassAdapter(cv, mRefactorClasses);
@@ -346,11 +384,21 @@
         if (mInjectedMethodsMap.keySet().contains(binaryNewName)) {
             cv = new InjectMethodsAdapter(cv, mInjectedMethodsMap.get(binaryNewName));
         }
-        cv = StubClassAdapter.builder(mLog, cv)
-                .withDeleteReturns(mDeleteReturns.get(className))
-                .withNewClassName(newName)
-                .useOnlyStubNative(stubNativesOnly)
-                .build();
+
+        if (mDelegateAllNative.contains(binaryNewName)) {
+            Set<String> delegateMethods = mDelegateMethods.remove(className);
+            if (delegateMethods != null && !delegateMethods.isEmpty()) {
+                cv = new DelegateClassAdapter(mLog, cv, className, delegateMethods);
+            }
+            cv = new DelegateToNativeAdapter(mLog, cv, className, mDelegates, delegateMethods);
+        }
+        else if (!mKeepAllNativeClasses && !mKeepNativeClasses.contains(binaryNewName)) {
+            cv = StubClassAdapter.builder(mLog, cv)
+                    .withDeleteReturns(mDeleteReturns.get(className))
+                    .withNewClassName(newName)
+                    .useOnlyStubNative(stubNativesOnly)
+                    .build();
+        }
 
         Set<String> delegateMethods = mDelegateMethods.get(className);
         if (delegateMethods != null && !delegateMethods.isEmpty()) {
@@ -367,10 +415,18 @@
         if (promoteFields != null && !promoteFields.isEmpty()) {
             cv = new PromoteFieldClassAdapter(cv, promoteFields);
         }
+        Set<String> promoteMethods = mPromotedMethods.get(className);
+        if (promoteMethods != null && !promoteMethods.isEmpty()) {
+            cv = new PromoteMethodClassAdapter(cv, promoteMethods);
+        }
         if (!mPromotedClasses.isEmpty()) {
             cv = new PromoteClassClassAdapter(cv, mPromotedClasses);
         }
 
+        if (mRenameStaticInitializerClasses.contains(binaryNewName)) {
+            cv = new DeferStaticInitializerClassAdapter(cv);
+        }
+
         // Make sure no class file has a version above 55 (corresponding to Java 11),
         // so that layoutlib can be run with JDK 11.
         cv = new ChangeFileVersionAdapter(mLog, 55, cv);
diff --git a/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 44046eb..66a22d1 100644
--- a/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -18,13 +18,23 @@
 
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 import com.android.tools.layoutlib.java.LinkedHashMap_Delegate;
-import com.android.tools.layoutlib.java.System_Delegate;
+import com.android.tools.layoutlib.java.NioUtils_Delegate;
+import com.android.tools.layoutlib.java.Reference_Delegate;
 
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.text.DateFormat;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.zip.ZipEntry;
 
 /**
  * Describes the work to be done by {@link AsmGenerator}.
@@ -32,6 +42,11 @@
 public final class CreateInfo implements ICreateInfo {
 
     @Override
+    public MethodReplacer[] getMethodReplacers() {
+        return METHOD_REPLACERS;
+    }
+
+    @Override
     public Class<?>[] getInjectedClasses() {
         return INJECTED_CLASSES;
     }
@@ -47,6 +62,21 @@
     }
 
     @Override
+    public String[] getDelegateClassNativesToNatives() {
+        return DELEGATE_CLASS_NATIVES_TO_NATIVES;
+    }
+
+    @Override
+    public boolean shouldKeepAllNativeClasses() {
+        return false;
+    }
+
+    @Override
+    public String[] getKeepClassNatives() {
+        return KEEP_CLASS_NATIVES;
+    }
+
+    @Override
     public String[] getRenamedClasses() {
         return RENAMED_CLASSES;
     }
@@ -84,6 +114,11 @@
     }
 
     @Override
+    public String[] getPromotedMethods() {
+        return PROMOTED_METHODS;
+    }
+
+    @Override
     public String[] getPromotedClasses() {
         return PROMOTED_CLASSES;
     }
@@ -93,8 +128,31 @@
         return INJECTED_METHODS;
     }
 
+    @Override
+    public String[] getDeferredStaticInitializerClasses() {
+        return DEFERRED_STATIC_INITIALIZER_CLASSES;
+    }
+
     //-----
 
+    private static final MethodReplacer[] METHOD_REPLACERS = new MethodReplacer[] {
+        new SystemLoadLibraryReplacer(),
+        new SystemArrayCopyReplacer(),
+        new LocaleGetDefaultReplacer(),
+        new LocaleAdjustLanguageCodeReplacer(),
+        new SystemLogReplacer(),
+        new SystemNanoTimeReplacer(),
+        new SystemCurrentTimeMillisReplacer(),
+        new LinkedHashMapEldestReplacer(),
+        new ContextGetClassLoaderReplacer(),
+        new ImageReaderNativeInitReplacer(),
+        new NioUtilsFreeBufferReplacer(),
+        new ProcessInitializerInitSchedReplacer(),
+        new NativeInitPathReplacer(),
+        new ActivityThreadInAnimationReplacer(),
+        new ReferenceRefersToReplacer(),
+    };
+
     /**
      * The list of class from layoutlib_create to inject in layoutlib.
      */
@@ -108,160 +166,41 @@
             InjectMethodRunnable.class,
             InjectMethodRunnables.class,
             /* Java package classes */
-            System_Delegate.class,
             LinkedHashMap_Delegate.class,
+            NioUtils_Delegate.class,
+            Reference_Delegate.class,
         };
 
     /**
      * The list of methods to rewrite as delegates.
      */
-    public final static String[] DELEGATE_METHODS = new String[] {
-        "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
-        "android.content.res.Resources#getAnimation",
-        "android.content.res.Resources#getAttributeSetSourceResId",
-        "android.content.res.Resources#getBoolean",
-        "android.content.res.Resources#getColor",
-        "android.content.res.Resources#getColorStateList",
-        "android.content.res.Resources#getDimension",
-        "android.content.res.Resources#getDimensionPixelOffset",
-        "android.content.res.Resources#getDimensionPixelSize",
-        "android.content.res.Resources#getDrawable",
-        "android.content.res.Resources#getFloat",
-        "android.content.res.Resources#getFont",
-        "android.content.res.Resources#getIdentifier",
-        "android.content.res.Resources#getIntArray",
-        "android.content.res.Resources#getInteger",
-        "android.content.res.Resources#getLayout",
-        "android.content.res.Resources#getQuantityString",
-        "android.content.res.Resources#getQuantityText",
-        "android.content.res.Resources#getResourceEntryName",
-        "android.content.res.Resources#getResourceName",
-        "android.content.res.Resources#getResourcePackageName",
-        "android.content.res.Resources#getResourceTypeName",
-        "android.content.res.Resources#getString",
-        "android.content.res.Resources#getStringArray",
-        "android.content.res.Resources#getText",
-        "android.content.res.Resources#getTextArray",
-        "android.content.res.Resources#getValue",
-        "android.content.res.Resources#getValueForDensity",
-        "android.content.res.Resources#getXml",
-        "android.content.res.Resources#loadXmlResourceParser",
-        "android.content.res.Resources#obtainAttributes",
-        "android.content.res.Resources#obtainTypedArray",
-        "android.content.res.Resources#openRawResource",
-        "android.content.res.Resources#openRawResourceFd",
-        "android.content.res.Resources$Theme#obtainStyledAttributes",
-        "android.content.res.Resources$Theme#resolveAttribute",
-        "android.content.res.Resources$Theme#resolveAttributes",
-        "android.content.res.AssetManager#nativeCreate",
-        "android.content.res.AssetManager#nativeDestroy",
-        "android.content.res.AssetManager#nativeThemeCreate",
-        "android.content.res.AssetManager#nativeThemeDestroy",
-        "android.content.res.AssetManager#getAssignedPackageIdentifiers",
-        "android.content.res.AssetManager#nativeCreateIdmapsForStaticOverlaysTargetingAndroid",
-        "android.content.res.TypedArray#getValueAt",
-        "android.content.res.TypedArray#obtain",
-        "android.graphics.BitmapFactory#finishDecode",
-        "android.graphics.BitmapFactory#setDensityFromOptions",
-        "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#useLastSeenTarget",
-        "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#onDraw",
-        "android.graphics.drawable.GradientDrawable#buildRing",
-        "android.graphics.drawable.AdaptiveIconDrawable#<init>",
-        "android.graphics.drawable.DrawableInflater#inflateFromClass",
-        "android.graphics.drawable.NinePatchDrawable#getOpacity",
-        "android.graphics.FontFamily#addFont",
-        "com.google.android.apps.common.testing.accessibility.framework.uielement" +
-                ".AccessibilityHierarchyAndroid$ViewElementClassNamesAndroid#getClassByName",
-        "android.graphics.Typeface#create",
-        "android.graphics.Typeface$Builder#createAssetUid",
-        "android.graphics.fonts.Font$Builder#createBuffer",
-        "android.graphics.fonts.SystemFonts#getSystemFontConfigInternal",
-        "android.os.Binder#getNativeBBinderHolder",
-        "android.os.Binder#getNativeFinalizer",
-        "android.os.Handler#sendMessageAtTime",
-        "android.os.HandlerThread#run",
-        "android.preference.Preference#getView",
-        "android.text.format.DateFormat#is24HourFormat",
-        "android.util.Xml#newPullParser",
-        "android.view.Choreographer#getInstance",
-        "android.view.Choreographer#getRefreshRate",
-        "android.view.Choreographer#scheduleVsyncLocked",
-        "android.view.Display#updateDisplayInfoLocked",
-        "android.view.Display#getWindowManager",
-        "android.view.HandlerActionQueue#postDelayed",
-        "android.view.LayoutInflater#initPrecompiledViews",
-        "android.view.LayoutInflater#rInflate",
-        "android.view.LayoutInflater#parseInclude",
-        "android.view.View#draw",
-        "android.view.View#dispatchDetachedFromWindow",
-        "android.view.View#layout",
-        "android.view.View#measure",
-        "android.view.View#getWindowToken",
-        "android.view.View#isInEditMode",
-        "android.view.ViewRootImpl#isInTouchMode",
-        "android.view.WindowManagerGlobal#getWindowManagerService",
-        "android.view.inputmethod.InputMethodManager#isInEditMode",
-        "android.view.MenuInflater#registerMenu",
-        "android.graphics.RenderNode#getMatrix",
-        "android.graphics.RenderNode#nCreate",
-        "android.graphics.RenderNode#nGetNativeFinalizer",
-        "android.graphics.RenderNode#nSetElevation",
-        "android.graphics.RenderNode#nGetElevation",
-        "android.graphics.RenderNode#nSetTranslationX",
-        "android.graphics.RenderNode#nGetTranslationX",
-        "android.graphics.RenderNode#nSetTranslationY",
-        "android.graphics.RenderNode#nGetTranslationY",
-        "android.graphics.RenderNode#nSetTranslationZ",
-        "android.graphics.RenderNode#nGetTranslationZ",
-        "android.graphics.RenderNode#nSetRotation",
-        "android.graphics.RenderNode#nGetRotation",
-        "android.graphics.RenderNode#nSetLeft",
-        "android.graphics.RenderNode#nSetTop",
-        "android.graphics.RenderNode#nSetRight",
-        "android.graphics.RenderNode#nSetBottom",
-        "android.graphics.RenderNode#nSetLeftTopRightBottom",
-        "android.graphics.RenderNode#nSetPivotX",
-        "android.graphics.RenderNode#nGetPivotX",
-        "android.graphics.RenderNode#nSetPivotY",
-        "android.graphics.RenderNode#nGetPivotY",
-        "android.graphics.RenderNode#nSetScaleX",
-        "android.graphics.RenderNode#nGetScaleX",
-        "android.graphics.RenderNode#nSetScaleY",
-        "android.graphics.RenderNode#nGetScaleY",
-        "android.graphics.RenderNode#nIsPivotExplicitlySet",
-        "android.provider.DeviceConfig#getBoolean",
-        "android.provider.DeviceConfig#getFloat",
-        "android.provider.DeviceConfig#getInt",
-        "android.provider.DeviceConfig#getLong",
-        "android.provider.DeviceConfig#getString",
-        "android.view.PointerIcon#loadResource",
-        "android.view.PointerIcon#registerDisplayListener",
-        "android.view.SurfaceControl#nativeCreateTransaction",
-        "android.view.SurfaceControl#nativeGetNativeTransactionFinalizer",
-        "android.view.ViewGroup#drawChild",
-        "com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
-        "com.android.internal.util.XmlUtils#convertValueToInt",
-        "dalvik.system.VMRuntime#getNotifyNativeInterval",
-        "dalvik.system.VMRuntime#newUnpaddedArray",
-        "libcore.io.MemoryMappedFile#mmapRO",
-        "libcore.io.MemoryMappedFile#close",
-        "libcore.io.MemoryMappedFile#bigEndianIterator",
-        "libcore.util.NativeAllocationRegistry#applyFreeFunction",
-        "libcore.util.NativeAllocationRegistry#registerNativeAllocation",
-    };
+    public final static String[] DELEGATE_METHODS = NativeConfig.DELEGATE_METHODS;
 
     /**
      * The list of classes on which to delegate all native methods.
      */
-    public final static String[] DELEGATE_CLASS_NATIVES = new String[] {
+    public final static String[] DELEGATE_CLASS_NATIVES = NativeConfig.DELEGATE_CLASS_NATIVES;
+
+    public final static String[] DELEGATE_CLASS_NATIVES_TO_NATIVES = new String[] {};
+
+    /**
+     * The list of classes on which NOT to delegate any native method.
+     */
+    public final static String[] KEEP_CLASS_NATIVES = new String[] {
         "android.animation.PropertyValuesHolder",
+        "android.content.res.StringBlock",
+        "android.content.res.XmlBlock",
         "android.graphics.BaseCanvas",
+        "android.graphics.BaseRecordingCanvas",
         "android.graphics.Bitmap",
         "android.graphics.BitmapFactory",
         "android.graphics.BitmapShader",
         "android.graphics.BlendModeColorFilter",
         "android.graphics.BlurMaskFilter",
+        "android.graphics.BlurShader",
+        "android.graphics.Camera",
         "android.graphics.Canvas",
+        "android.graphics.CanvasProperty",
         "android.graphics.Color",
         "android.graphics.ColorFilter",
         "android.graphics.ColorMatrixColorFilter",
@@ -274,6 +213,9 @@
         "android.graphics.DrawFilter",
         "android.graphics.EmbossMaskFilter",
         "android.graphics.FontFamily",
+        "android.graphics.HardwareRenderer",
+        "android.graphics.ImageDecoder",
+        "android.graphics.Interpolator",
         "android.graphics.LightingColorFilter",
         "android.graphics.LinearGradient",
         "android.graphics.MaskFilter",
@@ -285,27 +227,47 @@
         "android.graphics.PathDashPathEffect",
         "android.graphics.PathEffect",
         "android.graphics.PathMeasure",
+        "android.graphics.Picture",
         "android.graphics.PorterDuffColorFilter",
         "android.graphics.RadialGradient",
+        "android.graphics.RecordingCanvas",
         "android.graphics.Region",
+        "android.graphics.RegionIterator",
+        "android.graphics.RenderEffect",
+        "android.graphics.RenderNode",
+        "android.graphics.RuntimeShader",
         "android.graphics.Shader",
         "android.graphics.SumPathEffect",
         "android.graphics.SweepGradient",
+        "android.graphics.TableMaskFilter",
         "android.graphics.Typeface",
+        "android.graphics.YuvImage",
         "android.graphics.animation.NativeInterpolatorFactory",
+        "android.graphics.animation.RenderNodeAnimator",
         "android.graphics.drawable.AnimatedVectorDrawable",
         "android.graphics.drawable.VectorDrawable",
+        "android.graphics.fonts.Font",
         "android.graphics.fonts.Font$Builder",
+        "android.graphics.fonts.FontFamily",
         "android.graphics.fonts.FontFamily$Builder",
+        "android.graphics.fonts.FontFileUtil",
+        "android.graphics.fonts.SystemFonts",
+        "android.graphics.text.PositionedGlyphs",
+        "android.graphics.text.LineBreaker",
         "android.graphics.text.MeasuredText",
         "android.graphics.text.MeasuredText$Builder",
-        "android.graphics.text.LineBreaker",
-        "android.os.SystemClock",
+        "android.graphics.text.TextRunShaper",
+        "android.media.ImageReader",
+        "android.media.ImageReader$SurfaceImage",
+        "android.media.PublicFormatUtils",
         "android.os.SystemProperties",
+        "android.os.Trace",
+        "android.text.AndroidCharacter",
+        "android.util.Log",
         "android.util.PathParser",
-        "android.view.Display",
+        "android.view.MotionEvent",
+        "android.view.Surface",
         "com.android.internal.util.VirtualRefBasePtr",
-        "libcore.icu.ICU",
     };
 
     /**
@@ -321,7 +283,6 @@
             "android.view.accessibility.AccessibilityManager", "android.view.accessibility._Original_AccessibilityManager",
             "android.view.accessibility.AccessibilityNodeIdManager", "android.view.accessibility._Original_AccessibilityNodeIdManager",
             "android.webkit.WebView",                          "android.webkit._Original_WebView",
-            "android.graphics.ImageDecoder",                   "android.graphics._Original_ImageDecoder",
         };
 
     /**
@@ -360,19 +321,43 @@
      * needed when access from the delegate classes is needed.
      */
     private final static String[] PROMOTED_FIELDS = new String[] {
-        "android.graphics.drawable.VectorDrawable#mVectorState",
-        "android.view.Choreographer#mLastFrameTimeNanos",
-        "android.graphics.FontFamily#mBuilderPtr",
-        "android.graphics.Typeface#DEFAULT_FAMILY",
-        "android.graphics.Typeface#sDynamicTypefaceCache",
-        "android.graphics.drawable.AdaptiveIconDrawable#sMask",
-        "android.graphics.drawable.DrawableInflater#mRes",
+        "android.animation.AnimationHandler#mDelayedCallbackStartTime",
+        "android.animation.AnimationHandler#mAnimationCallbacks",
+        "android.animation.AnimationHandler#mCommitCallbacks",
+        "android.animation.AnimatorSet#mLastFrameTime",
         "android.animation.PropertyValuesHolder#sSetterPropertyMap",
         "android.animation.PropertyValuesHolder#sGetterPropertyMap",
         "android.animation.PropertyValuesHolder$IntPropertyValuesHolder#sJNISetterPropertyMap",
         "android.animation.PropertyValuesHolder$FloatPropertyValuesHolder#sJNISetterPropertyMap",
         "android.animation.PropertyValuesHolder$MultiFloatValuesHolder#sJNISetterPropertyMap",
         "android.animation.PropertyValuesHolder$MultiIntValuesHolder#sJNISetterPropertyMap",
+        "android.graphics.ImageDecoder$InputStreamSource#mInputStream",
+        "android.graphics.Typeface#DEFAULT_FAMILY",
+        "android.graphics.Typeface#sDynamicTypefaceCache",
+        "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorUI#mSet",
+        "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#mPendingAnimationActions",
+        "android.graphics.drawable.AnimatedVectorDrawable#mAnimatorSet",
+        "android.graphics.drawable.AdaptiveIconDrawable#sMask",
+        "android.graphics.drawable.DrawableInflater#mRes",
+        "android.view.Choreographer#mCallbackQueues", // required for tests only
+        "android.view.Choreographer$CallbackQueue#mHead", // required for tests only
+        "com.android.internal.util.ArrayUtils#sCache",
+    };
+
+    /**
+     * List of methods for which we will update the visibility to be public.
+     */
+    private final static String[] PROMOTED_METHODS = new String[] {
+        "android.animation.AnimationHandler#doAnimationFrame",
+        "android.content.res.StringBlock#addParagraphSpan",
+        "android.content.res.StringBlock#getColor",
+        "android.graphics.Bitmap#setNinePatchChunk",
+        "android.graphics.Path#nInit",
+        "android.media.ImageReader#nativeClassInit",
+        "android.view.Choreographer#doFrame",
+        "android.view.Choreographer#postCallbackDelayedInternal",
+        "android.view.Choreographer#removeCallbacksInternal",
+        "android.view.ViewRootImpl#getRootMeasureSpec",
     };
 
     /**
@@ -380,6 +365,11 @@
      * if possible.
      */
     private final static String[] PROMOTED_CLASSES = new String[] {
+        "android.content.res.StringBlock$Height",
+        "android.graphics.ImageDecoder$InputStreamSource",
+        "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorUI",
+        "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimator",
+        "android.view.Choreographer$CallbackQueue", // required for tests only
     };
 
     /**
@@ -392,9 +382,293 @@
         new String[] {
             null };                         // separator, for next class/methods list.
 
+    private final static String[] DEFERRED_STATIC_INITIALIZER_CLASSES =
+            NativeConfig.DEFERRED_STATIC_INITIALIZER_CLASSES;
+
     private final static Map<String, InjectMethodRunnable> INJECTED_METHODS =
             new HashMap<String, InjectMethodRunnable>(1) {{
                 put("android.content.Context",
                         InjectMethodRunnables.CONTEXT_GET_FRAMEWORK_CLASS_LOADER);
             }};
+
+    public static class LinkedHashMapEldestReplacer implements MethodReplacer {
+
+        private final String VOID_TO_MAP_ENTRY =
+                Type.getMethodDescriptor(Type.getType(Map.Entry.class));
+        private final String LINKED_HASH_MAP = Type.getInternalName(LinkedHashMap.class);
+
+        @Override
+        public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+            return LINKED_HASH_MAP.equals(owner) &&
+                    "eldest".equals(name) &&
+                    VOID_TO_MAP_ENTRY.equals(desc);
+        }
+
+        @Override
+        public void replace(MethodInformation mi) {
+            mi.opcode = Opcodes.INVOKESTATIC;
+            mi.owner = Type.getInternalName(LinkedHashMap_Delegate.class);
+            mi.desc = Type.getMethodDescriptor(
+                    Type.getType(Map.Entry.class), Type.getType(LinkedHashMap.class));
+        }
+    }
+
+    private static class ContextGetClassLoaderReplacer implements MethodReplacer {
+        // When LayoutInflater asks for a class loader, we must return the class loader that
+        // cannot return app's custom views/classes. This is so that in case of any failure
+        // or exception when instantiating the views, the IDE can replace it with a mock view
+        // and have proper error handling. However, if a custom view asks for the class
+        // loader, we must return a class loader that can find app's custom views as well.
+        // Thus, we rewrite the call to get class loader in LayoutInflater to
+        // getFrameworkClassLoader and inject a new method in Context. This leaves the normal
+        // method: Context.getClassLoader() free to be used by the apps.
+        private final String VOID_TO_CLASS_LOADER =
+                Type.getMethodDescriptor(Type.getType(ClassLoader.class));
+
+        @Override
+        public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+            return owner.equals("android/content/Context") &&
+                    sourceClass.equals("android/view/LayoutInflater") &&
+                    name.equals("getClassLoader") &&
+                    desc.equals(VOID_TO_CLASS_LOADER);
+        }
+
+        @Override
+        public void replace(MethodInformation mi) {
+            mi.name = "getFrameworkClassLoader";
+        }
+    }
+
+    private static class SystemCurrentTimeMillisReplacer implements MethodReplacer {
+        @Override
+        public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+            return Type.getInternalName(System.class).equals(owner) && name.equals("currentTimeMillis");
+        }
+
+        @Override
+        public void replace(MethodInformation mi) {
+            mi.name = "currentTimeMillis";
+            mi.owner = "com/android/internal/lang/System_Delegate";
+        }
+    }
+
+    private static class SystemNanoTimeReplacer implements MethodReplacer {
+        @Override
+        public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+            return Type.getInternalName(System.class).equals(owner) && name.equals("nanoTime");
+        }
+
+        @Override
+        public void replace(MethodInformation mi) {
+            mi.name = "nanoTime";
+            mi.owner = "com/android/internal/lang/System_Delegate";
+        }
+    }
+
+    public static class SystemLogReplacer implements MethodReplacer {
+        @Override
+        public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+            return Type.getInternalName(System.class).equals(owner) && name.length() == 4
+                    && name.startsWith("log");
+        }
+
+        @Override
+        public void replace(MethodInformation mi) {
+            assert mi.desc.equals("(Ljava/lang/String;Ljava/lang/Throwable;)V")
+                    || mi.desc.equals("(Ljava/lang/String;)V");
+            mi.name = "log";
+            mi.owner = "com/android/internal/lang/System_Delegate";
+        }
+    }
+
+    /**
+     * Platform code should not loadLibrary on its own. Layoutlib loading infrastructure takes case
+     * of loading all the necessary native libraries (having the right paths etc.)
+     */
+    public static class SystemLoadLibraryReplacer implements MethodReplacer {
+        @Override
+        public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+            return Type.getInternalName(System.class).equals(owner) && name.equals("loadLibrary");
+        }
+
+        @Override
+        public void replace(MethodInformation mi) {
+            mi.owner = "com/android/internal/lang/System_Delegate";
+        }
+    }
+
+    /**
+     * This is to replace a static call to a dummy, so that ImageReader can be loaded and accessed
+     * during JNI loading
+     */
+    public static class ImageReaderNativeInitReplacer implements MethodReplacer {
+        @Override
+        public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+            return "android/media/ImageReader".equals(owner) && name.equals("nativeClassInit");
+        }
+
+        @Override
+        public void replace(MethodInformation mi) {
+            mi.owner = "android/media/ImageReader_Delegate";
+            mi.opcode = Opcodes.INVOKESTATIC;
+        }
+    }
+
+    private static class LocaleGetDefaultReplacer implements MethodReplacer {
+
+        @Override
+        public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+            return Type.getInternalName(Locale.class).equals(owner)
+                    && "getDefault".equals(name)
+                    && desc.equals(Type.getMethodDescriptor(Type.getType(Locale.class)));
+        }
+
+        @Override
+        public void replace(MethodInformation mi) {
+            mi.owner = "com/android/layoutlib/bridge/android/AndroidLocale";
+        }
+    }
+
+    public static class LocaleAdjustLanguageCodeReplacer implements MethodReplacer {
+
+        @Override
+        public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+            return Type.getInternalName(java.util.Locale.class).equals(owner)
+                    && ("adjustLanguageCode".equals(name)
+                    && desc.equals(Type.getMethodDescriptor(Type.getType(String.class), Type.getType(String.class))));
+        }
+
+        @Override
+        public void replace(MethodInformation mi) {
+            mi.owner = "com/android/tools/layoutlib/java/util/LocaleAdjustLanguageCodeReplacement";
+        }
+    }
+
+    private static class SystemArrayCopyReplacer implements MethodReplacer {
+        /**
+         * Descriptors for specialized versions {@link System#arraycopy} that are not present on the
+         * Desktop VM.
+         */
+        private static Set<String> ARRAYCOPY_DESCRIPTORS = new HashSet<>(Arrays.asList(
+                "([CI[CII)V", "([BI[BII)V", "([SI[SII)V", "([II[III)V",
+                "([JI[JII)V", "([FI[FII)V", "([DI[DII)V", "([ZI[ZII)V"));
+
+        @Override
+        public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+            return Type.getInternalName(System.class).equals(owner) && "arraycopy".equals(name) &&
+                    ARRAYCOPY_DESCRIPTORS.contains(desc);
+        }
+
+        @Override
+        public void replace(MethodInformation mi) {
+            mi.desc = "(Ljava/lang/Object;ILjava/lang/Object;II)V";
+        }
+    }
+
+    public static class DateFormatSet24HourTimePrefReplacer implements MethodReplacer {
+
+        @Override
+        public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+            return Type.getInternalName(DateFormat.class).equals(owner) &&
+                    "set24HourTimePref".equals(name);
+        }
+
+        @Override
+        public void replace(MethodInformation mi) {
+            mi.owner = "com/android/tools/layoutlib/java/text/DateFormat_Delegate";
+        }
+    }
+
+    /**
+     * Replace references to ZipEntry.getDataOffset with a delegate, since it does not exist in the JDK.
+     * @see {@link com.android.tools.layoutlib.java.util.zip.ZipEntry_Delegate#getDataOffset(ZipEntry)}
+     */
+    public static class ZipEntryGetDataOffsetReplacer implements MethodReplacer {
+        @Override
+        public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+            return Type.getInternalName(ZipEntry.class).equals(owner)
+                    && "getDataOffset".equals(name);
+        }
+
+        @Override
+        public void replace(MethodInformation mi) {
+            mi.opcode = Opcodes.INVOKESTATIC;
+            mi.owner = "com/android/tools/layoutlib/java/util/zip/ZipEntry_Delegate";
+            mi.desc = Type.getMethodDescriptor(
+                    Type.getType(long.class), Type.getType(ZipEntry.class));
+        }
+    }
+
+    public static class NioUtilsFreeBufferReplacer implements MethodReplacer {
+        @Override
+        public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+            return "java/nio/NioUtils".equals(owner) && name.equals("freeDirectBuffer");
+        }
+
+        @Override
+        public void replace(MethodInformation mi) {
+            mi.owner = Type.getInternalName(NioUtils_Delegate.class);
+        }
+    }
+
+    public static class ProcessInitializerInitSchedReplacer implements MethodReplacer {
+        @Override
+        public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+            return "android/graphics/HardwareRenderer$ProcessInitializer".equals(owner) &&
+                    name.equals("initSched");
+        }
+
+        @Override
+        public void replace(MethodInformation mi) {
+            mi.owner = "android/graphics/HardwareRenderer_ProcessInitializer_Delegate";
+            mi.opcode = Opcodes.INVOKESTATIC;
+            mi.desc = "(J)V";
+        }
+    }
+
+    public static class NativeInitPathReplacer implements MethodReplacer {
+        @Override
+        public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+            return "android/graphics/Path".equals(owner) &&
+                    "nInit".equals(name) && "(J)J".equals(desc);
+        }
+
+        @Override
+        public void replace(MethodInformation mi) {
+            mi.owner = "android/graphics/Path_Delegate";
+            mi.opcode = Opcodes.INVOKESTATIC;
+        }
+    }
+
+    public static class ActivityThreadInAnimationReplacer implements MethodReplacer {
+        @Override
+        public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+            return ("android/app/ActivityThread").equals(owner) &&
+                    name.equals("getSystemUiContext") &&
+                    sourceClass.equals("android/view/animation/Animation");
+        }
+
+        @Override
+        public void replace(MethodInformation mi) {
+            mi.owner = "android/app/ActivityThread_Delegate";
+            mi.opcode = Opcodes.INVOKESTATIC;
+            mi.desc = "()Landroid/content/Context;";
+        }
+    }
+
+    public static class ReferenceRefersToReplacer implements MethodReplacer {
+        @Override
+        public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+            return Type.getInternalName(WeakReference.class).equals(owner) &&
+                    "refersTo".equals(name);
+        }
+
+        @Override
+        public void replace(MethodInformation mi) {
+            mi.opcode = Opcodes.INVOKESTATIC;
+            mi.owner = Type.getInternalName(Reference_Delegate.class);
+            mi.desc = Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Reference.class),
+                    Type.getType(Object.class));
+        }
+    }
 }
diff --git a/create/src/com/android/tools/layoutlib/create/DeferStaticInitializerClassAdapter.java b/create/src/com/android/tools/layoutlib/create/DeferStaticInitializerClassAdapter.java
new file mode 100644
index 0000000..b5c331d
--- /dev/null
+++ b/create/src/com/android/tools/layoutlib/create/DeferStaticInitializerClassAdapter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.layoutlib.create;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * Renames the static initializer to a public deferredStaticInitializer method.
+ */
+public class DeferStaticInitializerClassAdapter extends ClassVisitor {
+
+    public DeferStaticInitializerClassAdapter(ClassVisitor cv) {
+        super(Main.ASM_VERSION, cv);
+    }
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+            String[] exceptions) {
+       if (name.equals("<clinit>")) {
+           name = "deferredStaticInitializer";
+           access |= Modifier.PUBLIC;
+        }
+        return super.visitMethod(access, name, desc, signature, exceptions);
+    }
+
+    @Override
+    public FieldVisitor visitField(int access, String name, String desc, String signature,
+            Object value) {
+        // Java 9 does not allow static final field to be modified outside of <clinit>.
+        // So if a field is static, it has to be non-final.
+        if ((access & Opcodes.ACC_STATIC) != 0 ) {
+            access = access & ~Opcodes.ACC_FINAL;;
+        }
+        return super.visitField(access, name, desc, signature, value);
+    }
+}
diff --git a/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java b/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
index f9c7eb3..ac24170 100644
--- a/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
+++ b/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
@@ -99,11 +99,20 @@
         }
 
         if (CLASS_INIT.equals(name)) {
-            // We don't currently support generating delegates for constructors.
-            throw new UnsupportedOperationException(
-                String.format(
-                    "Delegate doesn't support overriding static constructor %1$s:%2$s(%3$s)",
-                    mClassName, name, desc));
+            access = access & ~Opcodes.ACC_PRIVATE;  // make the replacement method package protected.
+
+            // writer for moving the original code to a 'SomeClass.staticInit_Original' method
+            MethodVisitor renamedMethodWriter = super.visitMethod(access,
+                    "staticInit" + ORIGINAL_SUFFIX,
+                    desc, signature, exceptions);
+            // writer for writing the SomeClass.clinit method with a single call to
+            // SomeClass_Delegate.staticInit
+            MethodVisitor originalMethodWriter = super.visitMethod(access, name,
+                    desc, signature, exceptions);
+
+            return new StaticInitMethodAdapter(mLog, renamedMethodWriter,
+                    originalMethodWriter, mClassName);
+
         }
 
         if (isNative) {
diff --git a/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java b/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
index da8babc..6305500 100644
--- a/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
+++ b/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
@@ -89,6 +89,8 @@
     private final boolean mIsStaticInnerClass;
     /** The internal class name (e.g. <code>com/android/SomeClass$InnerClass</code>.) */
     private final String mClassName;
+    /** The corresponding delegate class name */
+    private final String mDelegateClassName;
     /** The method name. */
     private final String mMethodName;
     /** Logger object. */
@@ -111,6 +113,7 @@
      *          Must never be null.
      * @param className The internal class name of the class to visit,
      *          e.g. <code>com/android/SomeClass$InnerClass</code>.
+     * @param delegateClassName The internal class name of the delegate class
      * @param methodName The simple name of the method.
      * @param desc A method descriptor (c.f. {@link Type#getReturnType(String)} +
      *          {@link Type#getArgumentTypes(String)})
@@ -120,6 +123,7 @@
             MethodVisitor mvOriginal,
             MethodVisitor mvDelegate,
             String className,
+            String delegateClassName,
             String methodName,
             String desc,
             boolean isStatic,
@@ -129,12 +133,25 @@
         mOrgWriter = mvOriginal;
         mDelWriter = mvDelegate;
         mClassName = className;
+        mDelegateClassName = delegateClassName;
         mMethodName = methodName;
         mDesc = desc;
         mIsStatic = isStatic;
         mIsStaticInnerClass = isStaticClass;
     }
 
+    public DelegateMethodAdapter(Log log,
+            MethodVisitor mvOriginal,
+            MethodVisitor mvDelegate,
+            String className,
+            String methodName,
+            String desc,
+            boolean isStatic,
+            boolean isStaticClass) {
+        this(log, mvOriginal, mvDelegate, className, className + DELEGATE_SUFFIX, methodName,
+                desc, isStatic, isStaticClass);
+    }
+
     /**
      * Generates the new code for the method.
      * <p/>
@@ -189,7 +206,7 @@
         }
 
         ArrayList<Type> paramTypes = new ArrayList<>();
-        String delegateClassName = mClassName + DELEGATE_SUFFIX;
+        String delegateClassName = mDelegateClassName;
         boolean pushedArg0 = false;
         int maxStack = 0;
 
diff --git a/create/src/com/android/tools/layoutlib/create/DelegateToNativeAdapter.java b/create/src/com/android/tools/layoutlib/create/DelegateToNativeAdapter.java
new file mode 100644
index 0000000..d063e15
--- /dev/null
+++ b/create/src/com/android/tools/layoutlib/create/DelegateToNativeAdapter.java
@@ -0,0 +1,104 @@
+package com.android.tools.layoutlib.create;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Set;
+
+
+public class DelegateToNativeAdapter extends ClassVisitor {
+    private final Log mLog;
+    private final ClassWriter mDelegateWriter;
+    private final String mDelegateName;
+    private final String mClassName;
+    private final Set<String> mDelegateMethods;
+
+    public DelegateToNativeAdapter(Log logger, ClassVisitor cv, String className,
+            Map<String, ClassWriter> delegates, Set<String> delegateMethods) {
+        super(Main.ASM_VERSION, cv);
+        mLog = logger;
+        mDelegateWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+        mClassName = className;
+        mDelegateName = (className + "_NativeDelegate").replace('$', '_');
+        mDelegateMethods = delegateMethods;
+        delegates.put(mDelegateName, mDelegateWriter);
+    }
+
+    @Override
+    public void visit(int version, int access, String name, String signature, String superName,
+            String[] interfaces) {
+        super.visit(version, access, name, signature, superName, interfaces);
+
+        mDelegateWriter.visit(version, Opcodes.ACC_PUBLIC, mDelegateName, null, "java/lang/Object",
+                null);
+    }
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+            String[] exceptions) {
+        boolean isStaticMethod = (access & Opcodes.ACC_STATIC) != 0;
+        boolean isNative = (access & Opcodes.ACC_NATIVE) != 0;
+
+        if (isNative) {
+            mDelegateWriter.visitMethod(access, name + "_Original", desc, signature, exceptions);
+            generateDelegateMethod(name, desc, signature, exceptions);
+
+            if (mDelegateMethods == null || !mDelegateMethods.contains(name)) {
+                // Remove native flag
+                access = access & ~Opcodes.ACC_NATIVE;
+                MethodVisitor mwDelegate =
+                        super.visitMethod(access, name, desc, signature, exceptions);
+
+                DelegateMethodAdapter a =
+                        new DelegateMethodAdapter(mLog, null, mwDelegate, mClassName, mDelegateName,
+                                name, desc, isStaticMethod, false);
+
+                // A native has no code to visit, so we need to generate it directly.
+                a.generateDelegateCode();
+
+                return mwDelegate;
+            }
+        }
+        return super.visitMethod(access, name, desc, signature, exceptions);
+    }
+
+    private void generateDelegateMethod(String name, String desc, String signature,
+            String[] exceptions) {
+        MethodVisitor delegateVisitor =
+                mDelegateWriter.visitMethod(Opcodes.ACC_STATIC, name, desc,
+                        signature,
+                        exceptions);
+        AnnotationVisitor aw = delegateVisitor.visitAnnotation(
+                Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(),
+                true); // visible at runtime
+        if (aw != null) {
+            aw.visitEnd();
+        }
+        delegateVisitor.visitCode();
+        int maxStack = 0;
+        int maxLocals = 0;
+        Type[] argTypes = Type.getArgumentTypes(desc);
+        for (Type t : argTypes) {
+            int size = t.getSize();
+            delegateVisitor.visitVarInsn(t.getOpcode(Opcodes.ILOAD), maxLocals);
+            maxLocals += size;
+            maxStack += size;
+        }
+        delegateVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, mDelegateName,
+                name + "_Original", desc, false);
+
+        Type returnType = Type.getReturnType(desc);
+        delegateVisitor.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
+
+        delegateVisitor.visitMaxs(maxStack, maxLocals);
+        delegateVisitor.visitEnd();
+    }
+}
diff --git a/create/src/com/android/tools/layoutlib/create/ICreateInfo.java b/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
index 42d5727..83c5b24 100644
--- a/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
+++ b/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
@@ -19,13 +19,14 @@
 import org.objectweb.asm.ClassVisitor;
 
 import java.util.Map;
-import java.util.Set;
 
 /**
  * Interface describing the work to be done by {@link AsmGenerator}.
  */
 public interface ICreateInfo {
 
+    MethodReplacer[] getMethodReplacers();
+
     /**
      * Returns the list of class from layoutlib_create to inject in layoutlib.
      * The list can be empty but must not be null.
@@ -45,6 +46,27 @@
     String[] getDelegateClassNatives();
 
     /**
+     * Returns the list of classes for which to create a delegate class that delegates all native
+     * methods to corresponding native methods. This is useful for classes that call native
+     * methods during static initialization.
+     * The list can be empty but must not be null.
+     */
+    String[] getDelegateClassNativesToNatives();
+
+    /**
+     * Returns true if native methods should not be stubbed by default.
+     */
+    boolean shouldKeepAllNativeClasses();
+
+    /**
+     * Returns the list of classes for which not to delegate any native method.
+     * The list can be empty but must not be null.
+     *
+     * Only used when shouldKeepAllNativeClasses is false.
+     */
+    String[] getKeepClassNatives();
+
+    /**
      * Returns the list of classes to rename, must be an even list: the binary FQCN
      * of class to replace followed by the new FQCN.
      * The list can be empty but must not be null.
@@ -87,6 +109,13 @@
     String[] getPromotedFields();
 
     /**
+     * Returns a list of methods which should be promoted to public visibility. The array values
+     * are in the form of the binary FQCN of the class containing the method and the method name
+     * separated by a '#'.
+     */
+    String[] getPromotedMethods();
+
+    /**
      * Returns a list of classes to be promoted to public visibility.
      */
     String[] getPromotedClasses();
@@ -98,6 +127,18 @@
      */
     Map<String, InjectMethodRunnable> getInjectedMethodsMap();
 
+    String[] getDeferredStaticInitializerClasses();
+
+    interface MethodReplacer {
+        boolean isNeeded(String owner, String name, String desc, String sourceClass);
+
+        /**
+         * Updates the MethodInformation with the new values of the method attributes -
+         * opcode, owner, name and desc.
+         */
+        void replace(MethodInformation mi);
+    }
+
     abstract class InjectMethodRunnable {
         /**
          * @param cv Must be {@link ClassVisitor}. However, the param type is object so that when
@@ -107,4 +148,18 @@
          */
         public abstract void generateMethods(Object cv);
     }
+
+    class MethodInformation {
+        public int opcode;
+        public String owner;
+        public String name;
+        public String desc;
+
+        public MethodInformation(int opcode, String owner, String name, String desc) {
+            this.opcode = opcode;
+            this.owner = owner;
+            this.name = name;
+            this.desc = desc;
+        }
+    }
 }
diff --git a/create/src/com/android/tools/layoutlib/create/Main.java b/create/src/com/android/tools/layoutlib/create/Main.java
index 4cbffdd..4d0de14 100644
--- a/create/src/com/android/tools/layoutlib/create/Main.java
+++ b/create/src/com/android/tools/layoutlib/create/Main.java
@@ -16,9 +16,6 @@
 
 package com.android.tools.layoutlib.create;
 
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.ClassWriter;
 import org.objectweb.asm.Opcodes;
 
 import java.io.File;
@@ -62,6 +59,7 @@
         private boolean listAllDeps = false;
         private boolean listOnlyMissingDeps = false;
         private boolean createStubLib = false;
+        private boolean createNativeOnlyDelegates = false;
     }
 
     public static final int ASM_VERSION = Opcodes.ASM7;
@@ -76,7 +74,7 @@
         String[] osDestJar = { null };
 
         if (!processArgs(log, args, osJarPath, osDestJar)) {
-            log.error("Usage: layoutlib_create [-v] [--create-stub] output.jar input.jar ...");
+            log.error("Usage: layoutlib_create [-v] [--create-stub] [--create-native-only-delegates] output.jar input.jar ...");
             log.error("Usage: layoutlib_create [-v] [--list-deps|--missing-deps] input.jar ...");
             System.exit(1);
         }
@@ -99,7 +97,7 @@
         }
 
         try {
-            CreateInfo info = new CreateInfo();
+            ICreateInfo info = new CreateInfo();
             AsmGenerator agen = new AsmGenerator(log, info);
 
             AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath,
@@ -138,12 +136,13 @@
                         "com.android.internal.graphics.drawable.AnimationScaleListDrawable",
                         "com.google.android.apps.common.testing.accessibility.**",
                         "com.google.android.libraries.accessibility.**",
+                        "android.service.wallpaper.*",      // needed for Wear OS watch faces
                     },
                     info.getExcludedClasses(),
                     new String[] {
                         "com/android/i18n/phonenumbers/data/*",
                         "android/icu/impl/data/**"
-                    });
+                    }, info.getMethodReplacers());
             agen.setAnalysisResult(aa.analyze());
 
             Map<String, byte[]> outputClasses = agen.generate();
@@ -166,6 +165,13 @@
                 log.info("Created stub JAR file %s", stubDestJarFile);
             }
 
+            if (sOptions.createNativeOnlyDelegates) {
+                File osDestJarFile = new File(osDestJar);
+                Map<String, byte[]> nativeDelegateClasses =
+                        outputClasses.entrySet().stream().filter(entry -> entry.getKey().endsWith("_NativeDelegate.class")).collect(Collectors.toMap(Entry::getKey, Entry::getValue));
+                JarUtil.createJar(new FileOutputStream(osDestJarFile), nativeDelegateClasses);
+                log.info("Created native delegate JAR file %s", osDestJarFile);
+            }
 
             // Throw an error if any class failed to get renamed by the generator
             //
@@ -234,6 +240,8 @@
                 needs_dest = false;
             } else if (s.equals("--create-stub")) {
                 sOptions.createStubLib = true;
+            } else if (s.equals("--create-native-only-delegates")) {
+                sOptions.createNativeOnlyDelegates = true;
             } else if (!s.startsWith("-")) {
                 if (needs_dest && osDestJar[0] == null) {
                     osDestJar[0] = s;
diff --git a/create/src/com/android/tools/layoutlib/create/PromoteMethodClassAdapter.java b/create/src/com/android/tools/layoutlib/create/PromoteMethodClassAdapter.java
new file mode 100644
index 0000000..45be08f
--- /dev/null
+++ b/create/src/com/android/tools/layoutlib/create/PromoteMethodClassAdapter.java
@@ -0,0 +1,32 @@
+package com.android.tools.layoutlib.create;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+
+import java.util.Set;
+
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+
+public class PromoteMethodClassAdapter extends ClassVisitor {
+
+    private final Set<String> mMethodNames;
+    private static final int CLEAR_PRIVATE_MASK = ~(ACC_PRIVATE | ACC_PROTECTED);
+
+    public PromoteMethodClassAdapter(ClassVisitor cv, Set<String> methodNames) {
+        super(Main.ASM_VERSION, cv);
+        mMethodNames = methodNames;
+    }
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+            String[] exceptions) {
+        if (mMethodNames.contains(name)) {
+            if ((access & ACC_PUBLIC) == 0) {
+                access = (access & CLEAR_PRIVATE_MASK) | ACC_PUBLIC;
+            }
+        }
+        return super.visitMethod(access, name, desc, signature, exceptions);
+    }
+}
diff --git a/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java b/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
index 2967b05..a9bcd22 100644
--- a/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
+++ b/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
@@ -16,21 +16,12 @@
 
 package com.android.tools.layoutlib.create;
 
-import com.android.tools.layoutlib.java.LinkedHashMap_Delegate;
-import com.android.tools.layoutlib.java.System_Delegate;
+import com.android.tools.layoutlib.create.ICreateInfo.MethodInformation;
+import com.android.tools.layoutlib.create.ICreateInfo.MethodReplacer;
 
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
 import java.util.Set;
 
 /**
@@ -39,176 +30,12 @@
  */
 public class ReplaceMethodCallsAdapter extends ClassVisitor {
 
-    /**
-     * Descriptors for specialized versions {@link System#arraycopy} that are not present on the
-     * Desktop VM.
-     */
-    private static Set<String> ARRAYCOPY_DESCRIPTORS = new HashSet<>(Arrays.asList(
-            "([CI[CII)V", "([BI[BII)V", "([SI[SII)V", "([II[III)V",
-            "([JI[JII)V", "([FI[FII)V", "([DI[DII)V", "([ZI[ZII)V"));
-
-    private static final List<MethodReplacer> METHOD_REPLACERS = new ArrayList<>(5);
-
-    private static final String ANDROID_LOCALE_CLASS =
-            "com/android/layoutlib/bridge/android/AndroidLocale";
-
-    private static final String JAVA_LOCALE_CLASS = Type.getInternalName(java.util.Locale.class);
-    private static final Type STRING = Type.getType(String.class);
-
-    private static final String JAVA_LANG_SYSTEM = Type.getInternalName(System.class);
-
-    // Static initialization block to initialize METHOD_REPLACERS.
-    static {
-        // Case 1: java.lang.System.arraycopy()
-        METHOD_REPLACERS.add(new MethodReplacer() {
-            @Override
-            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
-                return JAVA_LANG_SYSTEM.equals(owner) && "arraycopy".equals(name) &&
-                        ARRAYCOPY_DESCRIPTORS.contains(desc);
-            }
-
-            @Override
-            public void replace(MethodInformation mi) {
-                mi.desc = "(Ljava/lang/Object;ILjava/lang/Object;II)V";
-            }
-        });
-
-        // Case 2: java.util.Locale.adjustLanguageCode() or java.util.Locale.getDefault()
-        METHOD_REPLACERS.add(new MethodReplacer() {
-
-            private final String STRING_TO_STRING = Type.getMethodDescriptor(STRING, STRING);
-            private final String VOID_TO_LOCALE =
-                    Type.getMethodDescriptor(Type.getType(Locale.class));
-
-            @Override
-            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
-                return JAVA_LOCALE_CLASS.equals(owner) &&
-                        ("adjustLanguageCode".equals(name) && desc.equals(STRING_TO_STRING) ||
-                        "getDefault".equals(name) && desc.equals(VOID_TO_LOCALE));
-            }
-
-            @Override
-            public void replace(MethodInformation mi) {
-                mi.owner = ANDROID_LOCALE_CLASS;
-            }
-        });
-
-        // Case 3: java.lang.System.log?()
-        METHOD_REPLACERS.add(new MethodReplacer() {
-            @Override
-            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
-                return JAVA_LANG_SYSTEM.equals(owner) && name.length() == 4
-                        && name.startsWith("log");
-            }
-
-            @Override
-            public void replace(MethodInformation mi) {
-                assert mi.desc.equals("(Ljava/lang/String;Ljava/lang/Throwable;)V")
-                        || mi.desc.equals("(Ljava/lang/String;)V");
-                mi.name = "log";
-                mi.owner = Type.getInternalName(System_Delegate.class);
-            }
-        });
-
-        // Case 4: java.lang.System time calls
-        METHOD_REPLACERS.add(new MethodReplacer() {
-            @Override
-            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
-                return JAVA_LANG_SYSTEM.equals(owner) && name.equals("nanoTime");
-            }
-
-            @Override
-            public void replace(MethodInformation mi) {
-                mi.name = "nanoTime";
-                mi.owner = Type.getInternalName(System_Delegate.class);
-            }
-        });
-        METHOD_REPLACERS.add(new MethodReplacer() {
-            @Override
-            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
-                return JAVA_LANG_SYSTEM.equals(owner) && name.equals("currentTimeMillis");
-            }
-
-            @Override
-            public void replace(MethodInformation mi) {
-                mi.name = "currentTimeMillis";
-                mi.owner = Type.getInternalName(System_Delegate.class);
-            }
-        });
-
-        // Case 5: java.util.LinkedHashMap.eldest()
-        METHOD_REPLACERS.add(new MethodReplacer() {
-
-            private final String VOID_TO_MAP_ENTRY =
-                    Type.getMethodDescriptor(Type.getType(Map.Entry.class));
-            private final String LINKED_HASH_MAP = Type.getInternalName(LinkedHashMap.class);
-
-            @Override
-            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
-                return LINKED_HASH_MAP.equals(owner) &&
-                        "eldest".equals(name) &&
-                        VOID_TO_MAP_ENTRY.equals(desc);
-            }
-
-            @Override
-            public void replace(MethodInformation mi) {
-                mi.opcode = Opcodes.INVOKESTATIC;
-                mi.owner = Type.getInternalName(LinkedHashMap_Delegate.class);
-                mi.desc = Type.getMethodDescriptor(
-                        Type.getType(Map.Entry.class), Type.getType(LinkedHashMap.class));
-            }
-        });
-
-        // Case 6: android.content.Context.getClassLoader() in LayoutInflater
-        METHOD_REPLACERS.add(new MethodReplacer() {
-            // When LayoutInflater asks for a class loader, we must return the class loader that
-            // cannot return app's custom views/classes. This is so that in case of any failure
-            // or exception when instantiating the views, the IDE can replace it with a mock view
-            // and have proper error handling. However, if a custom view asks for the class
-            // loader, we must return a class loader that can find app's custom views as well.
-            // Thus, we rewrite the call to get class loader in LayoutInflater to
-            // getFrameworkClassLoader and inject a new method in Context. This leaves the normal
-            // method: Context.getClassLoader() free to be used by the apps.
-            private final String VOID_TO_CLASS_LOADER =
-                    Type.getMethodDescriptor(Type.getType(ClassLoader.class));
-
-            @Override
-            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
-                return owner.equals("android/content/Context") &&
-                        sourceClass.equals("android/view/LayoutInflater") &&
-                        name.equals("getClassLoader") &&
-                        desc.equals(VOID_TO_CLASS_LOADER);
-            }
-
-            @Override
-            public void replace(MethodInformation mi) {
-                mi.name = "getFrameworkClassLoader";
-            }
-        });
-    }
-
-    /**
-     * If a method some.package.Class.Method(args) is called from some.other.Class,
-     * @param owner some/package/Class
-     * @param name Method
-     * @param desc (args)returnType
-     * @param sourceClass some/other/Class
-     * @return if the method invocation needs to be replaced by some other class.
-     */
-    public static boolean isReplacementNeeded(String owner, String name, String desc,
-            String sourceClass) {
-        for (MethodReplacer replacer : METHOD_REPLACERS) {
-            if (replacer.isNeeded(owner, name, desc, sourceClass)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
+    private Set<MethodReplacer> mMethodReplacers;
     private final String mOriginalClassName;
 
-    public ReplaceMethodCallsAdapter(ClassVisitor cv, String originalClassName) {
+    public ReplaceMethodCallsAdapter(Set<MethodReplacer> methodReplacers, ClassVisitor cv, String originalClassName) {
         super(Main.ASM_VERSION, cv);
+        mMethodReplacers = methodReplacers;
         mOriginalClassName = originalClassName;
     }
 
@@ -227,7 +54,7 @@
         @Override
         public void visitMethodInsn(int opcode, String owner, String name, String desc,
                 boolean itf) {
-            for (MethodReplacer replacer : METHOD_REPLACERS) {
+            for (MethodReplacer replacer : mMethodReplacers) {
                 if (replacer.isNeeded(owner, name, desc, mOriginalClassName)) {
                     MethodInformation mi = new MethodInformation(opcode, owner, name, desc);
                     replacer.replace(mi);
@@ -242,27 +69,4 @@
         }
     }
 
-    private static class MethodInformation {
-        public int opcode;
-        public String owner;
-        public String name;
-        public String desc;
-
-        public MethodInformation(int opcode, String owner, String name, String desc) {
-            this.opcode = opcode;
-            this.owner = owner;
-            this.name = name;
-            this.desc = desc;
-        }
-    }
-
-    private interface MethodReplacer {
-        boolean isNeeded(String owner, String name, String desc, String sourceClass);
-
-        /**
-         * Updates the MethodInformation with the new values of the method attributes -
-         * opcode, owner, name and desc.
-         */
-        void replace(MethodInformation mi);
-    }
 }
diff --git a/create/src/com/android/tools/layoutlib/create/StaticInitMethodAdapter.java b/create/src/com/android/tools/layoutlib/create/StaticInitMethodAdapter.java
new file mode 100644
index 0000000..0e3ffeb
--- /dev/null
+++ b/create/src/com/android/tools/layoutlib/create/StaticInitMethodAdapter.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static com.android.tools.layoutlib.create.DelegateMethodAdapter.DELEGATE_SUFFIX;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+class StaticInitMethodAdapter extends MethodVisitor {
+    /** static initializer delegate name. */
+    private static final String DELEGATE_STATIC_NAME = "staticInit";
+
+    /** The internal class name (e.g. <code>com/android/SomeClass$InnerClass</code>.) */
+    private final String mClassName;
+
+    /** Logger object. */
+    private final Log mLog;
+
+    /** The method writer to copy of the original method. */
+    private final MethodVisitor mRenamedMethodWriter;
+
+    /** The method writer to generate the original static { SomeClass_Delegate.staticInit} block */
+    private final MethodVisitor mOriginalMethodWriter;
+
+    /** Array used to capture the first line number information from the original method
+     *  and duplicate it in the delegate. */
+    private Object[] mDelegateLineNumber;
+
+    public StaticInitMethodAdapter(Log log, MethodVisitor renamedMethodWriter,
+            MethodVisitor originalMethodWriter,
+            String className) {
+        super(Main.ASM_VERSION);
+        mLog = log;
+        mRenamedMethodWriter = renamedMethodWriter;
+        mOriginalMethodWriter = originalMethodWriter;
+        mClassName = className;
+    }
+
+
+
+    /**
+     * Generate the new code for the method.
+     *
+     * This will be a call to className_Delegate#staticInit
+     */
+    private void generateDelegateCode() {
+        AnnotationVisitor aw = mOriginalMethodWriter.visitAnnotation(
+                Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(),
+                true); // visible at runtime
+        if (aw != null) {
+            aw.visitEnd();
+        }
+
+        mOriginalMethodWriter.visitCode();
+
+        if (mDelegateLineNumber != null) {
+            Object[] p = mDelegateLineNumber;
+            mOriginalMethodWriter.visitLineNumber((Integer) p[0], (Label) p[1]);
+        }
+
+        String delegateClassName = mClassName + DELEGATE_SUFFIX;
+        delegateClassName = delegateClassName.replace('$', '_');
+
+        // generate the SomeClass_Delegate.staticInit call.
+        mOriginalMethodWriter.visitMethodInsn(Opcodes.INVOKESTATIC, delegateClassName,
+                DELEGATE_STATIC_NAME,
+                Type.getMethodDescriptor(Type.VOID_TYPE),
+                false);
+        mOriginalMethodWriter.visitInsn(Type.VOID_TYPE.getOpcode(Opcodes.IRETURN));
+        mOriginalMethodWriter.visitMaxs(0, 0);
+
+        mOriginalMethodWriter.visitEnd();
+
+        mLog.debug("static initializer call for class %s delegated to %s#%s",
+                mClassName,
+                delegateClassName, DELEGATE_STATIC_NAME);
+    }
+
+    /* Pass down to visitor writer. In this implementation, either do nothing. */
+    @Override
+    public void visitCode() {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitCode();
+        }
+    }
+
+    /*
+     * visitMaxs is called just before visitEnd if there was any code to rewrite.
+     */
+    @Override
+    public void visitMaxs(int maxStack, int maxLocals) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitMaxs(maxStack, maxLocals);
+        }
+    }
+
+    /** End of visiting. Generate the delegating code. */
+    @Override
+    public void visitEnd() {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitEnd();
+        }
+        generateDelegateCode();
+    }
+
+    /* Writes all annotation from the original method. */
+    @Override
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        if (mRenamedMethodWriter != null) {
+            return mRenamedMethodWriter.visitAnnotation(desc, visible);
+        } else {
+            return null;
+        }
+    }
+
+    /* Writes all annotation default values from the original method. */
+    @Override
+    public AnnotationVisitor visitAnnotationDefault() {
+        if (mRenamedMethodWriter != null) {
+            return mRenamedMethodWriter.visitAnnotationDefault();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
+            boolean visible) {
+        if (mRenamedMethodWriter != null) {
+            return mRenamedMethodWriter.visitParameterAnnotation(parameter, desc, visible);
+        } else {
+            return null;
+        }
+    }
+
+    /* Writes all attributes from the original method. */
+    @Override
+    public void visitAttribute(Attribute attr) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.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.
+     */
+    @Override
+    public void visitLineNumber(int line, Label start) {
+        // Capture the first line values for the new delegate method
+        if (mDelegateLineNumber == null) {
+            mDelegateLineNumber = new Object[] { line, start };
+        }
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitLineNumber(line, start);
+        }
+    }
+
+    @Override
+    public void visitInsn(int opcode) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitInsn(opcode);
+        }
+    }
+
+    @Override
+    public void visitLabel(Label label) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitLabel(label);
+        }
+    }
+
+    @Override
+    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitTryCatchBlock(start, end, handler, type);
+        }
+    }
+
+    @Override
+    public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitMethodInsn(opcode, owner, name, desc, itf);
+        }
+    }
+
+    @Override
+    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitFieldInsn(opcode, owner, name, desc);
+        }
+    }
+
+    @Override
+    public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitFrame(type, nLocal, local, nStack, stack);
+        }
+    }
+
+    @Override
+    public void visitIincInsn(int var, int increment) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitIincInsn(var, increment);
+        }
+    }
+
+    @Override
+    public void visitIntInsn(int opcode, int operand) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitIntInsn(opcode, operand);
+        }
+    }
+
+    @Override
+    public void visitJumpInsn(int opcode, Label label) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitJumpInsn(opcode, label);
+        }
+    }
+
+    @Override
+    public void visitLdcInsn(Object cst) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitLdcInsn(cst);
+        }
+    }
+
+    @Override
+    public void visitLocalVariable(String name, String desc, String signature,
+            Label start, Label end, int index) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitLocalVariable(name, desc, signature, start, end, index);
+        }
+    }
+
+    @Override
+    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitLookupSwitchInsn(dflt, keys, labels);
+        }
+    }
+
+    @Override
+    public void visitMultiANewArrayInsn(String desc, int dims) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitMultiANewArrayInsn(desc, dims);
+        }
+    }
+
+    @Override
+    public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitTableSwitchInsn(min, max, dflt, labels);
+        }
+    }
+
+    @Override
+    public void visitTypeInsn(int opcode, String type) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitTypeInsn(opcode, type);
+        }
+    }
+
+    @Override
+    public void visitVarInsn(int opcode, int var) {
+        if (mRenamedMethodWriter != null) {
+            mRenamedMethodWriter.visitVarInsn(opcode, var);
+        }
+    }
+}
diff --git a/create/src/com/android/tools/layoutlib/create/StubClassAdapter.java b/create/src/com/android/tools/layoutlib/create/StubClassAdapter.java
index e1d2df2..5202ce4 100644
--- a/create/src/com/android/tools/layoutlib/create/StubClassAdapter.java
+++ b/create/src/com/android/tools/layoutlib/create/StubClassAdapter.java
@@ -34,7 +34,7 @@
 /**
  * Class adapter that can stub some or all of the methods of the class.
  */
-class StubClassAdapter extends ClassVisitor {
+public class StubClassAdapter extends ClassVisitor {
     public interface MethodVisitorFactory {
         @NotNull
         MethodVisitor create(@NotNull MethodVisitor mv,
diff --git a/create/src/com/android/tools/layoutlib/java/NioUtils_Delegate.java b/create/src/com/android/tools/layoutlib/java/NioUtils_Delegate.java
new file mode 100644
index 0000000..33039b5
--- /dev/null
+++ b/create/src/com/android/tools/layoutlib/java/NioUtils_Delegate.java
@@ -0,0 +1,15 @@
+
+package com.android.tools.layoutlib.java;
+
+import java.nio.ByteBuffer;
+
+public final class NioUtils_Delegate {
+  public static void freeDirectBuffer(ByteBuffer buffer) {
+    /*
+     * NioUtils is not included in layoutlib classpath. Thus, calling NioUtils.freeDirectBuffer in
+     * {@link android.graphics.ImageReader} produces ClassNotFound exception. Moreover, it does not
+     * seem we have to do anything in here as we are only referencing the existing native buffer
+     * and do not perform any allocation on creation.
+     */
+  }
+}
\ No newline at end of file
diff --git a/create/src/com/android/tools/layoutlib/java/Reference_Delegate.java b/create/src/com/android/tools/layoutlib/java/Reference_Delegate.java
new file mode 100644
index 0000000..a774275
--- /dev/null
+++ b/create/src/com/android/tools/layoutlib/java/Reference_Delegate.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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.java;
+
+import java.lang.ref.Reference;
+
+/**
+ * Provides an alternative implementation for java.lang.ref.Reference#refersTo, as it exists
+ * in ART but not in the host JDK.
+ */
+public class Reference_Delegate {
+    public static <T> boolean refersTo(Reference<T> thisRef, T obj) {
+        return thisRef.get() == obj;
+    }
+}
diff --git a/create/src/com/android/tools/layoutlib/java/System_Delegate.java b/create/src/com/android/tools/layoutlib/java/System_Delegate.java
deleted file mode 100644
index 335f566..0000000
--- a/create/src/com/android/tools/layoutlib/java/System_Delegate.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2014 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.java;
-
-import com.android.tools.layoutlib.create.ReplaceMethodCallsAdapter;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
-
-/**
- * Provides alternative implementations of methods that don't exist on the host VM.
- * This also providers a time control that allows to set a specific system time.
- *
- * @see ReplaceMethodCallsAdapter
- */
-@SuppressWarnings("unused")
-public class System_Delegate {
-    // Current system time
-    private static AtomicLong mNanosTime = new AtomicLong(System.nanoTime());
-    // Time that the system booted up in nanos
-    private static AtomicLong mBootNanosTime = new AtomicLong(System.nanoTime());
-
-    public static void log(String message) {
-        // ignore.
-    }
-
-    public static void log(String message, Throwable th) {
-        // ignore.
-    }
-
-    public static void setNanosTime(long nanos) {
-        mNanosTime.set(nanos);
-    }
-
-    public static void setBootTimeNanos(long nanos) {
-        mBootNanosTime.set(nanos);
-    }
-
-    public static long nanoTime() {
-        return mNanosTime.get();
-    }
-
-    public static long currentTimeMillis() {
-        return TimeUnit.NANOSECONDS.toMillis(mNanosTime.get());
-    }
-
-    public static long bootTime() {
-        return mBootNanosTime.get();
-    }
-
-    public static long bootTimeMillis() {
-        return TimeUnit.NANOSECONDS.toMillis(mBootNanosTime.get());
-    }
-}
diff --git a/create/tests/res/data/mock_android.jar b/create/tests/res/data/mock_android.jar
index 3fd6999..2534ece 100644
--- a/create/tests/res/data/mock_android.jar
+++ b/create/tests/res/data/mock_android.jar
Binary files differ
diff --git a/create/tests/res/mock_data/README.md b/create/tests/res/mock_data/README.md
new file mode 100644
index 0000000..6b59184
--- /dev/null
+++ b/create/tests/res/mock_data/README.md
@@ -0,0 +1,25 @@
+# Update mock_android.jar
+
+## Build mock_android.jar
+
+First, use set up instructions from layoutlib tutorial to make commands like `m` work.
+
+Then, run the following in the repository root:
+
+```
+m mock_android
+mv out/host/linux-x86/framework/mock_android.jar frameworks/layoutlib/create/tests/res/data/mock_android.jar
+```
+
+## Build problems
+
+If you see a build error like:
+
+```
+[ 29% 351/1203] including frameworks/layoutlib/out/test/create/mock_data/Android.mk ...
+FAILED:
+build/make/core/base_rules.mk:325: error: frameworks/layoutlib/out/test/create/mock_data: MODULE.linux.JAVA_LIBRARIES.mock_android already defined by frameworks/layoutlib/create/tests/res/mock_data.
+16:26:52 ckati failed with: exit status 1
+```
+
+a quick solution would be to remove `frameworks/layoutlib/out/test/create/mock_data/Android.mk`.
diff --git a/create/tests/res/mock_data/mock_android/view/LibLoader.java b/create/tests/res/mock_data/mock_android/view/LibLoader.java
new file mode 100644
index 0000000..c24a288
--- /dev/null
+++ b/create/tests/res/mock_data/mock_android/view/LibLoader.java
@@ -0,0 +1,7 @@
+package mock_android.view;
+
+class LibLoader {
+    static {
+        System.loadLibrary("dummy");
+    }
+}
\ No newline at end of file
diff --git a/create/tests/run_tests.sh b/create/tests/run_tests.sh
new file mode 100755
index 0000000..3d53838
--- /dev/null
+++ b/create/tests/run_tests.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+SCRIPT_DIR="$(dirname $0)"
+DIST_DIR="$1"
+
+STUDIO_JDK=${SCRIPT_DIR}"/../../../../prebuilts/jdk/jdk11/linux-x86"
+OUT_INTERMEDIATES=${SCRIPT_DIR}"/../../../../out/soong/.intermediates"
+
+${STUDIO_JDK}/bin/java -ea \
+    -cp ${OUT_INTERMEDIATES}/external/junit/junit/linux_glibc_common/javac/junit.jar:${OUT_INTERMEDIATES}/external/hamcrest/hamcrest-core/hamcrest/linux_glibc_common/javac/hamcrest.jar:${OUT_INTERMEDIATES}/frameworks/layoutlib/create/layoutlib_create/linux_glibc_common/combined/layoutlib_create.jar:${OUT_INTERMEDIATES}/frameworks/layoutlib/create/tests/layoutlib-create-tests/linux_glibc_common/combined/layoutlib-create-tests.jar:${SCRIPT_DIR}/res \
+    org.junit.runner.JUnitCore \
+    com.android.tools.layoutlib.create.AllTests
+
diff --git a/create/tests/src/com/android/tools/layoutlib/create/AllTests.java b/create/tests/src/com/android/tools/layoutlib/create/AllTests.java
new file mode 100644
index 0000000..cdc43e0
--- /dev/null
+++ b/create/tests/src/com/android/tools/layoutlib/create/AllTests.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 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.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+/**
+ * Suite containing all the layoutlib-create tests
+ */
+@RunWith(Suite.class)
+@SuiteClasses({
+        AsmAnalyzerTest.class, AsmGeneratorTest.class,
+        ClassHasNativeVisitorTest.class, DelegateClassAdapterTest.class,
+        LogTest.class, PromoteClassClassAdapterTest.class, RenameClassAdapterTest.class,
+        StubClassAdapterTest.class, StubMethodAdapterTest.class,
+})
+public class AllTests {
+}
diff --git a/create/tests/src/com/android/tools/layoutlib/create/AsmAnalyzerTest.java b/create/tests/src/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
index 327bbb7..d0943d5 100644
--- a/create/tests/src/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
@@ -19,6 +19,7 @@
 
 import com.android.tools.layoutlib.create.AsmAnalyzer.DependencyVisitor;
 import com.android.tools.layoutlib.create.AsmAnalyzer.Result;
+import com.android.tools.layoutlib.create.ICreateInfo.MethodReplacer;
 
 import org.junit.Test;
 import org.objectweb.asm.ClassReader;
@@ -55,7 +56,8 @@
     private static AsmAnalyzer getDefaultAnalyzer() {
         MockLog log = new MockLog();
         return new AsmAnalyzer(log, MOCK_ANDROID_JAR, null ,
-                null /* includeGlobs */, DEFAULT_EXCLUDES, DEFAULT_INCLUDE_FILES);
+                null /* includeGlobs */, DEFAULT_EXCLUDES, DEFAULT_INCLUDE_FILES,
+                new MethodReplacer[] {});
     }
 
     @Test
@@ -84,6 +86,7 @@
                 "mock_android.fake2.keep.DoNotRemove",
                 "mock_android.util.EmptyArray",
                 "mock_android.util.NotNeeded",
+                "mock_android.view.LibLoader",
                 "mock_android.view.View",
                 "mock_android.view.ViewGroup",
                 "mock_android.view.ViewGroup$LayoutParams",
@@ -126,7 +129,8 @@
                     "mock_android.fake2.*", // Exclude subpackages select
                 },
                 DEFAULT_EXCLUDES,
-                DEFAULT_INCLUDE_FILES);
+                DEFAULT_INCLUDE_FILES,
+                new MethodReplacer[] {});
         Result result = analyzer.analyze();
         assertArrayEquals(new String[] {
                         "mock_android.fake.InnerTest$NotStaticInner1",
diff --git a/create/tests/src/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/create/tests/src/com/android/tools/layoutlib/create/AsmGeneratorTest.java
index 3f5e8f6..aadab22 100644
--- a/create/tests/src/com/android/tools/layoutlib/create/AsmGeneratorTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -18,6 +18,10 @@
 package com.android.tools.layoutlib.create;
 
 
+import com.android.tools.layoutlib.create.CreateInfo.SystemLoadLibraryReplacer;
+import com.android.tools.layoutlib.create.ICreateInfo.MethodInformation;
+import com.android.tools.layoutlib.create.ICreateInfo.MethodReplacer;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -39,6 +43,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.zip.ZipEntry;
@@ -106,7 +111,8 @@
                     "**"
                 },
                 new String[]{}  /* excluded classes */,
-                new String[]{} /* include files */);
+                new String[]{}, /* include files */
+                new MethodReplacer[] {});
         agen.setAnalysisResult(aa.analyze());
         agen.generate();
 
@@ -150,7 +156,8 @@
                 new String[]{},
                 new String[] {        /* include files */
                     "mock_android/data/data*"
-                });
+                },
+                new MethodReplacer[] {} /* method replacers */);
         agen.setAnalysisResult(aa.analyze());
         Map<String, byte[]> output = agen.generate();
         RecordingClassVisitor cv = new RecordingClassVisitor();
@@ -195,7 +202,8 @@
                         "**"
                 },
                 new String[]{},
-                new String[] {});
+                new String[] {},
+                new MethodReplacer[] {});
         agen.setAnalysisResult(aa.analyze());
         Map<String, byte[]> output = agen.generate();
         RecordingClassVisitor cv = new RecordingClassVisitor();
@@ -232,7 +240,8 @@
                 ci.getExcludedClasses(),
                 new String[] {        /* include files */
                         "mock_android/data/data*"
-                });
+                },
+                new MethodReplacer[] {});
         agen.setAnalysisResult(aa.analyze());
         Map<String, byte[]> output = agen.generate();
         // Everything in .fake.** should be filtered
@@ -240,6 +249,7 @@
         assertArrayEquals(new String[] {
                 "mock_android.fake2.keep.DoNotRemove",
                 "mock_android.util.EmptyArray",
+                "mock_android.view.LibLoader",
                 "mock_android.view.View",
                 "mock_android.view.ViewGroup",
                 "mock_android.view.ViewGroup$LayoutParams",
@@ -275,7 +285,8 @@
                 ci.getExcludedClasses(),
                 new String[] {        /* include files */
                         "mock_android/data/data*"
-                });
+                },
+                new MethodReplacer[] {});
         agen.setAnalysisResult(aa.analyze());
         JarUtil.createJar(new FileOutputStream(mOsDestJar), agen.generate());
 
@@ -304,6 +315,58 @@
         assertEquals(classLoader, cl);
     }
 
+    @Test
+    public void testMethodVisitor_loadLibraryReplacer() throws IOException {
+        final List<String> isNeeded = new ArrayList<>();
+        final List<String> replaced = new ArrayList<>();
+        MethodReplacer recordingReplacer = new MethodReplacer() {
+            private final MethodReplacer loadLibraryReplacer = new SystemLoadLibraryReplacer();
+            private final List<String> isNeededList = isNeeded;
+            private final List<String> replacedList = replaced;
+
+            @Override
+            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+                boolean res = loadLibraryReplacer.isNeeded(owner, name, desc, sourceClass);
+                if (res) {
+                    isNeededList.add(sourceClass + "->" + owner + "." + name);
+                }
+                return res;
+            }
+
+            @Override
+            public void replace(MethodInformation mi) {
+                replacedList.add(mi.owner + "." + mi.name);
+            }
+        };
+        MethodReplacer[] replacers = new MethodReplacer[] { recordingReplacer };
+
+        ICreateInfo ci = new CreateInfoAdapter() {
+            @Override
+            public MethodReplacer[] getMethodReplacers() {
+                return replacers;
+            }
+        };
+
+        AsmGenerator agen = new AsmGenerator(mLog, ci);
+        AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath,
+                null,                 // derived from
+                new String[] {        // include classes
+                        "**"
+                },
+                new String[] {},
+                new String[] {},
+                replacers);
+        agen.setAnalysisResult(aa.analyze());
+
+        assertTrue(isNeeded.contains("mock_android/view/LibLoader->java/lang/System.loadLibrary"));
+        assertTrue(replaced.isEmpty());
+
+        agen.generate();
+
+        assertTrue(isNeeded.contains("mock_android/view/LibLoader->java/lang/System.loadLibrary"));
+        assertTrue(replaced.contains("java/lang/System.loadLibrary"));
+    }
+
     private static byte[] getByteArray(InputStream stream) throws IOException {
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         byte[] buffer = new byte[1024];
diff --git a/create/tests/src/com/android/tools/layoutlib/create/CreateInfoAdapter.java b/create/tests/src/com/android/tools/layoutlib/create/CreateInfoAdapter.java
index 668b578..3b1cb06 100644
--- a/create/tests/src/com/android/tools/layoutlib/create/CreateInfoAdapter.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/CreateInfoAdapter.java
@@ -23,6 +23,11 @@
     private static final String[] EMPTY_STRING_ARRAY = new String[0];
 
     @Override
+    public MethodReplacer[] getMethodReplacers() {
+        return new MethodReplacer[0];
+    }
+
+    @Override
     public Class<?>[] getInjectedClasses() {
         return new Class<?>[0];
     }
@@ -38,6 +43,21 @@
     }
 
     @Override
+    public String[] getDelegateClassNativesToNatives() {
+        return EMPTY_STRING_ARRAY;
+    }
+
+    @Override
+    public boolean shouldKeepAllNativeClasses() {
+        return false;
+    }
+
+    @Override
+    public String[] getKeepClassNatives() {
+        return EMPTY_STRING_ARRAY;
+    }
+
+    @Override
     public String[] getRenamedClasses() {
         return EMPTY_STRING_ARRAY;
     }
@@ -68,6 +88,11 @@
     }
 
     @Override
+    public String[] getPromotedMethods() {
+        return EMPTY_STRING_ARRAY;
+    }
+
+    @Override
     public String[] getPromotedClasses() {
         return EMPTY_STRING_ARRAY;
     }
@@ -76,4 +101,9 @@
     public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
         return Collections.emptyMap();
     }
+
+    @Override
+    public String[] getDeferredStaticInitializerClasses() {
+        return EMPTY_STRING_ARRAY;
+    }
 }
diff --git a/create/tests/src/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java b/create/tests/src/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
index b7aa4e6..c6e4508 100644
--- a/create/tests/src/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -57,6 +58,13 @@
     private static final String OUTER_CLASS_NAME = OuterClass.class.getName();
     private static final String INNER_CLASS_NAME = InnerClass.class.getName();
     private static final String STATIC_INNER_CLASS_NAME = StaticInnerClass.class.getName();
+    // use a string to avoid triggering the static init
+    private static final String CLASS_WITH_STATIC_INIT_NAME =
+            "com.android.tools.layoutlib.create.dataclass.ClassWithStaticInit";
+    private static final String INNER_CLASS_WITH_STATIC_INIT_NAME =
+            "com.android.tools.layoutlib.create.dataclass.ClassWithStaticInit$InnerClass";
+    private static final String INNER_CLASS_WITH_STATIC_INIT_DELEGATE_NAME =
+            "com.android.tools.layoutlib.create.dataclass.ClassWithStaticInit_InnerClass_Delegate";
 
     @Before
     public void setUp() throws Exception {
@@ -237,6 +245,74 @@
     }
 
     @Test
+    public void testDelegateStaticInitializer() throws Throwable {
+        ClassWriter cw = new ClassWriter(0 /*flags*/);
+
+        String internalClassName = CLASS_WITH_STATIC_INIT_NAME.replace('.', '/');
+
+        HashSet<String> delegateMethods = new HashSet<>();
+        delegateMethods.add("<clinit>");
+        DelegateClassAdapter cv = new DelegateClassAdapter(
+                mLog, cw, internalClassName, delegateMethods);
+
+        ClassReader cr = new ClassReader(CLASS_WITH_STATIC_INIT_NAME);
+        cr.accept(cv, 0 /* flags */);
+
+        ClassLoader2 cl2 = null;
+        try {
+            cl2 = new ClassLoader2() {
+                @Override
+                public void testModifiedInstance() throws Exception {
+                    Class<?> clazz2 = loadClass(CLASS_WITH_STATIC_INIT_NAME);
+
+                    assertNull(clazz2.getField("sList").get(null));
+
+                    Class<?> delegateClass = loadClass(CLASS_WITH_STATIC_INIT_NAME + "_Delegate");
+                    assertNotNull( delegateClass.getField("sList").get(null));
+                }
+            };
+            cl2.add(CLASS_WITH_STATIC_INIT_NAME, cw);
+            cl2.testModifiedInstance();
+        } catch (Throwable t) {
+            throw dumpGeneratedClass(t, cl2);
+        }
+    }
+
+    @Test
+    public void testDelegateInnerClassStaticInitializer() throws Throwable {
+        ClassWriter cw = new ClassWriter(0 /*flags*/);
+
+        String internalClassName = INNER_CLASS_WITH_STATIC_INIT_NAME.replace('.', '/');
+
+        HashSet<String> delegateMethods = new HashSet<>();
+        delegateMethods.add("<clinit>");
+        DelegateClassAdapter cv = new DelegateClassAdapter(
+                mLog, cw, internalClassName, delegateMethods);
+
+        ClassReader cr = new ClassReader(INNER_CLASS_WITH_STATIC_INIT_NAME);
+        cr.accept(cv, 0 /* flags */);
+
+        ClassLoader2 cl2 = null;
+        try {
+            cl2 = new ClassLoader2() {
+                @Override
+                public void testModifiedInstance() throws Exception {
+                    Class<?> clazz2 = loadClass(INNER_CLASS_WITH_STATIC_INIT_NAME);
+
+                    assertNull(clazz2.getField("sInnerList").get(null));
+
+                    Class<?> delegateClass = loadClass(INNER_CLASS_WITH_STATIC_INIT_DELEGATE_NAME);
+                    assertNotNull( delegateClass.getField("sList").get(null));
+                }
+            };
+            cl2.add(INNER_CLASS_WITH_STATIC_INIT_NAME, cw);
+            cl2.testModifiedInstance();
+        } catch (Throwable t) {
+            throw dumpGeneratedClass(t, cl2);
+        }
+    }
+
+    @Test
     public void testDelegateNative() throws Throwable {
         ClassWriter cw = new ClassWriter(0 /*flags*/);
         String internalClassName = NATIVE_CLASS_NAME.replace('.', '/');
diff --git a/create/tests/src/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java b/create/tests/src/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java
index 928ac4d..2e6ccd3 100644
--- a/create/tests/src/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java
@@ -162,7 +162,7 @@
         });
 
         reader.accept(adapter, 0);
-        assertTrue(log.mLog.contains("[visit] - version=52, access=[public], " +
+        assertTrue(log.mLog.contains("[visit] - version=55, access=[public], " +
                 "name=com/android/tools/layoutlib/create/PackageProtectedClass, signature=null, " +
                 "superName=java/lang/Object, interfaces=[]"));
 
diff --git a/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit.java b/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit.java
new file mode 100644
index 0000000..f5a78a9
--- /dev/null
+++ b/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.layoutlib.create.dataclass;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ClassWithStaticInit {
+
+    public static List<String> sList = new ArrayList<>();
+
+    public static class InnerClass {
+        public static List<String> sInnerList = new ArrayList<>();
+    }
+}
diff --git a/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit_Delegate.java b/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit_Delegate.java
new file mode 100644
index 0000000..3bc5491
--- /dev/null
+++ b/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit_Delegate.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.layoutlib.create.dataclass;
+
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The delegate that receives the call to {@link ClassWithStaticInit}'s overridden methods.
+ *
+ * Used by {@link DelegateClassAdapterTest}.
+ */
+public class ClassWithStaticInit_Delegate {
+
+    public static List<String> sList = null;
+
+    public static void staticInit() {
+        sList = new ArrayList<>();
+    }
+}
diff --git a/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit_InnerClass_Delegate.java b/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit_InnerClass_Delegate.java
new file mode 100644
index 0000000..faa2956
--- /dev/null
+++ b/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit_InnerClass_Delegate.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.layoutlib.create.dataclass;
+
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The delegate that receives the call to {@link ClassWithStaticInit$InnerClass}'s overridden
+ * methods.
+ *
+ * Used by {@link DelegateClassAdapterTest}.
+ */
+public class ClassWithStaticInit_InnerClass_Delegate {
+
+        public static List<String> sList = null;
+
+        public static void staticInit() {
+            sList = new ArrayList<>();
+        }
+}
diff --git a/delegates/Android.bp b/delegates/Android.bp
new file mode 100644
index 0000000..93bcd6d
--- /dev/null
+++ b/delegates/Android.bp
@@ -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.
+//
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library_host {
+    name: "layoutlib-common-delegates",
+
+    srcs: ["src/**/*.java"],
+
+    libs: [
+        "temp_layoutlib",
+        "layoutlib-common",
+    ],
+
+}
diff --git a/delegates/README b/delegates/README
new file mode 100644
index 0000000..0ef28c6
--- /dev/null
+++ b/delegates/README
@@ -0,0 +1 @@
+The common set of delegates shared between layoutlib and simulated_device
\ No newline at end of file
diff --git a/delegates/delegates.iml b/delegates/delegates.iml
new file mode 100644
index 0000000..04f8ae4
--- /dev/null
+++ b/delegates/delegates.iml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/tests/src" isTestSource="true" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="framework.jar" level="project" />
+    <orderEntry type="module" module-name="common" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/delegates/src/android/graphics/fonts/SystemFonts_Delegate.java b/delegates/src/android/graphics/fonts/SystemFonts_Delegate.java
new file mode 100644
index 0000000..ff2d64c
--- /dev/null
+++ b/delegates/src/android/graphics/fonts/SystemFonts_Delegate.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2018 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.fonts;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.FontConfig;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Delegate implementing the native methods of android.graphics.fonts.SystemFonts
+ * <p>
+ * Through the layoutlib_create tool, the original native methods of SystemFonts have been
+ * replaced by calls to methods of the same name in this delegate class.
+ * <p>
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between it
+ * and the original SystemFonts class.
+ *
+ */
+public class SystemFonts_Delegate {
+
+    private static final String TAG = "SystemFonts_Delegate";
+    private static String sFontLocation;
+    public static boolean sIsTypefaceInitialized = false;
+
+    public static void setFontLocation(String fontLocation) {
+        sFontLocation = fontLocation;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static FontConfig getSystemFontConfigInternal(
+            String fontsXml,
+            String systemFontDir,
+            String oemXml,
+            String productFontDir,
+            Map<String, File> updatableFontMap,
+            long lastModifiedDate,
+            int configVersion) {
+        sIsTypefaceInitialized = true;
+        return SystemFonts.getSystemFontConfigInternal_Original(
+            sFontLocation + "fonts.xml", sFontLocation, null, null, updatableFontMap,
+            lastModifiedDate, configVersion);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static ByteBuffer mmap(@NonNull String fullPath) {
+        // Android does memory mapping for font files. But Windows keeps files open
+        // until the byte buffer from the memory mapping is garbage collected.
+        // To avoid that, on Windows, read the file into a byte buffer instead.
+        // See JDK-4715154.
+        String osName = System.getProperty("os.name").toLowerCase(Locale.US);
+        if (osName.startsWith("windows")) {
+            try (FileInputStream file = new FileInputStream(fullPath)) {
+                final FileChannel fileChannel = file.getChannel();
+                final int size = (int) fileChannel.size();
+                // Native code requires the ByteBuffer to be direct
+                // (see android/graphics/fonts/Font.cpp)
+                ByteBuffer buffer = ByteBuffer.allocateDirect(size);
+                fileChannel.read(buffer);
+                return buffer;
+            } catch (IOException e) {
+                Log.e(TAG, "Error mapping font file " + fullPath);
+                return null;
+            }
+        } else {
+            return SystemFonts.mmap_Original(fullPath);
+        }
+    }
+}
\ No newline at end of file
diff --git a/delegates/src/android/media/ImageReader_Delegate.java b/delegates/src/android/media/ImageReader_Delegate.java
new file mode 100644
index 0000000..0ff1ce7
--- /dev/null
+++ b/delegates/src/android/media/ImageReader_Delegate.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 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.media;
+
+public class ImageReader_Delegate {
+
+    static void nativeClassInit() {
+        // Call ImageReader.nativeClassInit(); in layoutlib implicitly before using ImageReader
+    }
+}
diff --git a/delegates/src/com/android/tools/layoutlib/java/nio/Buffer_Delegate.java b/delegates/src/com/android/tools/layoutlib/java/nio/Buffer_Delegate.java
new file mode 100644
index 0000000..351d9b4
--- /dev/null
+++ b/delegates/src/com/android/tools/layoutlib/java/nio/Buffer_Delegate.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 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.java.nio;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.DoubleBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.LongBuffer;
+import java.nio.ShortBuffer;
+
+/**
+ * Delegate to fix differences between the Android and the JVM versions of java.nio.Buffer
+ */
+public class Buffer_Delegate {
+    /**
+     * The Android version of java.nio.Buffer has an extra final field called _elementSizeShift
+     * that only depend on the implementation of the buffer. This method can be called instead
+     * when wanting to access the value of that field on the JVM.
+     */
+    public static int elementSizeShift(Buffer buffer) {
+        if (buffer instanceof ByteBuffer) {
+            return 0;
+        }
+        if (buffer instanceof ShortBuffer || buffer instanceof CharBuffer) {
+            return 1;
+        }
+        if (buffer instanceof IntBuffer || buffer instanceof FloatBuffer) {
+            return 2;
+        }
+        if (buffer instanceof LongBuffer || buffer instanceof DoubleBuffer) {
+            return 3;
+        }
+        return 0;
+    }
+}
diff --git a/delegates/src/com/android/tools/layoutlib/java/nio/NIOAccess_Delegate.java b/delegates/src/com/android/tools/layoutlib/java/nio/NIOAccess_Delegate.java
new file mode 100644
index 0000000..addcc0b
--- /dev/null
+++ b/delegates/src/com/android/tools/layoutlib/java/nio/NIOAccess_Delegate.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 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.java.nio;
+
+import com.android.layoutlib.common.util.ReflectionUtils;
+import com.android.layoutlib.common.util.ReflectionUtils.ReflectionException;
+
+import java.nio.Buffer;
+
+/**
+ * A fork of libcore's java.nio.NIOAccess which does not exist in the JVM
+ *
+ * This class is used via JNI by code in frameworks/base/.
+ * @hide
+ */
+// @VisibleForTesting : was default
+public final class NIOAccess_Delegate {
+
+    /**
+     * Returns the underlying native pointer to the data of the given
+     * Buffer starting at the Buffer's current position, or 0 if the
+     * Buffer is not backed by native heap storage.
+     * @hide
+     */
+    // @VisibleForTesting : was default
+    public static long getBasePointer(Buffer b) {
+        try {
+            long address = (long)ReflectionUtils.getFieldValue(Buffer.class, b, "address");
+            if (address == 0L || !b.isDirect()) {
+                return 0L;
+            }
+            return address + ((long)b.position() << Buffer_Delegate.elementSizeShift(b));
+        } catch (ReflectionException e) {
+            return 0L;
+        }
+    }
+
+    /**
+     * Returns the underlying Java array containing the data of the
+     * given Buffer, or null if the Buffer is not backed by a Java array.
+     */
+    static Object getBaseArray(Buffer b) {
+        return b.hasArray() ? b.array() : null;
+    }
+
+    /**
+     * Returns the offset in bytes from the start of the underlying
+     * Java array object containing the data of the given Buffer to
+     * the actual start of the data. The start of the data takes into
+     * account the Buffer's current position. This method is only
+     * meaningful if getBaseArray() returns non-null.
+     */
+    static int getBaseArrayOffset(Buffer b) {
+        return b.hasArray() ?
+                ((b.arrayOffset() + b.position()) << Buffer_Delegate.elementSizeShift(b)) : 0;
+    }
+
+
+}
diff --git a/delegates/src/com/android/tools/layoutlib/java/text/DateFormat_Delegate.java b/delegates/src/com/android/tools/layoutlib/java/text/DateFormat_Delegate.java
new file mode 100644
index 0000000..cb72c2d
--- /dev/null
+++ b/delegates/src/com/android/tools/layoutlib/java/text/DateFormat_Delegate.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 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.java.text;
+
+import java.text.DateFormat;
+
+/**
+ * Provides alternate implementation to java.text.DateFormat.set24HourTimePref, which is present
+ * as a
+ * non-public method in the Android VM, but not present on the host VM. This is injected in the
+ * layoutlib using {@link ReplaceMethodCallsAdapter}.
+ */
+public class DateFormat_Delegate {
+
+    public static final void set24HourTimePref(Boolean is24Hour) {
+        // ignore
+    }
+}
diff --git a/delegates/src/com/android/tools/layoutlib/java/util/LocaleAdjustLanguageCodeReplacement.java b/delegates/src/com/android/tools/layoutlib/java/util/LocaleAdjustLanguageCodeReplacement.java
new file mode 100644
index 0000000..b335e86
--- /dev/null
+++ b/delegates/src/com/android/tools/layoutlib/java/util/LocaleAdjustLanguageCodeReplacement.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 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.java.util;
+
+import java.util.Locale;
+
+/**
+ * This class provides an alternate implementation for {@code java.util.Locale#adjustLanguageCode}
+ * which is not available in openJDK.
+ *
+ * The create tool re-writes references to the above mentioned method to this one. Hence it's
+ * imperative that this class is not deleted unless the create tool is modified.
+ */
+public class LocaleAdjustLanguageCodeReplacement {
+
+    public static String adjustLanguageCode(String languageCode) {
+        String adjusted = languageCode.toLowerCase(Locale.US);
+        // Map new language codes to the obsolete language
+        // codes so the correct resource bundles will be used.
+        if (languageCode.equals("he")) {
+            adjusted = "iw";
+        } else if (languageCode.equals("id")) {
+            adjusted = "in";
+        } else if (languageCode.equals("yi")) {
+            adjusted = "ji";
+        }
+
+        return adjusted;
+    }
+}
diff --git a/delegates/src/com/android/tools/layoutlib/java/util/zip/ZipEntry_Delegate.java b/delegates/src/com/android/tools/layoutlib/java/util/zip/ZipEntry_Delegate.java
new file mode 100644
index 0000000..4475fff
--- /dev/null
+++ b/delegates/src/com/android/tools/layoutlib/java/util/zip/ZipEntry_Delegate.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 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.java.util.zip;
+
+import java.util.zip.ZipEntry;
+
+/**
+ * Wrapper for calls to Android-added API to ZipEntry
+ */
+public class ZipEntry_Delegate extends ZipEntry {
+
+    private final long mDataOffset;
+
+    // Called from  StrictJarFile native code.
+    public ZipEntry_Delegate(String name, String comment, long crc, long compressedSize, long size,
+            int compressionMethod, int xdostime, byte[] extra, long dataOffset) {
+        super(name);
+        setComment(comment);
+        setCrc(crc);
+        setCompressedSize(compressedSize);
+        setSize(size);
+        setMethod(compressionMethod);
+        setTime(xdostime);
+        setExtra(extra);
+        mDataOffset = dataOffset;
+    }
+
+    /**
+     * Handle calls to the Android-added ZipEntry#getDataOffset.
+     *
+     * Called from StrictJarFile java code.
+     */
+    public static long getDataOffset(ZipEntry original) {
+        return ((ZipEntry_Delegate) original).mDataOffset;
+    }
+}
diff --git a/delegates/src/dalvik/system/VMRuntimeCommonHelper.java b/delegates/src/dalvik/system/VMRuntimeCommonHelper.java
new file mode 100644
index 0000000..ae63625
--- /dev/null
+++ b/delegates/src/dalvik/system/VMRuntimeCommonHelper.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 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 dalvik.system;
+
+/**
+ * Common VMRuntime Delegate code used by both layoutlib and simulated_device.
+ */
+class VMRuntimeCommonHelper {
+
+    // Copied from libcore/libdvm/src/main/java/dalvik/system/VMRuntime
+    /*package*/ static Object newUnpaddedArray(VMRuntime runtime, Class<?> componentType,
+            int minLength) {
+        // Dalvik has 32bit pointers, the array header is 16bytes plus 4bytes for dlmalloc,
+        // allocations are 8byte aligned so having 4bytes of array data avoids padding.
+        if (!componentType.isPrimitive()) {
+            int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+            return java.lang.reflect.Array.newInstance(componentType, size);
+        } else if (componentType == char.class) {
+            int bytes = 20 + (2 * minLength);
+            int alignedUpBytes = (bytes + 7) & -8;
+            int dataBytes = alignedUpBytes - 20;
+            int size = dataBytes / 2;
+            return new char[size];
+        } else if (componentType == int.class) {
+            int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+            return new int[size];
+        } else if (componentType == byte.class) {
+            int bytes = 20 + minLength;
+            int alignedUpBytes = (bytes + 7) & -8;
+            int dataBytes = alignedUpBytes - 20;
+            int size = dataBytes;
+            return new byte[size];
+        } else if (componentType == boolean.class) {
+            int bytes = 20 + minLength;
+            int alignedUpBytes = (bytes + 7) & -8;
+            int dataBytes = alignedUpBytes - 20;
+            int size = dataBytes;
+            return new boolean[size];
+        } else if (componentType == short.class) {
+            int bytes = 20 + (2 * minLength);
+            int alignedUpBytes = (bytes + 7) & -8;
+            int dataBytes = alignedUpBytes - 20;
+            int size = dataBytes / 2;
+            return new short[size];
+        } else if (componentType == float.class) {
+            int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+            return new float[size];
+        } else if (componentType == long.class) {
+            return new long[minLength];
+        } else if (componentType == double.class) {
+            return new double[minLength];
+        } else {
+            assert componentType == void.class;
+            throw new IllegalArgumentException("Can't allocate an array of void");
+        }
+    }
+
+
+    /*package*/ static int getNotifyNativeInterval() {
+        // This cannot return 0, otherwise it is responsible for triggering an exception
+        // whenever trying to use a NativeAllocationRegistry with size 0
+        return 128; // see art/runtime/gc/heap.h -> kNotifyNativeInterval
+    }
+}
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java
index 01efd12..06fa6b2 100644
--- a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java
@@ -102,6 +102,11 @@
         return mDelegate.getFlag(key);
     }
 
+    @Override
+    public String getResourcePackage() throws RemoteException {
+        return mDelegate.getResourcePackage();
+    }
+
     @Nullable
     @Override
     public Path findClassPath(String name) {
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteLayoutLog.java b/remote/common/src/com/android/layout/remote/api/RemoteLayoutLog.java
index 335cf9d..8e09ece 100644
--- a/remote/common/src/com/android/layout/remote/api/RemoteLayoutLog.java
+++ b/remote/common/src/com/android/layout/remote/api/RemoteLayoutLog.java
@@ -73,10 +73,10 @@
     void error(String tag, String message, Throwable throwable, Object viewCookie, Serializable data)
             throws RemoteException;
 
-    /** 
-     * Logs messages coming from the Android Framework. 
+    /**
+     * Logs messages coming from the Android Framework.
      *
-     * @param priority the priority level of the message 
+     * @param priority the priority level of the message
      * @param tag a tag describing the type of the error
      * @param message the message of the error
      */
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java b/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java
index d68934a..ccf1fee 100644
--- a/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java
+++ b/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java
@@ -52,6 +52,8 @@
 
     <T> T getFlag(Key<T> key) throws RemoteException;
 
+    String getResourcePackage() throws RemoteException;
+
     Path findClassPath(String name) throws RemoteException;
 
     RemoteXmlPullParser createXmlParserForPsiFile(String fileName) throws RemoteException;
diff --git a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutlibCallbackAdapter.java b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutlibCallbackAdapter.java
index 719de4d..cba8a47 100644
--- a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutlibCallbackAdapter.java
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutlibCallbackAdapter.java
@@ -218,6 +218,15 @@
     }
 
     @Override
+    public String getResourcePackage() {
+        try {
+            return mDelegate.getResourcePackage();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
     public Class<?> findClass(String name) throws ClassNotFoundException {
         return mPathClassLoader.loadClass(name);
     }
diff --git a/rename_font/README b/rename_font/README
deleted file mode 100644
index 600b756..0000000
--- a/rename_font/README
+++ /dev/null
@@ -1,9 +0,0 @@
-This tool is used to rename the PS name encoded inside the ttf font that we ship
-with the SDK. There is bug in Java that returns incorrect results for
-java.awt.Font#layoutGlyphVector() if two fonts with same name but differnt
-versions are loaded. As a workaround, we rename all the fonts that we ship with
-the SDK by appending the font version to its name.
-
-
-The build_font.py copies all files from input_dir to output_dir while renaming
-the font files (*.ttf) in the process.
diff --git a/rename_font/Roboto-Regular.ttf b/rename_font/Roboto-Regular.ttf
deleted file mode 100644
index 7469063..0000000
--- a/rename_font/Roboto-Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/rename_font/build_font.py b/rename_font/build_font.py
deleted file mode 100755
index db0c98a..0000000
--- a/rename_font/build_font.py
+++ /dev/null
@@ -1,227 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright (C) 2014 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.
-
-"""
-Rename the PS name of all fonts in the input directories and copy them to the
-output directory.
-
-Usage: build_font.py /path/to/input_fonts1/ /path/to/input_fonts2/ /path/to/output_fonts/
-
-"""
-
-import glob
-from multiprocessing import Pool
-import os
-import re
-import shutil
-import sys
-import xml.etree.ElementTree as etree
-
-# Prevent .pyc files from being created.
-sys.dont_write_bytecode = True
-
-# fontTools is available at platform/external/fonttools
-from fontTools import ttx
-
-# global variable
-dest_dir = '/tmp'
-
-
-class FontInfo(object):
-  family = None
-  style = None
-  version = None
-  ends_in_regular = False
-  fullname = None
-
-
-class InvalidFontException(Exception):
-  pass
-
-
-# These constants represent the value of nameID parameter in the namerecord for
-# different information.
-# see http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08#3054f18b
-NAMEID_FAMILY = 1
-NAMEID_STYLE = 2
-NAMEID_FULLNAME = 4
-NAMEID_VERSION = 5
-
-
-def main(argv):
-  if len(argv) < 2:
-    sys.exit('Usage: build_font.py /path/to/input_fonts/ /path/to/out/dir/')
-  for directory in argv:
-    if not os.path.isdir(directory):
-      sys.exit(directory + ' is not a valid directory')
-  global dest_dir
-  dest_dir = argv[-1]
-  src_dirs = argv[:-1]
-  cwd = os.getcwd()
-  os.chdir(dest_dir)
-  files = glob.glob('*')
-  for filename in files:
-    os.remove(filename)
-  os.chdir(cwd)
-  input_fonts = list()
-  for src_dir in src_dirs:
-    for dirname, dirnames, filenames in os.walk(src_dir):
-      for filename in filenames:
-        input_path = os.path.join(dirname, filename)
-        extension = os.path.splitext(filename)[1].lower()
-        if extension == '.ttf':
-          input_fonts.append(input_path)
-        elif extension == '.xml':
-          shutil.copy(input_path, dest_dir)
-      if '.git' in dirnames:
-        # don't go into any .git directories.
-        dirnames.remove('.git')
-  # Create as many threads as the number of CPUs
-  pool = Pool(processes=None)
-  pool.map(convert_font, input_fonts)
-
-
-def convert_font(input_path):
-  filename = os.path.basename(input_path)
-  print('Converting font: ' + filename)
-  # the path to the output file. The file name is the fontfilename.ttx
-  ttx_path = os.path.join(dest_dir, filename)
-  ttx_path = ttx_path[:-1] + 'x'
-  try:
-    # run ttx to generate an xml file in the output folder which represents all
-    # its info
-    ttx_args = ['--no-recalc-timestamp', '-q', '-d', dest_dir, input_path]
-    ttx.main(ttx_args)
-    # now parse the xml file to change its PS name.
-    tree = etree.parse(ttx_path)
-    root = tree.getroot()
-    for name in root.iter('name'):
-      update_tag(name, get_font_info(name))
-    tree.write(ttx_path, xml_declaration=True, encoding='utf-8')
-    # generate the udpated font now.
-    ttx_args = ['-q', '-d', dest_dir, ttx_path]
-    ttx.main(ttx_args)
-  except InvalidFontException:
-    # In case of invalid fonts, we exit.
-    print(filename + ' is not a valid font')
-    raise
-  except Exception as e:
-    print('Error converting font: ' + filename)
-    print(e)
-    # Some fonts are too big to be handled by the ttx library.
-    # Just copy paste them.
-    shutil.copy(input_path, dest_dir)
-  try:
-    # delete the temp ttx file is it exists.
-    os.remove(ttx_path)
-  except OSError:
-    pass
-
-
-def get_font_info(tag):
-  """ Returns a list of FontInfo representing the various sets of namerecords
-      found in the name table of the font. """
-  fonts = []
-  font = None
-  last_name_id = sys.maxsize
-  for namerecord in tag.iter('namerecord'):
-    if 'nameID' in namerecord.attrib:
-      name_id = int(namerecord.attrib['nameID'])
-      # A new font should be created for each platform, encoding and language
-      # id. But, since the nameIDs are sorted, we use the easy approach of
-      # creating a new one when the nameIDs reset.
-      if name_id <= last_name_id and font is not None:
-        fonts.append(font)
-        font = None
-      last_name_id = name_id
-      if font is None:
-        font = FontInfo()
-      if name_id == NAMEID_FAMILY:
-        font.family = namerecord.text.strip()
-      if name_id == NAMEID_STYLE:
-        font.style = namerecord.text.strip()
-      if name_id == NAMEID_FULLNAME:
-        font.ends_in_regular = ends_in_regular(namerecord.text)
-        font.fullname = namerecord.text.strip()
-      if name_id == NAMEID_VERSION:
-        font.version = get_version(namerecord.text)
-  if font is not None:
-    fonts.append(font)
-  return fonts
-
-
-def update_tag(tag, fonts):
-  last_name_id = sys.maxsize
-  fonts_iterator = fonts.__iter__()
-  font = None
-  for namerecord in tag.iter('namerecord'):
-    if 'nameID' in namerecord.attrib:
-      name_id = int(namerecord.attrib['nameID'])
-      if name_id <= last_name_id:
-        font = next(fonts_iterator)
-        font = update_font_name(font)
-      last_name_id = name_id
-      if name_id == NAMEID_FAMILY:
-        namerecord.text = font.family
-      if name_id == NAMEID_FULLNAME:
-        namerecord.text = font.fullname
-
-
-def update_font_name(font):
-  """ Compute the new font family name and font fullname. If the font has a
-      valid version, it's sanitized and appended to the font family name. The
-      font fullname is then created by joining the new family name and the
-      style. If the style is 'Regular', it is appended only if the original font
-      had it. """
-  if font.family is None or font.style is None:
-    raise InvalidFontException('Font doesn\'t have proper family name or style')
-  if font.version is not None:
-    new_family = font.family + font.version
-  else:
-    new_family = font.family
-  if font.style == 'Regular' and not font.ends_in_regular:
-    font.fullname = new_family
-  else:
-    font.fullname = new_family + ' ' + font.style
-  font.family = new_family
-  return font
-
-
-def ends_in_regular(string):
-  """ According to the specification, the font fullname should not end in
-      'Regular' for plain fonts. However, some fonts don't obey this rule. We
-      keep the style info, to minimize the diff. """
-  string = string.strip().split()[-1]
-  return string == 'Regular'
-
-
-def get_version(string):
-  string = string.strip()
-  # The spec says that the version string should start with "Version ". But not
-  # all fonts do. So, we return the complete string if it doesn't start with
-  # the prefix, else we return the rest of the string after sanitizing it.
-  prefix = 'Version '
-  if string.startswith(prefix):
-    string = string[len(prefix):]
-  return sanitize(string)
-
-
-def sanitize(string):
-  """ Remove non-standard chars. """
-  return re.sub(r'[^\w-]+', '', string)
-
-if __name__ == '__main__':
-  main(sys.argv[1:])
diff --git a/rename_font/build_font_single.py b/rename_font/build_font_single.py
deleted file mode 100755
index b254072..0000000
--- a/rename_font/build_font_single.py
+++ /dev/null
@@ -1,221 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright (C) 2014 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.
-
-"""
-Rename the PS name of the input font.
-
-OpenType fonts (*.otf) and TrueType Collections (*.ttc) are not currently supported. They are copied to the destination
-without renaming. XML files are also copied in case they are passed there by mistake.
-
-Usage: build_font_single.py /path/to/input_font.ttf /path/to/output_font.ttf
-
-"""
-
-import glob
-import os
-import re
-import shutil
-import sys
-import xml.etree.ElementTree as etree
-
-# Prevent .pyc files from being created.
-sys.dont_write_bytecode = True
-
-# fontTools is available at platform/external/fonttools
-from fontTools import ttx
-
-
-class FontInfo(object):
-  family = None
-  style = None
-  version = None
-  ends_in_regular = False
-  fullname = None
-
-
-class InvalidFontException(Exception):
-  pass
-
-
-# A constant to copy the font without modifying. This is useful when running
-# locally and speed up the time to build the SDK.
-COPY_ONLY = False
-
-# These constants represent the value of nameID parameter in the namerecord for
-# different information.
-# see http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08#3054f18b
-NAMEID_FAMILY = 1
-NAMEID_STYLE = 2
-NAMEID_FULLNAME = 4
-NAMEID_VERSION = 5
-
-NAMEID_LIST = [NAMEID_FAMILY, NAMEID_STYLE, NAMEID_FULLNAME, NAMEID_VERSION]
-NAMEID_LIST_MIN = min(NAMEID_LIST)
-NAMEID_LIST_MAX = max(NAMEID_LIST)
-
-# A list of extensions to process.
-EXTENSIONS = ['.ttf', '.ttc', '.otf', '.xml']
-
-def main(argv):
-  if len(argv) < 2:
-    print('Incorrect usage: ' + str(argv))
-    sys.exit('Usage: build_font_single.py /path/to/input/font.ttf /path/to/out/font.ttf')
-  dest_path = argv[-1]
-  input_path = argv[0]
-  extension = os.path.splitext(input_path)[1].lower()
-  if extension in EXTENSIONS:
-    if not COPY_ONLY and extension == '.ttf':
-      convert_font(input_path, dest_path)
-      return
-    shutil.copy(input_path, dest_path)
-
-
-def convert_font(input_path, dest_path):
-  filename = os.path.basename(input_path)
-  print('Converting font: ' + filename)
-  # the path to the output file. The file name is the fontfilename.ttx
-  ttx_path = dest_path[:-1] + 'x'
-  try:
-    # run ttx to generate an xml file in the output folder which represents all
-    # its info
-    ttx_args = ['--no-recalc-timestamp', '-q', '-o', ttx_path, input_path]
-    ttx.main(ttx_args)
-    # now parse the xml file to change its PS name.
-    tree = etree.parse(ttx_path)
-    root = tree.getroot()
-    for name in root.iter('name'):
-      update_tag(name, get_font_info(name))
-    tree.write(ttx_path, xml_declaration=True, encoding='utf-8')
-    # generate the udpated font now.
-    ttx_args = ['-q', '-o', dest_path, ttx_path]
-    ttx.main(ttx_args)
-  except InvalidFontException:
-    # assume, like for .ttc and .otf that font might be valid, but warn.
-    print('Family and/or Style nameIDs not found in '+ filename)
-    shutil.copy(input_path, dest_path)
-  except Exception as e:
-    print('Error converting font: ' + filename)
-    print(e)
-    # Some fonts are too big to be handled by the ttx library.
-    # Just copy paste them.
-    shutil.copy(input_path, dest_path)
-  try:
-    # delete the temp ttx file is it exists.
-    os.remove(ttx_path)
-  except OSError:
-    pass
-
-
-def get_font_info(tag):
-  """ Returns a list of FontInfo representing the various sets of namerecords
-      found in the name table of the font. """
-  fonts = []
-  font = None
-  last_name_id = sys.maxsize
-  for namerecord in tag.iter('namerecord'):
-    if 'nameID' in namerecord.attrib:
-      name_id = int(namerecord.attrib['nameID'])
-      # skip irrelevant records
-      if name_id < NAMEID_LIST_MIN or name_id > NAMEID_LIST_MAX:
-        continue
-      # A new font should be created for each platform, encoding and language
-      # id. But, since the nameIDs are sorted, we use the easy approach of
-      # creating a new one when the nameIDs reset.
-      if name_id <= last_name_id and font is not None:
-        fonts.append(font)
-        font = None
-      last_name_id = name_id
-      if font is None:
-        font = FontInfo()
-      if name_id == NAMEID_FAMILY:
-        font.family = namerecord.text.strip()
-      if name_id == NAMEID_STYLE:
-        font.style = namerecord.text.strip()
-      if name_id == NAMEID_FULLNAME:
-        font.ends_in_regular = ends_in_regular(namerecord.text)
-        font.fullname = namerecord.text.strip()
-      if name_id == NAMEID_VERSION:
-        font.version = get_version(namerecord.text)
-  if font is not None:
-    fonts.append(font)
-  return fonts
-
-
-def update_tag(tag, fonts):
-  last_name_id = sys.maxsize
-  fonts_iterator = fonts.__iter__()
-  font = None
-  for namerecord in tag.iter('namerecord'):
-    if 'nameID' in namerecord.attrib:
-      name_id = int(namerecord.attrib['nameID'])
-      # skip irrelevant records
-      if name_id < NAMEID_LIST_MIN or name_id > NAMEID_LIST_MAX:
-        continue
-      if name_id <= last_name_id:
-        font = next(fonts_iterator)
-        font = update_font_name(font)
-      last_name_id = name_id
-      if name_id == NAMEID_FAMILY:
-        namerecord.text = font.family
-      if name_id == NAMEID_FULLNAME:
-        namerecord.text = font.fullname
-
-
-def update_font_name(font):
-  """ Compute the new font family name and font fullname. If the font has a
-      valid version, it's sanitized and appended to the font family name. The
-      font fullname is then created by joining the new family name and the
-      style. If the style is 'Regular', it is appended only if the original font
-      had it. """
-  if font.family is None or font.style is None:
-    raise InvalidFontException('Font doesn\'t have proper family name or style')
-  if font.version is not None:
-    new_family = font.family + font.version
-  else:
-    new_family = font.family
-  if font.style == 'Regular' and not font.ends_in_regular:
-    font.fullname = new_family
-  else:
-    font.fullname = new_family + ' ' + font.style
-  font.family = new_family
-  return font
-
-
-def ends_in_regular(string):
-  """ According to the specification, the font fullname should not end in
-      'Regular' for plain fonts. However, some fonts don't obey this rule. We
-      keep the style info, to minimize the diff. """
-  string = string.strip().split()[-1]
-  return string == 'Regular'
-
-
-def get_version(string):
-  string = string.strip()
-  # The spec says that the version string should start with "Version ". But not
-  # all fonts do. So, we return the complete string if it doesn't start with
-  # the prefix, else we return the rest of the string after sanitizing it.
-  prefix = 'Version '
-  if string.startswith(prefix):
-    string = string[len(prefix):]
-  return sanitize(string)
-
-
-def sanitize(string):
-  """ Remove non-standard chars. """
-  return re.sub(r'[^\w-]+', '', string)
-
-if __name__ == '__main__':
-  main(sys.argv[1:])
diff --git a/rename_font/test.py b/rename_font/test.py
deleted file mode 100755
index cf26ee9..0000000
--- a/rename_font/test.py
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env python3
-
-"""Tests build_font.py by renaming a font.
-
-The test copies Roboto-Regular.ttf to a tmp directory and ask build_font.py to rename it and put in another dir.
-We then use ttx to dump the new font to its xml and check if rename was successful
-
-To test locally, use:
-PYTHONPATH="$PYTHONPATH:/path/to/android/checkout/external/fonttools/Lib" ./test.py
-"""
-
-import unittest
-import build_font
-
-from fontTools import ttx
-import os
-import xml.etree.ElementTree as etree
-import shutil
-import tempfile
-
-class MyTest(unittest.TestCase):
-  def test(self):
-    font_name = "Roboto-Regular.ttf"
-    srcdir = tempfile.mkdtemp()
-    print("srcdir: " + srcdir)
-    shutil.copy(font_name, srcdir)
-    destdir = tempfile.mkdtemp()
-    print("destdir: " + destdir)
-    self.assertTrue(build_font.main([srcdir, destdir]) is None)
-    out_path = os.path.join(destdir, font_name)
-    ttx.main([out_path])
-    ttx_path = out_path[:-1] + "x"
-    tree = etree.parse(ttx_path)
-    root = tree.getroot()
-    name_tag = root.find('name')
-    fonts = build_font.get_font_info(name_tag)
-    shutil.rmtree(srcdir)
-    shutil.rmtree(destdir)
-    self.assertEqual(fonts[0].family, "Roboto1200310")
-    self.assertEqual(fonts[0].fullname, "Roboto1200310 Regular")
-
-
-
-if __name__ == '__main__':
-  unittest.main()
diff --git a/split_universal_binary.sh b/split_universal_binary.sh
new file mode 100755
index 0000000..bd57245
--- /dev/null
+++ b/split_universal_binary.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+readonly OUT_DIR="$1"
+readonly DIST_DIR="$2"
+readonly BUILD_NUMBER="$3"
+
+readonly SCRIPT_DIR="$(dirname "$0")"
+
+readonly ARM=arm64
+readonly X86=x86_64
+
+NATIVE_LIBRARIES=${SCRIPT_DIR}"/../../out/host/darwin-x86/lib64"
+
+# Find lipo command used to create and manipulate universal binaries
+LIPO=$(/usr/bin/xcrun --find lipo)
+
+mkdir ${OUT_DIR}/${ARM}
+mkdir ${OUT_DIR}/${X86}
+
+# Split all universal binaries built for layoutlib into an ARM64 version and a X86_64 version
+for f in ${NATIVE_LIBRARIES}/*
+do
+  ${LIPO} $f -output ${OUT_DIR}/${ARM}/$(basename $f) -thin ${ARM}
+  ${LIPO} $f -output ${OUT_DIR}/${X86}/$(basename $f) -thin ${X86}
+done
+
+# Put the single architecture binaries inside the DIST folder to be accessible on ab/
+if [[ -d "${DIST_DIR}" ]]; then
+    cp -r ${OUT_DIR}/${ARM} ${DIST_DIR}/layoutlib_native/darwin
+    cp -r ${OUT_DIR}/${X86} ${DIST_DIR}/layoutlib_native/darwin
+fi
+
+# Clean
+rm -rf ${OUT_DIR}/${ARM}
+rm -rf ${OUT_DIR}/${X86}
+
+exit 0
diff --git a/validator/Android.bp b/validator/Android.bp
index ca68179..2ca1f21 100644
--- a/validator/Android.bp
+++ b/validator/Android.bp
@@ -25,7 +25,6 @@
     java_resource_dirs: ["resources"],
 
     libs: [
-        "tools-common-prebuilt",
         "temp_layoutlib",
         "layoutlib_api-prebuilt",
         "layoutlib-common",
diff --git a/validator/resources/strings.properties b/validator/resources/strings.properties
index 7855142..7b324b9 100644
--- a/validator/resources/strings.properties
+++ b/validator/resources/strings.properties
@@ -14,132 +14,140 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+result_message_not_important_for_accessibility = This item was not found to be important for accessibility.
+result_message_no_content_desc = This item has no <tt>android:contentDescription</tt>.
+result_message_not_visible = This item is not visible.
+result_message_not_enabled = This item isn\'t enabled.
+result_message_not_text_view = This item is not a <tt>TextView</tt>.
+result_message_no_screencapture = Screen capture data could not be obtained.
+result_message_view_not_within_screencapture = This item\'s on-screen location (<tt>%1$s</tt>) were not within the screen capture on-screen location (<tt>%2$s</tt>).
+result_message_screencapture_data_hidden = Screen capture information for this item was hidden.
+result_message_screencapture_uniform_color = Screen capture has a uniform color.
+clickable = clickable
+non_clickable = non-clickable
+long_clickable = long clickable
+clickable_and_long_clickable = clickable and long clickable
 actionable = actionable
-button_item_type = button
-check_title_accessibility_traversal = Traversal order
-check_title_class_name_not_supported = Unsupported item type
+result_message_sdk_version_not_applicable = This check is not applicable on devices running Android %1$s and above.
+result_message_addendum_view_potentially_obscured = This item may be obscured by other on-screen content. Consider manually testing this item\'s contrast.
 check_title_clickablespan = Link
+result_message_clickablespan_no_determined_type = This item\'s type is undetermined.
+result_message_urlspan_invalid_url = Verify that the URL within this item\'s <tt>URLSpan</tt> is valid.
+result_message_urlspan_not_clickablespan = This item should use a <tt>URLSpan</tt> in place of a <tt>ClickableSpan</tt>.
 check_title_duplicate_clickable_bounds = Clickable items
+result_message_brief_same_view_bounds = Multiple %1$s items share this location on the screen.
+result_message_same_view_bounds = This %1$s item has the same on-screen location (<tt>%2$s</tt>) as %3$d other item(s) with those properties.
+result_message_view_bounds = This %1$s item also has an on-screen location of <tt>%2$s</tt>.
 check_title_duplicate_speakable_text = Item descriptions
+result_message_brief_same_speakable_text = Multiple items have the same description.
+result_message_same_speakable_text = This %1$s item\'s speakable text: \"<tt>%2$s</tt>\" is identical to that of %3$d other item(s).
+result_message_speakable_text = This %1$s item also has speakable text: \"<tt>%2$s</tt>\".
 check_title_editable_content_desc = Editable item label
+result_message_editable_textview_content_desc = This editable <tt>TextView</tt> has an <tt>android:contentDescription</tt>. A screen reader may read this attribute instead of the editable content when the user is navigating.
+result_message_not_editable_textview = This item is not an editable <tt>TextView</tt>.
 check_title_image_contrast = Image contrast
-check_title_item_exposed = Exposed items
-check_title_link_test = Link text
-check_title_reading_score = Readability
+result_message_not_imageview = This item is not an <tt>ImageView</tt>.
+result_message_brief_image_contrast_not_sufficient = Consider increasing the contrast ratio between this image\'s foreground and background.
+result_message_image_contrast_not_sufficient = The image\'s contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%3$06X</tt> and an estimated background color of <tt>#%4$06X</tt>. Consider increasing this ratio to %2$.2f or greater.
+result_message_image_contrast_not_sufficient_confirmed = The image\'s contrast ratio is %1$.2f. This ratio is based on the provided foreground color of <tt>#%3$06X</tt> and provided background color of <tt>#%4$06X</tt>. Consider increasing this ratio to %2$.2f or greater.
+result_message_image_customized_contrast_not_sufficient = The image\'s contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%3$06X</tt> and an estimated background color of <tt>#%4$06X</tt>. Consider increasing this ratio to the modified ratio of %2$.2f or greater.
+result_message_image_customized_contrast_not_sufficient_confirmed = The image\'s contrast ratio is %1$.2f. This ratio is based on the provided foreground color of <tt>#%3$06X</tt> and provided background color of <tt>#%4$06X</tt>. Consider increasing this ratio to the modified ratio of %2$.2f or greater.
 check_title_redundant_description = Item type label
-check_title_speakable_text_present = Item label
-check_title_text_contrast = Text contrast
-check_title_text_style = Text Style
-check_title_touch_target_size = Touch target
-check_view_banned_word = Banned word
+result_message_english_locale_only = This check only runs on devices with locales set to English.
+result_message_brief_content_desc_contains_redundant_word = This item\'s <tt>android:contentDescription</tt> might contain unnecessary text.
+result_message_content_desc_ends_with_view_type = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\" ends with the item\'s type.
+result_message_content_desc_contains_redundant_word = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\" contains the item type \"<tt>%2$s</tt>\".
+result_message_content_desc_contains_action = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\", contains the action \"<tt>%2$s</tt>\".
+result_message_content_desc_contains_state = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\", contains the state \"<tt>%2$s</tt>\".
+button_item_type = button
 checkbox_item_type = checkbox
 checkbox_item_type_separate_words = check box
 checked_state = checked
-click_action = click
-clickable = clickable
-clickable_and_long_clickable = clickable and long clickable
-description_remove_view_attribute = Remove the view attribute <tt>%1$s</tt>.
-description_set_view_attribute = Set the view attribute <tt>%1$s</tt> to <tt>%2$s</tt>.
-description_set_view_attribute_with_an_empty_string = Set the view attribute <tt>%1$s</tt> to a meaningful non-empty string or resource reference.
-italic_text = italic
-italic_underline_text = italic and underline
-long_clickable = long clickable
-non_clickable = non-clickable
-question_id_message_confirm_foreground_background_colors = Are the detected foreground and background colors correct?
-question_id_message_provide_background_color = What is the correct background color?
-question_id_message_provide_foreground_color = What is the correct foreground color?
-question_message_identify_unexposed_items = Select regions of the screen with unidentified items.
-question_message_screen_has_unexposed_items = Does this screen contain important items that are not outlined?
-question_option_message_background_incorrect = Background incorrect
-question_option_message_both_correct = Both correct
-question_option_message_both_incorrect = Foreground and background incorrect
-question_option_message_foreground_incorrect = Foreground incorrect
-question_option_message_unknown = Unknown
-result_message_addendum_against_scrollable_edge = This item may be only partially visible within a scrollable container.
-result_message_addendum_clickable_ancestor = A parent container may be handling touch events for this item. If selecting the larger container performs the same action as selecting this item, consider defining this item as not clickable. If a different action is performed, consider increasing the size of this item.
-result_message_addendum_clipped_by_ancestor = A parent container may be clipping the size of this item, which has a drawing area of <tt>%1$ddp</tt> x <tt>%2$ddp</tt>. Consider increasing the size of this item\'s clipping ancestor, or allowing a larger parent container to handle actions on behalf of this item.
-result_message_addendum_opacity_description = Its actual opacity is %1$.2f%%.
-result_message_addendum_touch_delegate = A <tt>TouchDelegate</tt> has been detected on one of this item\'s ancestors. This message can be ignored if the delegate is of sufficient size and handles touches for this item.
-result_message_addendum_touch_delegate_with_hit_rect = A <tt>TouchDelegate</tt> with size <tt>%1$ddp</tt> x <tt>%2$ddp</tt> has been detected for this item. Consider increasing the size of its hit <tt>Rect</tt>.
-result_message_addendum_view_potentially_obscured = This item may be obscured by other on-screen content. Consider manually testing this item\'s contrast.
-result_message_background_must_be_opaque = This items\'s background color is not opaque.
-result_message_banned_word = This item\'s text may contain an inappropriate word, "<tt>%1$s</tt>"
-result_message_brief_banned_word = Consider removing inappropriate words from this item\'s text
-result_message_brief_content_desc_contains_redundant_word = This item\'s <tt>android:contentDescription</tt> might contain unnecessary text.
-result_message_brief_image_contrast_not_sufficient = Consider increasing the contrast ratio between this image\'s foreground and background.
-result_message_brief_is_unexposed_item_screen_region = Consider exposing items in this region to accessibility services.
-result_message_brief_link_text_not_descriptive = Consider using more descriptive text in the link.
-result_message_brief_low_reading_score = This text may have a low readability score.
-result_message_brief_same_speakable_text = Multiple items have the same description.
-result_message_brief_same_view_bounds = Multiple %1$s items share this location on the screen.
-result_message_brief_small_touch_target = Consider making this clickable item larger.
-result_message_brief_styled_text = Consider removing %1$s styling on longer passages of text.
-result_message_brief_text_contrast_not_sufficient = Consider increasing this item\'s text foreground to background contrast ratio.
-result_message_brief_unpredictable_traversal = Traversal behavior with screen readers may be unpredictable.
-result_message_class_name_is_empty = This item\'s type may not be reported to accessibility services. Consider using a type defined by the Android SDK.
-result_message_class_name_is_unknown = This item\'s type could not be determined.
-result_message_class_name_not_supported_brief = This item\'s type may not be supported.
-result_message_class_name_not_supported_detail = This item\'s type <tt>%1$s</tt> may not be resolvable by accessibility services. Consider using a type defined by the Android SDK.
-result_message_clickablespan_no_determined_type = This item\'s type is undetermined.
-result_message_content_desc_contains_action = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\", contains the action \"<tt>%2$s</tt>\".
-result_message_content_desc_contains_redundant_word = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\" contains the item type \"<tt>%2$s</tt>\".
-result_message_content_desc_contains_state = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\", contains the state \"<tt>%2$s</tt>\".
-result_message_content_desc_ends_with_view_type = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\" ends with the item\'s type.
-result_message_could_not_get_background_color = This item\'s background color could not be determined.
-result_message_could_not_get_text_color = This item\'s text color could not be determined.
-result_message_customized_small_touch_target_height = This item\'s height is <tt>%1$ddp</tt>. Consider increasing the height of this touch target to at least the configured minimum height of <tt>%2$ddp</tt>.
-result_message_customized_small_touch_target_width = This item\'s width is <tt>%1$ddp</tt>. Consider increasing the width of this touch target to at least the configured minimum width of <tt>%2$ddp</tt>.
-result_message_customized_small_touch_target_width_and_height = This item\'s size is <tt>%1$ddp</tt> x <tt>%2$ddp</tt>. Consider increasing this touch target to at least the configured minimum size of <tt>%3$ddp</tt> x <tt>%4$ddp</tt>.
-result_message_disruptive_announcement = A disruptive accessibility announcement has been used.
-result_message_editable_textview_content_desc = This editable <tt>TextView</tt> has an <tt>android:contentDescription</tt>. A screen reader may read this attribute instead of the editable content when the user is navigating.
-result_message_english_locale_only = This check only runs on devices with locales set to English.
-result_message_has_unexposed_items = This screen may have items that are not exposed to accessibility services.
-result_message_image_contrast_not_sufficient = The image\'s contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%3$06X</tt> and an estimated background color of <tt>#%4$06X</tt>. Consider increasing this ratio to %2$.2f or greater.
-result_message_image_customized_contrast_not_sufficient = The image\'s contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%3$06X</tt> and an estimated background color of <tt>#%4$06X</tt>. Consider increasing this ratio to the configured ratio of %2$.2f or greater.
-result_message_is_unexposed_item_screen_region = The region with on-screen location <tt>%1$s</tt> contains at least one item that is not exposed to accessibility services.
-result_message_item_exposed_needs_manual_assessment = This screen needs manual inspection to ensure all items are exposed to accessibility services.
-result_message_link_text_not_descriptive = The link text \"<tt>%1$s</tt>\" may not independently convey the link\'s purpose.
-result_message_low_reading_score = This item\'s text has an approximate readability score of %1$.0f, which is lower than the recommended score of %2$.0f. Consider using simpler words or sentences to make the text easier to read.
-result_message_missing_speakable_text = This item may not have a label readable by screen readers.
-result_message_no_content_desc = This item has no <tt>android:contentDescription</tt>.
-result_message_no_screencapture = Screen capture data could not be obtained.
-result_message_no_typeface_info = This item\'s typeface could not be determined.
-result_message_not_clickable = This view is not clickable.
-result_message_not_editable_textview = This item is not an editable <tt>TextView</tt>.
-result_message_not_enabled = This item isn\'t enabled.
-result_message_not_imageview = This item is not an <tt>ImageView</tt>.
-result_message_not_important_for_accessibility = This item was not found to be important for accessibility.
-result_message_not_text_view = This item is not a <tt>TextView</tt>.
-result_message_not_visible = This item is not visible.
-result_message_same_speakable_text = This %1$s item\'s speakable text: \"<tt>%2$s</tt>\" is identical to that of %3$d other item(s).
-result_message_same_view_bounds = This %1$s item has the same on-screen location (<tt>%2$s</tt>) as %3$d other item(s) with those properties.
-result_message_screencapture_data_hidden = Screen capture information for this item was hidden.
-result_message_screencapture_uniform_color = Screen capture has a uniform color.
-result_message_sdk_version_not_applicable = This check is not applicable on devices running Android %1$s and above.
-result_message_short_text = This item\'s text is too short to be evaluated.
-result_message_should_not_focus = This item would not be focused by a screen reader.
-result_message_small_touch_target_height = This item\'s height is <tt>%1$ddp</tt>. Consider making the height of this touch target <tt>%2$ddp</tt> or larger.
-result_message_small_touch_target_width = This item\'s width is <tt>%1$ddp</tt>. Consider making the width of this touch target <tt>%2$ddp</tt> or larger.
-result_message_small_touch_target_width_and_height = This item\'s size is <tt>%1$ddp</tt> x <tt>%2$ddp</tt>. Consider making this touch target <tt>%3$ddp</tt> wide and <tt>%4$ddp</tt> high or larger.
-result_message_speakable_text = This %1$s item also has speakable text: \"<tt>%2$s</tt>\".
-result_message_styled_text = This item may use %1$s font for a long passage of text. Consider removing the style from the font to improve readability.
-result_message_text_must_be_opaque = This item\'s text color is not opaque.
-result_message_textview_contrast_not_sufficient = The item\'s text contrast ratio is %1$.2f. This ratio is based on a text color of <tt>#%2$06X</tt> and background color of <tt>#%3$06X</tt>. Consider increasing this item\'s text contrast ratio to %4$.2f or greater.
-result_message_textview_empty = This <tt>TextView</tt> is empty.
-result_message_textview_heuristic_contrast_not_sufficient = The item\'s text contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%2$06X</tt> and an estimated background color of <tt>#%3$06X</tt>. Consider using colors that result in a contrast ratio greater than %4$.2f for small text, or %5$.2f for large text.
-result_message_textview_heuristic_contrast_not_sufficient_confirmed = The item\'s text contrast ratio is %1$.2f. This ratio is based on the provided foreground color of <tt>#%2$06X</tt> and provided background color of <tt>#%3$06X</tt>. Consider using colors that result in a contrast ratio greater than %4$.2f for small text, or %5$.2f for large text.
-result_message_textview_heuristic_customized_contrast_not_sufficient = The item\'s text contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%2$06X</tt> and an estimated background color of <tt>#%3$06X</tt>. Consider increasing this item\'s text contrast ratio to the configured ratio of %4$.2f or greater.
-result_message_textview_heuristic_customized_contrast_not_sufficient_confirmed = The item\'s text contrast ratio is %1$.2f. This ratio is based on the provided foreground color of <tt>#%2$06X</tt> and provided background color of <tt>#%3$06X</tt>. Consider increasing this item\'s text contrast ratio to the configured ratio of %4$.2f or greater.
-result_message_traversal_cycle = This item may be part of a traversal ordering cycle due to its <tt>%1$s</tt> attribute. Traversal behavior with screen readers may be unpredictable.
-result_message_traversal_over_constrained = Traversal ordering for this item may be over constrained based on its <tt>android:accessibilityTraversalBefore</tt> and <tt>android:accessibilityTraversalAfter</tt> attributes. Traversal behavior with screen readers may be unpredictable.
-result_message_urlspan_invalid_url = Verify that the URL within this item\'s <tt>URLSpan</tt> is valid.
-result_message_urlspan_not_clickablespan = This item should use a <tt>URLSpan</tt> in place of a <tt>ClickableSpan</tt>.
-result_message_view_bounds = This %1$s item also has an on-screen location of <tt>%2$s</tt>.
-result_message_view_not_within_screencapture = This item\'s on-screen location (<tt>%1$s</tt>) were not within the screen capture on-screen location (<tt>%2$s</tt>).
-result_message_web_content = Web content is not evaluated.
+unchecked_state = unchecked
 selected_state = selected
+unselected_state = unselected
+click_action = click
 swipe_action = swipe
 tap_action = tap
-unchecked_state = unchecked
+check_title_speakable_text_present = Item label
+result_message_should_not_focus = This item would not be focused by a screen reader.
+result_message_web_content = Web content is not evaluated.
+result_message_missing_speakable_text = This item may not have a label readable by screen readers.
+check_title_text_contrast = Text contrast
+result_message_textview_empty = This <tt>TextView</tt> is empty.
+result_message_could_not_get_text_color = This item\'s text color could not be determined.
+result_message_could_not_get_background_color = This item\'s background color could not be determined.
+result_message_text_must_be_opaque = This item\'s text color is not opaque.
+result_message_background_must_be_opaque = This items\'s background color is not opaque.
+result_message_addendum_opacity_description = Its actual opacity is %1$.2f%%.
+result_message_brief_text_contrast_not_sufficient = Consider increasing this item\'s text foreground to background contrast ratio.
+result_message_textview_contrast_not_sufficient = The item\'s text contrast ratio is %1$.2f. This ratio is based on a text color of <tt>#%2$06X</tt> and background color of <tt>#%3$06X</tt>. Consider increasing this item\'s text contrast ratio to %4$.2f or greater.
+result_message_customized_textview_contrast_not_sufficient = The item\'s text contrast ratio is %1$.2f. This ratio is based on a text color of <tt>#%2$06X</tt> and background color of <tt>#%3$06X</tt>. Consider using colors that result in a contrast ratio greater than the modified ratio of %4$.2f.
+result_message_textview_heuristic_contrast_not_sufficient = The item\'s text contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%2$06X</tt> and an estimated background color of <tt>#%3$06X</tt>. Consider using colors that result in a contrast ratio greater than %4$.2f for small text, or %5$.2f for large text.
+result_message_textview_heuristic_contrast_not_sufficient_when_text_size_available = The item\'s text contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%2$06X</tt> and an estimated background color of <tt>#%3$06X</tt>. Consider increasing this item\'s text contrast ratio to %4$.2f or greater.
+result_message_textview_heuristic_contrast_not_sufficient_confirmed = The item\'s text contrast ratio is %1$.2f. This ratio is based on the provided foreground color of <tt>#%2$06X</tt> and provided background color of <tt>#%3$06X</tt>. Consider using colors that result in a contrast ratio greater than %4$.2f for small text, or %5$.2f for large text.
+result_message_textview_heuristic_contrast_not_sufficient_when_text_size_available_confirmed = The item\'s text contrast ratio is %1$.2f. This ratio is based on the provided foreground color of <tt>#%2$06X</tt> and provided background color of <tt>#%3$06X</tt>. Consider increasing this item\'s text contrast ratio to %4$.2f or greater.
+result_message_textview_heuristic_customized_contrast_not_sufficient = The item\'s text contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%2$06X</tt> and an estimated background color of <tt>#%3$06X</tt>. Consider increasing this item\'s text contrast ratio to the modified ratio of %4$.2f or greater.
+result_message_textview_heuristic_customized_contrast_not_sufficient_confirmed = The item\'s text contrast ratio is %1$.2f. This ratio is based on the provided foreground color of <tt>#%2$06X</tt> and provided background color of <tt>#%3$06X</tt>. Consider increasing this item\'s text contrast ratio to the modified ratio of %4$.2f or greater.
+result_message_contrast_sufficient_confirmed = This item\'s foreground and background colors meet the suggested contrast ratio.
+result_message_customized_contrast_sufficient_confirmed = This item\'s foreground and background colors meet the modified contrast ratio.
+question_id_message_confirm_foreground_background_colors = Are the detected foreground and background colors correct?
+question_option_message_both_correct = Both correct
+question_option_message_foreground_incorrect = Foreground incorrect
+question_option_message_background_incorrect = Background incorrect
+question_option_message_both_incorrect = Foreground and background incorrect
+question_option_message_unknown = Unknown
+question_id_message_provide_foreground_color = What is the correct foreground color?
+question_id_message_provide_background_color = What is the correct background color?
+check_title_touch_target_size = Touch target
+result_message_not_clickable = This view is not clickable.
+result_message_brief_small_touch_target = Consider making this clickable item larger.
+result_message_small_touch_target_width_and_height = This item\'s size is <tt>%1$ddp</tt> x <tt>%2$ddp</tt>. Consider making this touch target <tt>%3$ddp</tt> wide and <tt>%4$ddp</tt> high or larger.
+result_message_small_touch_target_height = This item\'s height is <tt>%1$ddp</tt>. Consider making the height of this touch target <tt>%2$ddp</tt> or larger.
+result_message_small_touch_target_width = This item\'s width is <tt>%1$ddp</tt>. Consider making the width of this touch target <tt>%2$ddp</tt> or larger.
+result_message_customized_small_touch_target_width_and_height = This item\'s size is <tt>%1$ddp</tt> x <tt>%2$ddp</tt>. Consider increasing this touch target to at least the modified minimum size of <tt>%3$ddp</tt> x <tt>%4$ddp</tt>.
+result_message_customized_small_touch_target_height = This item\'s height is <tt>%1$ddp</tt>. Consider increasing the height of this touch target to at least the modified minimum height of <tt>%2$ddp</tt>.
+result_message_customized_small_touch_target_width = This item\'s width is <tt>%1$ddp</tt>. Consider increasing the width of this touch target to at least the modified minimum width of <tt>%2$ddp</tt>.
+result_message_addendum_touch_delegate = A <tt>TouchDelegate</tt> has been detected on one of this item\'s ancestors. This message can be ignored if the delegate is of sufficient size and handles touches for this item.
+result_message_addendum_touch_delegate_with_hit_rect = A <tt>TouchDelegate</tt> with size <tt>%1$ddp</tt> x <tt>%2$ddp</tt> has been detected for this item. Consider increasing the size of its hit <tt>Rect</tt>.
+result_message_addendum_clickable_ancestor = A parent container may be handling touch events for this item. If selecting the larger container performs the same action as selecting this item, consider defining this item as not clickable. If a different action is performed, consider increasing the size of this item.
+result_message_addendum_clipped_by_ancestor = A parent container may be clipping the size of this item, which has a drawing area of <tt>%1$ddp</tt> x <tt>%2$ddp</tt>. Consider increasing the size of this item\'s clipping ancestor, or allowing a larger parent container to handle actions on behalf of this item.
+result_message_addendum_against_scrollable_edge = This item may be only partially visible within a scrollable container.
+check_title_class_name_not_supported = Unsupported item type
+result_message_class_name_not_supported_brief = This item\'s type may not be supported.
+result_message_class_name_not_supported_detail = This item\'s type <tt>%1$s</tt> may not be resolvable by accessibility services. Consider using a type defined by the Android SDK.
+result_message_class_name_is_empty = This item\'s type may not be reported to accessibility services. Consider using a type defined by the Android SDK.
+result_message_class_name_is_unknown = This item\'s type could not be determined.
+check_title_accessibility_traversal = Traversal order
+result_message_traversal_cycle = This item may be part of a traversal ordering cycle due to its <tt>%1$s</tt> attribute. Traversal behavior with screen readers may be unpredictable.
+result_message_traversal_over_constrained = Traversal ordering for this item may be over constrained based on its <tt>android:accessibilityTraversalBefore</tt> and <tt>android:accessibilityTraversalAfter</tt> attributes. Traversal behavior with screen readers may be unpredictable.
+result_message_brief_unpredictable_traversal = Traversal behavior with screen readers may be unpredictable.
+result_message_disruptive_announcement = A disruptive accessibility announcement has been used.
+check_view_banned_word = Banned word
+result_message_banned_word = This item\'s text may contain an inappropriate word, "<tt>%1$s</tt>"
+result_message_brief_banned_word = Consider removing inappropriate words from this item\'s text
+check_title_text_style = Text Style
+result_message_styled_text = This item may use %1$s font for a long passage of text. Consider removing the style from the font to improve readability.
+result_message_brief_styled_text = Consider removing %1$s styling on longer passages of text.
+italic_text = italic
 underline_text = underline
-unselected_state = unselected
+italic_underline_text = italic and underline
+result_message_no_typeface_info = This item\'s typeface could not be determined.
+check_title_link_test = Link text
+result_message_brief_link_text_not_descriptive = Consider using more descriptive text in the link.
+result_message_link_text_not_descriptive = The link text \"<tt>%1$s</tt>\" may not independently convey the link\'s purpose.
+check_title_reading_score = Readability
+result_message_low_reading_score = This item\'s text has an approximate readability score of %1$.0f, which is lower than the recommended score of %2$.0f. Consider using simpler words or sentences to make the text easier to read.
+result_message_brief_low_reading_score = This text may have a low readability score.
+result_message_short_text = This item\'s text is too short to be evaluated.
+result_message_item_exposed_needs_manual_assessment = This screen needs manual inspection to ensure all items are exposed to accessibility services.
+result_message_has_unexposed_items = This screen may have items that are not exposed to accessibility services.
+result_message_is_unexposed_item_screen_region = The region with on-screen location <tt>%1$s</tt> contains at least one item that is not exposed to accessibility services.
+check_title_item_exposed = Exposed items
+result_message_brief_is_unexposed_item_screen_region = Consider exposing items in this region to accessibility services.
+check_title_text_size = Text Size
+question_message_screen_has_unexposed_items = Does this screen contain important items that are not outlined?
+question_message_identify_unexposed_items = Select regions of the screen with unidentified items.
+suggestion_remove_view_attribute = Remove this item\'s <tt>%1$s</tt>.
+suggestion_set_view_attribute = Set this item\'s <tt>%1$s</tt> to <tt>%2$s</tt>.
+suggestion_set_view_attribute_with_an_non_empty_string = Set this item\'s <tt>%1$s</tt> to a meaningful non-empty string or resource reference.
diff --git a/validator/resources/values.xml b/validator/resources/values.xml
deleted file mode 100644
index 55dcf57..0000000
--- a/validator/resources/values.xml
+++ /dev/null
@@ -1,155 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 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.
-  -->
-<resources xmlns:ns1="urn:oasis:names:tc:xliff:document:1.2">
-    <string description="Describes a UI element on which an action may be performed by the user [CHAR LIMIT=NONE]" name="actionable">actionable</string>
-    <string description="The term for a UI element that functions as a button. [CHAR LIMIT=NONE]" name="button_item_type">button</string>
-    <string description="The title of a check on the accessiblity traversal constraints on the elements in a view. [CHAR LIMIT=50]" name="check_title_accessibility_traversal">Traversal order</string>
-    <string description="The title of a check describing that the class name is not supported by the accessibility service. [CHAR LIMIT=50]" name="check_title_class_name_not_supported">Unsupported item type</string>
-    <string description="The title of a check describing that views should use the android concept of a &apos;URLSpan&apos; instead of a &apos;ClickableSpan&apos;, for improved accessibility. [CHAR LIMIT=50]" name="check_title_clickablespan">Link</string>
-    <string description="The title of a check describing that multiple clickable views (UI elements) share the exact same space on the screen. [CHAR LIMIT=50]" name="check_title_duplicate_clickable_bounds">Clickable items</string>
-    <string description="The title of a check describing that this view (UI element) has the same text to be spoken by a screen reader as another view on the screen. [CHAR LIMIT=50]" name="check_title_duplicate_speakable_text">Item descriptions</string>
-    <string description="The title of a check describing that this editable view (UI element) should not populate its &apos;android:contentDescription&apos; attribute. [CHAR LIMIT=50]" name="check_title_editable_content_desc">Editable item label</string>
-    <string description="The title of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of the image in this view (UI element) is lower than the required ratio. [CHAR LIMIT=50]" name="check_title_image_contrast">Image contrast</string>
-    <string description="The title of a check describing that UI elements may be unexposed to accessibility services [CHAR LIMIT=NONE]" name="check_title_item_exposed">Exposed items</string>
-    <string description="The title of a check on link text. [CHAR LIMIT=50]" name="check_title_link_test">Link text</string>
-    <string description="The title of a check used to determine readability of text based on reading score. [CHAR LIMIT=50]" name="check_title_reading_score">Readability</string>
-    <string description="The title of a check describing that this view (UI element) has redundant or unnecessary text within its &apos;android:contentDescription&apos; attribute. [CHAR LIMIT=50]" name="check_title_redundant_description">Item type label</string>
-    <string description="The title of a check describing that this View (UI element) has no description that would be spoken to the user by a screen reader if this view were to be focused. [CHAR LIMIT=50]" name="check_title_speakable_text_present">Item label</string>
-    <string description="The title of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of this TextView (UI element) is lower than the required ratio. [CHAR LIMIT=50]" name="check_title_text_contrast">Text contrast</string>
-    <string description="The title of a check on the styling of text. [CHAR LIMIT=50]" name="check_title_text_style">Text Style</string>
-    <string description="The title of a check describing that the size of this clickable view (UI element) is below minimum requirements. [CHAR LIMIT=50]" name="check_title_touch_target_size">Touch target</string>
-    <string description="The title of a check used to detect banned words within the text of a view. [CHAR LIMIT=50]" name="check_view_banned_word">Banned word</string>
-    <string description="The term for a UI element that functions as a checkbox, as a single word (ex. &apos;checkbox&apos; instead of &apos;check box&apos; in English). If there isn&apos;t a single word representation in the target language, use multiple words. [CHAR LIMIT=NONE]" name="checkbox_item_type">checkbox</string>
-    <string description="The term for a UI element that functions as a checkbox, as separate words (ex. &apos;check box&apos; instead of &apos;checkbox&apos; in English). If there isn&apos;t a multiple word representation in the target language, use a single word. [CHAR LIMIT=NONE]" name="checkbox_item_type_separate_words">check box</string>
-    <string description="Adjective describing a checkbox that is selected. [CHAR LIMIT=NONE]" name="checked_state">checked</string>
-    <string description="An action in a UI, as in the imperative &apos;click to send&apos;. [CHAR LIMIT=NONE]" name="click_action">click</string>
-    <string description="Describes a UI element that is clickable [CHAR LIMIT=NONE]" name="clickable">clickable</string>
-    <string description="Describes a UI element that is both clickable and long clickable [CHAR LIMIT=NONE]" name="clickable_and_long_clickable">clickable and long clickable</string>
-    <string description="The description for a fix suggestion which recommends removing a view attribute [CHAR LIMIT=NONE]" name="description_remove_view_attribute" translatable="false">Remove the view attribute &lt;tt><ns1:g example="android:contentDescription" id="view_attribute_fully_qualified_name">%1$s</ns1:g>&lt;/tt>.</string>
-    <string description="The description for a fix suggestion which recommends setting a value to a view attribute [CHAR LIMIT=NONE]" name="description_set_view_attribute" translatable="false">Set the view attribute &lt;tt><ns1:g example="android:contentDescription" id="view_attribute_fully_qualified_name">%1$s</ns1:g>&lt;/tt> to &lt;tt><ns1:g example="#FFFFFF" id="suggested_value">%2$s</ns1:g>&lt;/tt>.</string>
-    <string description="The description for a fix suggestion which recommends asking the developer to set the view attribute to a meaningful non-empty String literal or relevant resource [CHAR LIMIT=NONE]" name="description_set_view_attribute_with_an_empty_string" translatable="false">Set the view attribute &lt;tt><ns1:g example="android:contentDescription" id="view_attribute_fully_qualified_name">%1$s</ns1:g>&lt;/tt> to a meaningful non-empty string or resource reference.</string>
-    <string description="Describes italic text styling. [CHAR LIMIT=NONE]" name="italic_text">italic</string>
-    <string description="Describes text with both italic and underlined styling. [CHAR LIMIT=NONE]" name="italic_underline_text">italic and underline</string>
-    <string description="Describes a UI element that is long clickable [CHAR LIMIT=NONE]" name="long_clickable">long clickable</string>
-    <string description="Describes a UI element that is not clickable [CHAR LIMIT=NONE]" name="non_clickable">non-clickable</string>
-    <string description="The question message for question of if the foreground and background colors are correct [CHAR LIMIT=NONE]" name="question_id_message_confirm_foreground_background_colors">Are the detected foreground and background colors correct?</string>
-    <string description="The question prompting a user to provide a background color [CHAR LIMIT=NONE]" name="question_id_message_provide_background_color">What is the correct background color?</string>
-    <string description="The question prompting a user to provide a foreground color [CHAR LIMIT=NONE]" name="question_id_message_provide_foreground_color">What is the correct foreground color?</string>
-    <string description="The question message for question prompting the identification of screen regions with unexposed elements [CHAR LIMIT=NONE]" name="question_message_identify_unexposed_items">Select regions of the screen with unidentified items.</string>
-    <string description="The question message for question of if there are unexposed elements in a view [CHAR LIMIT=NONE]" name="question_message_screen_has_unexposed_items">Does this screen contain important items that are not outlined?</string>
-    <string description="The user selection option for if only the background color is incorrect. [CHAR LIMIT=NONE]" name="question_option_message_background_incorrect">Background incorrect</string>
-    <string description="The user selection option for if the foreground and background colors are both correct [CHAR LIMIT=NONE]" name="question_option_message_both_correct">Both correct</string>
-    <string description="The user selection option for if the foreground and background colors are incorrect.[CHAR LIMIT=NONE]" name="question_option_message_both_incorrect">Foreground and background incorrect</string>
-    <string description="The user selection option for if only the foreground color is incorrect. [CHAR LIMIT=NONE]" name="question_option_message_foreground_incorrect">Foreground incorrect</string>
-    <string description="The user selection option for if it is unknown if the foreground and background colors are both incorrect.[CHAR LIMIT=NONE]" name="question_option_message_unknown">Unknown</string>
-    <string description="A message that is appended to a result message of a check indicating the view (UI element) may only be partially visible and is against the scrollable edge of a parent (a larger containing UI element). [CHAR LIMIT=NONE]" name="result_message_addendum_against_scrollable_edge">This item may be only partially visible within a scrollable container.</string>
-    <string description="A message that is appended to a result message of a check describing that the result may be ignored in the case where this view&apos;s (UI element) parent (a larger containing UI element) performs the same action of this view when clicked. [CHAR LIMIT=NONE]" name="result_message_addendum_clickable_ancestor">A parent container may be handling touch events for this item. If selecting the larger container performs the same action as selecting this item, consider defining this item as not clickable. If a different action is performed, consider increasing the size of this item.</string>
-    <string description="A message that is appended to a result message of a check describing that this view&apos;s (UI element) parent (a containing UI element) is clipping (constraining in size) the size of this item. [CHAR LIMIT=NONE]" name="result_message_addendum_clipped_by_ancestor">A parent container may be clipping the size of this item, which has a drawing area of <ns1:g example="30dp" id="nonclipped_width">&lt;tt>%1$ddp&lt;/tt></ns1:g> x <ns1:g example="30dp" id="nonclipped_height">&lt;tt>%2$ddp&lt;/tt></ns1:g>. Consider increasing the size of this item\'s clipping ancestor, or allowing a larger parent container to handle actions on behalf of this item.</string>
-    <string description="A description of a view&apos;s (UI element&apos;s) opacity including level of translucency. [CHAR LIMIT=NONE]" name="result_message_addendum_opacity_description">Its actual opacity is <ns1:g example="50" id="opacity">%1$.2f</ns1:g>%%.</string>
-    <string description="A message that is appended to a result message of a check describing that the result may be inaccurate because this view (UI element) uses a &apos;TouchDelegate&apos;, which allows a developer to define their own touchable region for the view. [CHAR LIMIT=NONE]" name="result_message_addendum_touch_delegate">A <ns1:g example="TouchDelegate" id="touch_delegate_class">&lt;tt>TouchDelegate&lt;/tt></ns1:g> has been detected on one of this item\'s ancestors. This message can be ignored if the delegate is of sufficient size and handles touches for this item.</string>
-    <string description="A message that is appended to a result message of a check describing that the result may be inaccurate because this view (UI element) uses a &apos;TouchDelegate&apos;, which allows a developer to define their own touchable region for the view. [CHAR LIMIT=NONE]" name="result_message_addendum_touch_delegate_with_hit_rect">A <ns1:g example="TouchDelegate" id="touch_delegate_class">&lt;tt>TouchDelegate&lt;/tt></ns1:g> with size <ns1:g example="30dp" id="hit_rect_width">&lt;tt>%1$ddp&lt;/tt></ns1:g> x <ns1:g example="30dp" id="hit_rect_height">&lt;tt>%2$ddp&lt;/tt></ns1:g> has been detected for this item. Consider increasing the size of its hit <ns1:g example="Rect" id="rect_class">&lt;tt>Rect&lt;/tt></ns1:g>.</string>
-    <string description="A message that is appended to a result message of a check describing that the view (UI element) may be obscured by other on-screen content. [CHAR LIMIT=NONE]" name="result_message_addendum_view_potentially_obscured">This item may be obscured by other on-screen content. Consider manually testing this item\'s contrast.</string>
-    <string description="The result message of a check describing that this view&apos;s background is non-opaque/translucent. [CHAR LIMIT=NONE]" name="result_message_background_must_be_opaque">This items\'s background color is not opaque.</string>
-    <string description="The result message of a check describing that the word used in this string is inappropriate. [CHAR LIMIT=NONE]" name="result_message_banned_word">This item\'s text may contain an inappropriate word, "<ns1:g example="heck" id="banned_word">&lt;tt>%1$s&lt;/tt></ns1:g>"</string>
-    <string description="The brief result message describing an inappropriate word being used. [CHAR LIMIT=NONE]" name="result_message_brief_banned_word">Consider removing inappropriate words from this item\'s text</string>
-    <string description="The brief result message of a check describing that this view (UI element) has redundant or unnecessary text within its &apos;android:contentDescription&apos; attribute. [CHAR LIMIT=NONE]" name="result_message_brief_content_desc_contains_redundant_word">This item\'s <ns1:g example="android:contentDescription" id="content_description_attr">&lt;tt>android:contentDescription&lt;/tt></ns1:g> might contain unnecessary text.</string>
-    <string description="The brief result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of the image in this view (UI element) is lower than the required ratio. [CHAR LIMIT=NONE]" name="result_message_brief_image_contrast_not_sufficient">Consider increasing the contrast ratio between this image\'s foreground and background.</string>
-    <string description="The brief result message of a check describing that an element is not exposed to accessibility services [CHAR LIMIT=NONE]" name="result_message_brief_is_unexposed_item_screen_region">Consider exposing items in this region to accessibility services.</string>
-    <string description="The brief result message of a check describing that link text is not descriptive. [CHAR LIMIT=NONE]" name="result_message_brief_link_text_not_descriptive">Consider using more descriptive text in the link.</string>
-    <string description="The brief result message if text has a low readability score. [CHAR LIMIT=NONE]" name="result_message_brief_low_reading_score">This text may have a low readability score.</string>
-    <string description="The brief result message of a check describing that multiple items (UI elements) share the same description that would be spoken by a screen reader. [CHAR LIMIT=NONE]" name="result_message_brief_same_speakable_text">Multiple items have the same description.</string>
-    <string description="The brief result message of a check describing that multiple actionable items (UI elements) share the same space on the screen. [CHAR LIMIT=NONE]" name="result_message_brief_same_view_bounds">Multiple <ns1:g example="clickable and long clickable" id="clickability">%1$s</ns1:g> items share this location on the screen.</string>
-    <string description="The brief result message of a check describing that the size of this view (UI element) may be too small to be touched or interacted with reliably. [CHAR LIMIT=NONE]" name="result_message_brief_small_touch_target">Consider making this clickable item larger.</string>
-    <string description="The brief message describing that using bold typeface is ideal. [CHAR LIMIT=NONE]" name="result_message_brief_styled_text">Consider removing <ns1:g example="italic" id="style_info">%1$s</ns1:g> styling on longer passages of text.</string>
-    <string description="The brief result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of the text in this TextView (UI element) is lower than the required ratio. [CHAR LIMIT=NONE]" name="result_message_brief_text_contrast_not_sufficient">Consider increasing this item\'s text foreground to background contrast ratio.</string>
-    <string description="The brief result message of a check describing that this view may be presented incorrectly to the user when traversed with a screen reader. [CHAR LIMIT=NONE]" name="result_message_brief_unpredictable_traversal">Traversal behavior with screen readers may be unpredictable.</string>
-    <string description="The result message of a check describing that the class name is empty and not supported by the accessibility service. [CHAR LIMIT=NONE]" name="result_message_class_name_is_empty">This item\'s type may not be reported to accessibility services. Consider using a type defined by the Android SDK.</string>
-    <string description="The result message of a check describing that the class name is unknown. [CHAR LIMIT=NONE]" name="result_message_class_name_is_unknown">This item\'s type could not be determined.</string>
-    <string description="The result message of a check describing that the class name is not supported by the accessibility service. [CHAR LIMIT=NONE]" name="result_message_class_name_not_supported_brief">This item\'s type may not be supported.</string>
-    <string description="The result message of a check describing that the class name is not supported by the accessibility service. [CHAR LIMIT=NONE]" name="result_message_class_name_not_supported_detail">This item\'s type <ns1:g example="com.example.MyButton" id="class_name">&lt;tt>%1$s&lt;/tt></ns1:g> may not be resolvable by accessibility services. Consider using a type defined by the Android SDK.</string>
-    <string description="The result message of a check describing the specific android class of the view (UI element) could not be determined. [CHAR LIMIT=NONE]" name="result_message_clickablespan_no_determined_type">This item\'s type is undetermined.</string>
-    <string description="The result message of a check stating that a description of a view (UI element) might specify an available action within its &apos;android:contentDescription&apos; attribute. [CHAR LIMIT=NONE]" name="result_message_content_desc_contains_action">
-    This item\'s <ns1:g example="android:contentDescription" id="content_description_attr">&lt;tt>android:contentDescription&lt;/tt></ns1:g>,
-    \"<ns1:g example="Tap to send" id="content_desc">&lt;tt>%1$s&lt;/tt></ns1:g>\",
-    contains the action \"<ns1:g example="tap" id="item_action">&lt;tt>%2$s&lt;/tt></ns1:g>\".
-  </string>
-    <string description="The result message of a check stating that a description of a view (UI element) has redundant or unnecessary text (ex: a view of type Button whose &apos;android:contentDescription&apos; is &apos;Save button&apos;). [CHAR LIMIT=NONE]" name="result_message_content_desc_contains_redundant_word">This item\'s <ns1:g example="android:contentDescription" id="content_description_attr">&lt;tt>android:contentDescription&lt;/tt></ns1:g>, \"<ns1:g example="Save button" id="content_desc">&lt;tt>%1$s&lt;/tt></ns1:g>\" contains the item type \"<ns1:g example="button" id="item_type">&lt;tt>%2$s&lt;/tt></ns1:g>\".</string>
-    <string description="The result message of a check stating that a description of a view (UI element) might specify an element state within its &apos;android:contentDescription&apos; attribute. [CHAR LIMIT=NONE]" name="result_message_content_desc_contains_state">
-    This item\'s <ns1:g example="android:contentDescription" id="content_description_attr">&lt;tt>android:contentDescription&lt;/tt></ns1:g>,
-    \"<ns1:g example="Rush delivery selected" id="content_desc">&lt;tt>%1$s&lt;/tt></ns1:g>\",
-    contains the state \"<ns1:g example="selected" id="item_state">&lt;tt>%2$s&lt;/tt></ns1:g>\".
-  </string>
-    <string description="The result message of a check stating that a description of a view (UI element) ends with that view&apos;s type (ex: a view of type Button whose &apos;android:contentDescription&apos; is &apos;Save button&apos;). [CHAR LIMIT=NONE]" name="result_message_content_desc_ends_with_view_type">This item\'s <ns1:g example="android:contentDescription" id="content_description_attr">&lt;tt>android:contentDescription&lt;/tt></ns1:g>, \"<ns1:g example="Save button" id="content_desc">&lt;tt>%1$s&lt;/tt></ns1:g>\" ends with the item\'s type.</string>
-    <string description="The result message of a check describing that the background color of a view (UI element) could not be determined. [CHAR LIMIT=NONE]" name="result_message_could_not_get_background_color">This item\'s background color could not be determined.</string>
-    <string description="The result message of a check describing that the color of the text within a view (UI element) could not be determined. [CHAR LIMIT=NONE]" name="result_message_could_not_get_text_color">This item\'s text color could not be determined.</string>
-    <string description="The result message of a check describing that the height of this view (UI element) is below the user-defined minimum requirement. [CHAR LIMIT=NONE]" name="result_message_customized_small_touch_target_height">This item\'s height is <ns1:g example="30dp" id="view_height">&lt;tt>%1$ddp&lt;/tt></ns1:g>. Consider increasing the height of this touch target to at least the configured minimum height of <ns1:g example="48dp" id="recommended_minimum_height">&lt;tt>%2$ddp&lt;/tt></ns1:g>.</string>
-    <string description="The result message of a check describing that the width of this view (UI element) is below the user-defined minimum requirement. [CHAR LIMIT=NONE]" name="result_message_customized_small_touch_target_width">This item\'s width is <ns1:g example="30dp" id="view_width">&lt;tt>%1$ddp&lt;/tt></ns1:g>. Consider increasing the width of this touch target to at least the configured minimum width of <ns1:g example="48dp" id="recommended_minimum_width">&lt;tt>%2$ddp&lt;/tt></ns1:g>.</string>
-    <string description="The result message of a check describing that the width and height of this view (UI element) are both below user-defined minimum requirements. [CHAR LIMIT=NONE]" name="result_message_customized_small_touch_target_width_and_height">This item\'s size is <ns1:g example="30dp" id="view_width">&lt;tt>%1$ddp&lt;/tt></ns1:g> x <ns1:g example="30dp" id="view_height">&lt;tt>%2$ddp&lt;/tt></ns1:g>. Consider increasing this touch target to at least the configured minimum size of <ns1:g example="32dp" id="recommended_view_width">&lt;tt>%3$ddp&lt;/tt></ns1:g> x <ns1:g example="32dp" id="recommended_view_height">&lt;tt>%4$ddp&lt;/tt></ns1:g>.</string>
-    <string description="The result message of a check describing that an event can be disruptive to the user. [CHAR LIMIT=NONE]" name="result_message_disruptive_announcement">A disruptive accessibility announcement has been used.</string>
-    <string description="The result message of a check describing that this editable view (UI element) should not populate its &apos;android:contentDescription&apos; attribute. [CHAR LIMIT=NONE]" name="result_message_editable_textview_content_desc">This editable <ns1:g example="TextView" id="text_view_class">&lt;tt>TextView&lt;/tt></ns1:g> has an <ns1:g example="android:contentDescription" id="content_description_tag">&lt;tt>android:contentDescription&lt;/tt></ns1:g>. A screen reader may read this attribute instead of the editable content when the user is navigating.</string>
-    <string description="The result message of a check describing that this check can only be run in English locales. [CHAR LIMIT=NONE]" name="result_message_english_locale_only">This check only runs on devices with locales set to English.</string>
-    <string description="The result message if user was uncertain if a screen has any UI elements that were not exposed properly to accessibility services. [CHAR LIMIT=NONE]" name="result_message_has_unexposed_items">This screen may have items that are not exposed to accessibility services.</string>
-    <string description="The result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of the image in this view (UI element) is lower than the required ratio. [CHAR LIMIT=NONE]" name="result_message_image_contrast_not_sufficient">The image\'s contrast ratio is <ns1:g example="2.9" id="contrast_ratio">%1$.2f</ns1:g>. This ratio is based on an estimated foreground color of <ns1:g example="#000000" id="foreground_color">&lt;tt>#%3$06X&lt;/tt></ns1:g> and an estimated background color of <ns1:g example="#FFFFFF" id="background_color">&lt;tt>#%4$06X&lt;/tt></ns1:g>. Consider increasing this ratio to <ns1:g example="3.0" id="recommended_contrast_ratio">%2$.2f</ns1:g> or greater.</string>
-    <string description="The result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of the image in this view (UI element) is lower than a user-defined ratio. [CHAR LIMIT=NONE]" name="result_message_image_customized_contrast_not_sufficient">The image\'s contrast ratio is <ns1:g example="2.9" id="contrast_ratio">%1$.2f</ns1:g>. This ratio is based on an estimated foreground color of <ns1:g example="#000000" id="foreground_color">&lt;tt>#%3$06X&lt;/tt></ns1:g> and an estimated background color of <ns1:g example="#FFFFFF" id="background_color">&lt;tt>#%4$06X&lt;/tt></ns1:g>. Consider increasing this ratio to the configured ratio of <ns1:g example="3.0" id="customized_heuristic_contrast_ratio">%2$.2f</ns1:g> or greater.</string>
-    <string description="The result message if a screen region contains an unexposed element [CHAR LIMIT=NONE]" name="result_message_is_unexposed_item_screen_region">The region with on-screen location <ns1:g example="[0,0][1920,1080]" id="region_bounds">&lt;tt>%1$s&lt;/tt></ns1:g> contains at least one item that is not exposed to accessibility services.</string>
-    <string description="The result message indicating that the user must provide more information to determine if all UI elements within a view hierarchy are exposed properly to accessibility services. [CHAR LIMIT=NONE]" name="result_message_item_exposed_needs_manual_assessment">This screen needs manual inspection to ensure all items are exposed to accessibility services.</string>
-    <string description="The result message of a check describing that link text is not descriptive. [CHAR LIMIT=NONE]" name="result_message_link_text_not_descriptive">The link text \"<ns1:g example="This is an arbitrary string." id="link_text">&lt;tt>%1$s&lt;/tt></ns1:g>\" may not independently convey the link\'s purpose.</string>
-    <string description="The full result message if text has a low readability score. [CHAR LIMIT=NONE]" name="result_message_low_reading_score">This item\'s text has an approximate readability score of <ns1:g example="32" id="text_reading_score">%1$.0f</ns1:g>, which is lower than the recommended score of <ns1:g example="70" id="target_reading_score">%2$.0f</ns1:g>. Consider using simpler words or sentences to make the text easier to read.</string>
-    <string description="The result message of a check describing that this View (UI element) has no description that would be spoken to the user by a screen reader if this view were to be focused. [CHAR LIMIT=NONE]" name="result_message_missing_speakable_text">This item may not have a label readable by screen readers.</string>
-    <string description="The result message of a check describing that this view (UI element) does not have any text set for the android contentDescription attribute. [CHAR LIMIT=NONE]" name="result_message_no_content_desc">This item has no <ns1:g example="android:contentDescription" id="content_description_attr">&lt;tt>android:contentDescription&lt;/tt></ns1:g>.</string>
-    <string description="The result message of a check describing that we were unable to obtain a screenshot. [CHAR LIMIT=NONE]" name="result_message_no_screencapture">Screen capture data could not be obtained.</string>
-    <string description="The message if this check was unable to get typeface info for a TextView. [CHAR LIMIT=NONE]" name="result_message_no_typeface_info">This item\'s typeface could not be determined.</string>
-    <string description="The result message of a check describing that this view (UI element) is not clickable. [CHAR LIMIT=NONE]" name="result_message_not_clickable">This view is not clickable.</string>
-    <string description="The result message of a check describing that this view (UI element) does not contain editable text content. [CHAR LIMIT=NONE]" name="result_message_not_editable_textview">This item is not an editable <ns1:g example="TextView" id="text_view_class">&lt;tt>TextView&lt;/tt></ns1:g>.</string>
-    <string description="The result message of a check describing that this view (UI element) is in a &apos;not enabled&apos; or &apos;disabled&apos; state. [CHAR LIMIT=NONE]" name="result_message_not_enabled">This item isn\'t enabled.</string>
-    <string description="The result message of a check describing that this view (UI element) is not an instance of the android class ImageView. [CHAR LIMIT=NONE]" name="result_message_not_imageview">This item is not an <ns1:g example="ImageView" id="image_view_class">&lt;tt>ImageView&lt;/tt></ns1:g>.</string>
-    <string description="The result message of a check describing that this view (UI element) is not exposed to a screen reader or other accessibility service. [CHAR LIMIT=NONE]" name="result_message_not_important_for_accessibility">This item was not found to be important for accessibility.</string>
-    <string description="The result message of a check describing that this view (UI element) is not an instance of the android TextView class [CHAR LIMIT=NONE]" name="result_message_not_text_view">This item is not a <ns1:g example="TextView" id="text_view_class">&lt;tt>TextView&lt;/tt></ns1:g>.</string>
-    <string description="The result message of a check describing that this view (UI element) is not currently visible to the user. [CHAR LIMIT=NONE]" name="result_message_not_visible">This item is not visible.</string>
-    <string description="The result message of a check describing that this view (UI element) has the same text to be spoken by a screen reader as another view which has the same clickability. [CHAR LIMIT=NONE]" name="result_message_same_speakable_text">This <ns1:g example="clickable" id="clickability">%1$s</ns1:g> item\'s speakable text: \"<ns1:g example="This is an arbitrary string." id="speakable_text">&lt;tt>%2$s&lt;/tt></ns1:g>\" is identical to that of <ns1:g example="2" id="num_views">%3$d</ns1:g> other item(s).</string>
-    <string description="The result message of a check describing that this view (UI element) shares exact same space on the screen as another view which has the same clickability and long clickability. [CHAR LIMIT=NONE]" name="result_message_same_view_bounds">This <ns1:g example="clickable and long clickable" id="clickability">%1$s</ns1:g> item has the same on-screen location (<ns1:g example="[0,0][100,100]" id="view_bounds">&lt;tt>%2$s&lt;/tt></ns1:g>) as <ns1:g example="2" id="num_views">%3$d</ns1:g> other item(s) with those properties.</string>
-    <string description="The result message of a check describing that the screenshot of a view (UI element) was hidden. [CHAR LIMIT=NONE]" name="result_message_screencapture_data_hidden">Screen capture information for this item was hidden.</string>
-    <string description="The result message of a check describing that the screenshot of a view (UI element) has a uniform color. [CHAR LIMIT=NONE]" name="result_message_screencapture_uniform_color">Screen capture has a uniform color.</string>
-    <string description="The result message of a check describing that the check is not applicable since specific Android SDK version. [CHAR LIMIT=NONE]" name="result_message_sdk_version_not_applicable">This check is not applicable on devices running Android <ns1:g example="8.0" id="android_version">%1$s</ns1:g> and above.</string>
-    <string description="The result message if text is too short to run a check on. [CHAR LIMIT=NONE]" name="result_message_short_text">This item\'s text is too short to be evaluated.</string>
-    <string description="The result message of a check describing that a screen reader would not focus this view (UI element). [CHAR LIMIT=NONE]" name="result_message_should_not_focus">This item would not be focused by a screen reader.</string>
-    <string description="The result message of a check describing that the height of this view (UI element) is below the minimum requirement. [CHAR LIMIT=NONE]" name="result_message_small_touch_target_height">This item\'s height is <ns1:g example="30dp" id="view_height">&lt;tt>%1$ddp&lt;/tt></ns1:g>. Consider making the height of this touch target <ns1:g example="48dp" id="recommended_minimum_height">&lt;tt>%2$ddp&lt;/tt></ns1:g> or larger.</string>
-    <string description="The result message of a check describing that the width of this view (UI element) is below the minimum requirement. [CHAR LIMIT=NONE]" name="result_message_small_touch_target_width">This item\'s width is <ns1:g example="30dp" id="view_width">&lt;tt>%1$ddp&lt;/tt></ns1:g>. Consider making the width of this touch target <ns1:g example="48dp" id="recommended_minimum_width">&lt;tt>%2$ddp&lt;/tt></ns1:g> or larger.</string>
-    <string description="The result message of a check describing that the width and height of this view (UI element) are both below minimum requirements. [CHAR LIMIT=NONE]" name="result_message_small_touch_target_width_and_height">This item\'s size is <ns1:g example="30dp" id="view_width">&lt;tt>%1$ddp&lt;/tt></ns1:g> x <ns1:g example="30dp" id="view_height">&lt;tt>%2$ddp&lt;/tt></ns1:g>. Consider making this touch target <ns1:g example="32dp" id="recommended_view_width">&lt;tt>%3$ddp&lt;/tt></ns1:g> wide and <ns1:g example="32dp" id="recommended_view_height">&lt;tt>%4$ddp&lt;/tt></ns1:g> high or larger.</string>
-    <string description="The result message of a check describing the text that would be spoken to the user by a screen reader if this view (UI element) were to be focused. [CHAR LIMIT=NONE]" name="result_message_speakable_text">This <ns1:g example="clickable and long clickable" id="clickability">%1$s</ns1:g> item also has speakable text: \"<ns1:g example="This is an arbitrary string." id="speakable_text">&lt;tt>%2$s&lt;/tt></ns1:g>\".</string>
-    <string description="The full result message if text uses italic style. [CHAR LIMIT=NONE]" name="result_message_styled_text">This item may use <ns1:g example="italic" id="style_info">%1$s</ns1:g> font for a long passage of text. Consider removing the style from the font to improve readability.</string>
-    <string description="The result message of a check describing that this view&apos;s text is non-opaque/translucent. [CHAR LIMIT=NONE]" name="result_message_text_must_be_opaque">This item\'s text color is not opaque.</string>
-    <string description="The result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of this TextView (UI element) is lower than the required ratio. [CHAR LIMIT=NONE]" name="result_message_textview_contrast_not_sufficient">The item\'s text contrast ratio is <ns1:g example="2.9" id="contrast_ratio">%1$.2f</ns1:g>. This ratio is based on a text color of <ns1:g example="#000000" id="text_color">&lt;tt>#%2$06X&lt;/tt></ns1:g> and background color of <ns1:g example="#FFFFFF" id="background_color">&lt;tt>#%3$06X&lt;/tt></ns1:g>. Consider increasing this item\'s text contrast ratio to <ns1:g example="3.0" id="recommended_contrast_ratio">%4$.2f</ns1:g> or greater.</string>
-    <string description="The result message of a check describing that this TextView (UI element) has no text. [CHAR LIMIT=NONE]" name="result_message_textview_empty">This <ns1:g example="TextView" id="text_view_class">&lt;tt>TextView&lt;/tt></ns1:g> is empty.</string>
-    <string description="The result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of this TextView (UI element) is lower than the required ratio unless its text is considered to be large. [CHAR LIMIT=NONE]" name="result_message_textview_heuristic_contrast_not_sufficient">The item\'s text contrast ratio is <ns1:g example="2.9" id="contrast_ratio">%1$.2f</ns1:g>. This ratio is based on an estimated foreground color of <ns1:g example="#FFFFFF" id="foreground_color">&lt;tt>#%2$06X&lt;/tt></ns1:g> and an estimated background color of <ns1:g example="#000000" id="background_color">&lt;tt>#%3$06X&lt;/tt></ns1:g>. Consider using colors that result in a contrast ratio greater than <ns1:g example="4.5" id="recommended_contrast_ratio">%4$.2f</ns1:g> for small text, or <ns1:g example="3.0" id="tolerant_contrast_ratio">%5$.2f</ns1:g> for large text.</string>
-    <string description="The result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of this TextView (UI element) is lower than the required ratio unless its text is considered to be large. Result based on colors obtained or confirmed through questions. [CHAR LIMIT=NONE]" name="result_message_textview_heuristic_contrast_not_sufficient_confirmed">The item\'s text contrast ratio is <ns1:g example="2.9" id="contrast_ratio">%1$.2f</ns1:g>. This ratio is based on the provided foreground color of <ns1:g example="#FFFFFF" id="foreground_color">&lt;tt>#%2$06X&lt;/tt></ns1:g> and provided background color of <ns1:g example="#000000" id="background_color">&lt;tt>#%3$06X&lt;/tt></ns1:g>. Consider using colors that result in a contrast ratio greater than <ns1:g example="4.5" id="recommended_contrast_ratio">%4$.2f</ns1:g> for small text, or <ns1:g example="3.0" id="tolerant_contrast_ratio">%5$.2f</ns1:g> for large text.</string>
-    <string description="The result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of this TextView (UI element) is lower than a user-defined ratio. [CHAR LIMIT=NONE]" name="result_message_textview_heuristic_customized_contrast_not_sufficient">The item\'s text contrast ratio is <ns1:g example="2.9" id="contrast_ratio">%1$.2f</ns1:g>. This ratio is based on an estimated foreground color of <ns1:g example="#FFFFFF" id="foreground_color">&lt;tt>#%2$06X&lt;/tt></ns1:g> and an estimated background color of <ns1:g example="#000000" id="background_color">&lt;tt>#%3$06X&lt;/tt></ns1:g>. Consider increasing this item\'s text contrast ratio to the configured ratio of <ns1:g example="3.0" id="customized_heuristic_contrast_ratio">%4$.2f</ns1:g> or greater.</string>
-    <string description="The result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of this TextView (UI element) is lower than a user-defined ratio based on user provided colors. [CHAR LIMIT=NONE]" name="result_message_textview_heuristic_customized_contrast_not_sufficient_confirmed">The item\'s text contrast ratio is <ns1:g example="2.9" id="contrast_ratio">%1$.2f</ns1:g>. This ratio is based on the provided foreground color of <ns1:g example="#FFFFFF" id="foreground_color">&lt;tt>#%2$06X&lt;/tt></ns1:g> and provided background color of <ns1:g example="#000000" id="background_color">&lt;tt>#%3$06X&lt;/tt></ns1:g>. Consider increasing this item\'s text contrast ratio to the configured ratio of <ns1:g example="3.0" id="customized_heuristic_contrast_ratio">%4$.2f</ns1:g> or greater.</string>
-    <string description="The result message of a check describing that this view (UI element) may be presented incorrectly to the user when traversed with a screen reader. [CHAR LIMIT=NONE]" name="result_message_traversal_cycle">This item may be part of a traversal ordering cycle due to its <ns1:g example="android:accessibilityTraversalAfter" id="traversal_after_attr">&lt;tt>%1$s&lt;/tt></ns1:g> attribute.  Traversal behavior with screen readers may be unpredictable.</string>
-    <string description="The result message of a check describing that this view (UI element) may be presented incorrectly to the user when traversed with a screen reader. [CHAR LIMIT=NONE]" name="result_message_traversal_over_constrained">Traversal ordering for this item may be over constrained based on its <ns1:g example="android:accessibilityTraversalBefore" id="traversal_before_attr">&lt;tt>android:accessibilityTraversalBefore&lt;/tt></ns1:g> and <ns1:g example="android:accessibilityTraversalAfter" id="traversal_after_attr">&lt;tt>android:accessibilityTraversalAfter&lt;/tt></ns1:g> attributes. Traversal behavior with screen readers may be unpredictable.</string>
-    <string description="The result message of a check describing that the view has text marked up with an android URLSpan (a hyperlink), which has an invalid URL. [CHAR LIMIT=NONE]" name="result_message_urlspan_invalid_url">Verify that the URL within this item\'s <ns1:g example="URLSpan" id="url_span_class">&lt;tt>URLSpan&lt;/tt></ns1:g> is valid.</string>
-    <string description="The result message of a check describing that views should use the android concept of a &apos;URLSpan&apos; instead of a &apos;ClickableSpan&apos;, for improved accessibility. [CHAR LIMIT=NONE]" name="result_message_urlspan_not_clickablespan">This item should use a <ns1:g example="URLSpan" id="url_span_class">&lt;tt>URLSpan&lt;/tt></ns1:g> in place of a <ns1:g example="ClickableSpan" id="clickable_span_class">&lt;tt>ClickableSpan&lt;/tt></ns1:g>.</string>
-    <string description="The result message of a check describing the position of this view (UI element) on the screen. [CHAR LIMIT=NONE]" name="result_message_view_bounds">This <ns1:g example="clickable and long clickable" id="clickability">%1$s</ns1:g> item also has an on-screen location of <ns1:g example="[0,0][100,100]" id="view_bounds">&lt;tt>%2$s&lt;/tt></ns1:g>.</string>
-    <string description="The result message of a check describing that all or part of the view (UI element) was off the screen when the screenshot was taken. [CHAR LIMIT=NONE]" name="result_message_view_not_within_screencapture">This item\'s on-screen location (<ns1:g example="[0,0][100,100]" id="view_bounds">&lt;tt>%1$s&lt;/tt></ns1:g>) were not within the screen capture on-screen location (<ns1:g example="[0,0][1920,1080]" id="capture_bounds">&lt;tt>%2$s&lt;/tt></ns1:g>).</string>
-    <string description="The result message of a check describing that web content (UI element shown within a browser) was not evaluated. [CHAR LIMIT=NONE]" name="result_message_web_content">Web content is not evaluated.</string>
-    <string description="Adjective describing a view that is selected. [CHAR LIMIT=NONE]" name="selected_state">selected</string>
-    <string description="An action in a UI, as in the imperative &apos;swipe to unlock&apos;. [CHAR LIMIT=NONE]" name="swipe_action">swipe</string>
-    <string description="An action in a UI, as in the imperative &apos;tap to select&apos;. [CHAR LIMIT=NONE]" name="tap_action">tap</string>
-    <string description="Adjective describing a checkbox that is not selected. [CHAR LIMIT=NONE]" name="unchecked_state">unchecked</string>
-    <string description="Describes underlined text styling. [CHAR LIMIT=NONE]" name="underline_text">underline</string>
-    <string description="Adjective describing a view that is not selected. [CHAR LIMIT=NONE]" name="unselected_state">unselected</string>
-</resources>
\ No newline at end of file
diff --git a/validator/src/ResourceConverter.java b/validator/src/ResourceConverter.java
index 6b9d73e..839f191 100644
--- a/validator/src/ResourceConverter.java
+++ b/validator/src/ResourceConverter.java
@@ -37,7 +37,7 @@
      */
     public static void main(String[] args) throws Exception {
         System.out.println("Parsing input...");
-        Map<String, String> map = loadStrings("./validator/resources/values.xml");
+        Map<String, String> map = loadStrings("./validator/resources/strings.xml");
         System.out.println("Writing to output...");
         writeStrings(map, "./validator/resources/strings.properties");
         System.out.println("Finished converting.");
@@ -91,30 +91,36 @@
             String name = node.getAttributes().getNamedItem("name").getNodeValue();
 
             StringBuilder valueBuilder = new StringBuilder();
-            /**
-             * This is a very hacky way to bypass "ns1:g" tag in android's .xml.
-             * Ideally we'll read the tag from the parent and apply it here, but it being the
-             * deep node list I'm not currently sure how to parse it safely. Might need to look
-             * into IntelliJ PSI tree we have in Studio. But I didn't want to add unnecessary deps
-             * to LayoutLib.
-             *
-             * It also means resource namespaces are rendered useless after conversion.
-             */
-            for (int j = 0; j < node.getChildNodes().getLength(); j++) {
-                Node child = node.getChildNodes().item(j);
-                String toAdd = null;
-                if ("ns1:g".equals(child.getNodeName())) {
-                    toAdd = child.getFirstChild().getNodeValue();
-                } else {
-                    toAdd = child.getNodeValue();
+            try {
+                /**
+                 * This is a very hacky way to bypass "ns1:g" tag in android's .xml.
+                 * Ideally we'll read the tag from the parent and apply it here, but it being the
+                 * deep node list I'm not currently sure how to parse it safely. Might need to look
+                 * into IntelliJ PSI tree we have in Studio. But I didn't want to add unnecessary
+                 * deps to LayoutLib.
+                 *
+                 * It also means resource namespaces are rendered useless after conversion.
+                 */
+                for (int j = 0; j < node.getChildNodes().getLength(); j++) {
+                    Node child = node.getChildNodes().item(j);
+                    String toAdd = null;
+                    if ("ns1:g".equals(child.getNodeName())) {
+                        toAdd = child.getFirstChild().getNodeValue();
+                    } else if ("xliff:g".equals(child.getNodeName())) {
+                        toAdd = child.getFirstChild().getNodeValue();
+                    } else {
+                        toAdd = child.getNodeValue();
+                    }
+                    // Replace all tab, newline and multi indentations.
+                    toAdd = toAdd.replaceAll("[\n\t]", "");
+                    toAdd = toAdd.replaceAll("[ ]+", " ");
+                    valueBuilder.append(toAdd);
                 }
-                // Replace all tab, newline and multi indentations.
-                toAdd = toAdd.replaceAll("[\n\t]", "");
-                toAdd = toAdd.replaceAll("[ ]+", " ");
-                valueBuilder.append(toAdd);
+                String finalString = valueBuilder.toString().trim();
+                toReturn.put(name, finalString);
+            } catch (Exception e) {
+                e.printStackTrace();
             }
-            String finalString = valueBuilder.toString().trim();
-            toReturn.put(name, finalString);
         }
         return toReturn;
     }
diff --git a/validator/src/com/android/tools/idea/validator/AtfBufferedImage.java b/validator/src/com/android/tools/idea/validator/AtfBufferedImage.java
new file mode 100644
index 0000000..c5fc13f
--- /dev/null
+++ b/validator/src/com/android/tools/idea/validator/AtfBufferedImage.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2020 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.idea.validator;
+
+import com.android.tools.idea.validator.ValidatorResult.ImageSize;
+import com.android.tools.idea.validator.ValidatorResult.Metric;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import android.annotation.NonNull;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.awt.image.WritableRaster;
+import java.io.File;
+import java.io.IOException;
+
+import com.google.android.apps.common.testing.accessibility.framework.utils.contrast.Image;
+import javax.imageio.ImageIO;
+
+import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
+
+/**
+ * Image implementation to be used in Accessibility Test Framework.
+ */
+public class AtfBufferedImage implements Image {
+
+    // The source buffered image, expected to contain the full screen rendered image of the layout.
+    @NotNull private final BufferedImage mImage;
+    // Metrics to be returned
+    @NotNull private final Metric mMetric;
+
+    // Points in before scaled coord
+    private final int mLeft;
+    private final int mTop;
+    private final int mWidth;
+    private final int mHeight;
+
+    // Scale factors in case layoutlib scaled the screen.
+    private final float mScaleX;
+    private final float mScaleY;
+
+    AtfBufferedImage(
+            @NotNull BufferedImage image,
+            @NotNull Metric metric,
+            float scaleX,
+            float scaleY) {
+        // Without unscaling, atf does not recognize bounds that goes over the scaled image.
+        // E.g. if pxl4 is scaled to 1k x 2k (originally 2k x 4k), then atf could request for
+        // bounds at x:1.5k y:0, then without unscaling the call would fail.
+        this(image,
+            metric,
+            0,
+            0,
+            (int) (image.getWidth() * 1.0f / scaleX),
+            (int) (image.getHeight() * 1.0f / scaleY),
+            scaleX,
+            scaleY);
+        assert(image.getType() == TYPE_INT_ARGB);
+
+        // FOR DEBUGGING ONLY
+        if (LayoutValidator.shouldSaveCroppedImages()) {
+            saveImage(image);
+        }
+    }
+
+    private AtfBufferedImage(
+            @NotNull BufferedImage image,
+            @NotNull Metric metric,
+            int left,
+            int top,
+            int width,
+            int height,
+            float scaleX,
+            float scaleY) {
+        mImage = image;
+        mMetric = metric;
+        mLeft = left;
+        mTop = top;
+        mWidth = width;
+        mHeight = height;
+        mScaleX = scaleX;
+        mScaleY = scaleY;
+    }
+
+    @Override
+    public int getHeight() {
+        return mHeight;
+    }
+
+    @Override
+    public int getWidth() {
+        return mWidth;
+    }
+
+    @Override
+    @NotNull
+    public Image crop(int left, int top, int width, int height) {
+        return new AtfBufferedImage(mImage, mMetric, left, top, width, height, mScaleX, mScaleY);
+    }
+
+    /**
+     * @return the region that matches the scaled buffered image. The returned image
+     * will not have the width same as {@link #getWidth()} but rather width * mScaleX.
+     */
+    @Override
+    @NotNull
+    public int[] getPixels() {
+        int scaledLeft = (int)(mLeft * mScaleX);
+        int scaledTop = (int)(mTop * mScaleY);
+        int scaledWidth = (int)(mWidth * mScaleX);
+        int scaledHeight = (int)(mHeight * mScaleY);
+
+        if (scaledWidth <= 0 || scaledHeight <= 0) {
+            return new int[0];
+        }
+
+        BufferedImage cropped = mImage.getSubimage(
+                scaledLeft, scaledTop, scaledWidth, scaledHeight);
+        WritableRaster raster =
+                cropped.copyData(cropped.getRaster().createCompatibleWritableRaster());
+        int[] toReturn = ((DataBufferInt) raster.getDataBuffer()).getData();
+        mMetric.mImageMemoryBytes += toReturn.length * 4;
+
+        if (LayoutValidator.shouldSaveCroppedImages()) {
+            saveImage(cropped);
+        }
+
+        return toReturn;
+    }
+
+    // FOR DEBUGGING ONLY
+    private static int SAVE_IMAGE_COUNTER = 0;
+    private void saveImage(BufferedImage image) {
+        try {
+            String name = SAVE_IMAGE_COUNTER + "_img_for_atf_LxT:WxH_" +
+                    mLeft + "x" + mTop + ":" +
+                    mWidth + "x" + mHeight;
+
+            mMetric.mImageSizes.add(new ImageSize(mLeft, mTop, mWidth, mHeight));
+
+            File output = new File(getDebugDir(), name);
+            if (output.exists()) {
+                output.delete();
+            }
+            ImageIO.write(image, "PNG", output);
+            SAVE_IMAGE_COUNTER++;
+        } catch (IOException ioe) {
+            mMetric.mErrorMessage = ioe.getMessage();
+        }
+    }
+
+    @NonNull
+    private File getDebugDir() {
+        File failureDir;
+        String failureDirString = System.getProperty("debug.dir");
+        if (failureDirString != null) {
+            failureDir = new File(failureDirString);
+        } else {
+            String workingDirString = System.getProperty("user.dir");
+            failureDir = new File(workingDirString, "out/debugs");
+        }
+
+        failureDir.mkdirs();
+        return failureDir;
+    }
+}
diff --git a/validator/src/com/android/tools/idea/validator/LayoutValidator.java b/validator/src/com/android/tools/idea/validator/LayoutValidator.java
index dc34e90..03f080c 100644
--- a/validator/src/com/android/tools/idea/validator/LayoutValidator.java
+++ b/validator/src/com/android/tools/idea/validator/LayoutValidator.java
@@ -19,9 +19,9 @@
 import com.android.tools.idea.validator.ValidatorData.Level;
 import com.android.tools.idea.validator.ValidatorData.Policy;
 import com.android.tools.idea.validator.ValidatorData.Type;
-import com.android.tools.idea.validator.accessibility.AccessibilityValidator;
 import com.android.tools.layoutlib.annotations.NotNull;
 import com.android.tools.layoutlib.annotations.Nullable;
+import com.android.tools.layoutlib.annotations.VisibleForTesting;
 
 import android.view.View;
 
@@ -35,26 +35,120 @@
 
     public static final ValidatorData.Policy DEFAULT_POLICY = new Policy(
             EnumSet.of(Type.ACCESSIBILITY, Type.RENDER),
-            EnumSet.of(Level.ERROR, Level.WARNING));
+            EnumSet.of(Level.ERROR, Level.WARNING, Level.INFO, Level.VERBOSE));
 
     private static ValidatorData.Policy sPolicy = DEFAULT_POLICY;
 
+    private static boolean sPaused = false;
+
+    private static boolean sSaveCroppedImages = false;
+
+    private static boolean sObtainCharacterLocations = false;
+
+    /**
+     * @return true if validator is paused. False otherwise.
+     */
+    public static boolean isPaused() {
+        return sPaused;
+    }
+
+    /**
+     * Pause or resume validator. {@link RenderParamsFlags#FLAG_ENABLE_LAYOUT_VALIDATOR} must be
+     * enabled.
+     * @param paused true if validator should be paused. False to resume.
+     */
+    public static void setPaused(boolean paused) {
+        sPaused = paused;
+    }
+
+    public static boolean shouldSaveCroppedImages() {
+        return sSaveCroppedImages;
+    }
+
+    /**
+     * For Debugging purpose. Save all cropped images used by atf if enabled.
+     * @param save
+     */
+    public static void setSaveCroppedImages(boolean save) {
+        sSaveCroppedImages = save;
+    }
+
+    /**
+     * Indicates whether text character locations should be requested.
+     *
+     * @param obtainCharacterLocations true if text character locations should be requested.
+     */
+    public static void setObtainCharacterLocations(boolean obtainCharacterLocations) {
+        sObtainCharacterLocations = obtainCharacterLocations;
+    }
+
+    /**
+     * @return true if text character locations should be requested.
+     */
+    public static boolean obtainCharacterLocations() {
+        return sObtainCharacterLocations;
+    }
+
     /**
      * Validate the layout using the default policy.
      * Precondition: View must be attached to the window.
      *
+     * Used for testing.
+     *
      * @return The validation results. If no issue is found it'll return empty result.
      */
     @NotNull
-    public static ValidatorResult validate(@NotNull View view, @Nullable BufferedImage image) {
-        if (view.isAttachedToWindow()) {
-            return AccessibilityValidator.validateAccessibility(view, image, sPolicy);
+    @VisibleForTesting
+    public static ValidatorResult validate(
+            @NotNull View view,
+            @Nullable BufferedImage image,
+            float scaleX,
+            float scaleY) {
+        if (!sPaused && view.isAttachedToWindow()) {
+            ValidatorHierarchy hierarchy = ValidatorUtil.buildHierarchy(
+                    sPolicy,
+                    view,
+                    image,
+                    scaleX,
+                    scaleY);
+            return ValidatorUtil.generateResults(sPolicy, hierarchy);
         }
         // TODO: Add non-a11y layout validation later.
         return new ValidatorResult.Builder().build();
     }
 
     /**
+     * Build the hierarchy necessary for validating the layout.
+     * The operation is quick thus can be used frequently.
+     *
+     * @return The hierarchy to be used for validation.
+     */
+    @NotNull
+    public static ValidatorHierarchy buildHierarchy(
+            @NotNull View view,
+            @Nullable BufferedImage image,
+            float scaleX,
+            float scaleY) {
+        if (!sPaused && view.isAttachedToWindow()) {
+            return ValidatorUtil.buildHierarchy(
+                    sPolicy,
+                    view,
+                    image,
+                    scaleX,
+                    scaleY);
+        }
+        return new ValidatorHierarchy();
+    }
+
+    /**
+     * @return The validator result that matches the hierarchy
+     */
+    @NotNull
+    public static ValidatorResult validate(@NotNull ValidatorHierarchy hierarchy) {
+        return ValidatorUtil.generateResults(sPolicy, hierarchy);
+    }
+
+    /**
      * Update the policy with which to run the validation call.
      * @param policy new policy.
      */
diff --git a/validator/src/com/android/tools/idea/validator/ValidatorData.java b/validator/src/com/android/tools/idea/validator/ValidatorData.java
index 49f0e30..167cab1 100644
--- a/validator/src/com/android/tools/idea/validator/ValidatorData.java
+++ b/validator/src/com/android/tools/idea/validator/ValidatorData.java
@@ -21,6 +21,7 @@
 
 import java.util.EnumSet;
 import java.util.HashSet;
+import java.util.List;
 
 import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck;
 
@@ -53,8 +54,14 @@
      * Determine what types and levels of validation to run.
      */
     public static class Policy {
+        /** Sets of types to filter by. */
         @NotNull public final EnumSet<Type> mTypes;
+        /** Sets of levels to filter by. */
         @NotNull public final EnumSet<Level> mLevels;
+        /**
+         * List of checks to use for the scan. If empty we use the default set
+         * defined by {@link AccessibilityCheckPreset.LATEST}
+         */
         @NotNull public final HashSet<AccessibilityHierarchyCheck> mChecks = new HashSet();
 
         public Policy(@NotNull EnumSet<Type> types, @NotNull EnumSet<Level> levels) {
@@ -64,17 +71,6 @@
     }
 
     /**
-     * Suggested fix to the user or to the studio.
-     */
-    public static class Fix {
-        @NotNull public final String mFix;
-
-        public Fix(String fix) {
-            mFix = fix;
-        }
-    }
-
-    /**
      * Issue describing the layout problem.
      */
     public static class Issue {
@@ -181,4 +177,112 @@
             }
         }
     }
+
+    /**
+     * Represents a view attribute which contains a namespace and an attribute name.
+     */
+    public static class ViewAttribute {
+        /** The namespace used in XML files for this view attribute. */
+        @NotNull public final String mNamespaceUri;
+        /** The namespace of this view attribute. */
+        @NotNull public final String mNamespace;
+        /** The attribute name of this view attribute. */
+        @NotNull public final String mAttributeName;
+
+        public ViewAttribute(
+                @NotNull String namespaceUri,
+                @NotNull String namespace,
+                @NotNull String attributeName) {
+            mNamespaceUri = namespaceUri;
+            mNamespace = namespace;
+            mAttributeName = attributeName;
+        }
+    }
+
+    /**
+     * Suggested fix to the user or to the studio.
+     */
+    public static interface Fix {
+        /**
+         * @return a human-readable description for this fix.
+         */
+        @NotNull String getDescription();
+    }
+
+    /**
+     * Suggest setting a value to a {@link ViewAttribute} to fix a specific {@link Issue}.
+     *
+     * <ul>
+     *   <li>If the view attribute has not been set before, add the view attribute and set its value
+     *       to the suggested value.
+     *   <li>If the view attribute has been set before, replace its value with the suggested value.
+     *   <li>If the suggested value is an empty string, ask the developer to set the view attribute
+     *       to a meaningful non-empty string or resource reference. DO NOT set the view attribute
+     *       to an empty string.
+     * </ul>
+     */
+    public static class SetViewAttributeFix implements Fix {
+        /** The {@link ViewAttribute} suggested to be changed. */
+        @NotNull public final ViewAttribute mViewAttribute;
+
+        /** The suggested value of the {@link ViewAttribute} suggested to be changed. */
+        @NotNull public final String mSuggestedValue;
+
+        @NotNull private final String mDescription;
+
+        public SetViewAttributeFix(@NotNull ViewAttribute viewAttribute,
+                @NotNull String suggestedValue, @NotNull String description) {
+            mViewAttribute = viewAttribute;
+            mSuggestedValue = suggestedValue;
+            mDescription = description;
+        }
+
+        @Override
+        @NotNull public String getDescription() {
+            return mDescription;
+        }
+    }
+
+    /**
+     * Suggest removing a {@link ViewAttribute} to fix a specific {@link Issue}.
+     */
+    public static class RemoveViewAttributeFix implements Fix {
+        /** The {@link ViewAttribute} suggested to be removed. */
+        @NotNull public final ViewAttribute mViewAttribute;
+
+        @NotNull private final String mDescription;
+
+        public RemoveViewAttributeFix(@NotNull ViewAttribute viewAttribute,
+                @NotNull String description) {
+            mViewAttribute = viewAttribute;
+            mDescription = description;
+        }
+
+        @Override
+        @NotNull public String getDescription() {
+            return mDescription;
+        }
+    }
+
+    /**
+     * Suggest applying multiple {@link Fix} together to fix a specific {@link Issue}.
+     *
+     * <p>A {@link CompoundFix} must contain at least 2 {@link Fix}.
+     */
+    public static class CompoundFix implements Fix {
+        /** Lists of {@link Fix} suggested to be applied together. */
+        @NotNull public final List<Fix> mFixes;
+
+        @NotNull private final String mDescription;
+
+        public CompoundFix(@NotNull List<Fix> fixes, String description) {
+            mFixes = fixes;
+            mDescription = description;
+        }
+
+        @Override
+        @NotNull public String getDescription() {
+            return mDescription;
+        }
+    }
 }
diff --git a/validator/src/com/android/tools/idea/validator/ValidatorHierarchy.java b/validator/src/com/android/tools/idea/validator/ValidatorHierarchy.java
new file mode 100644
index 0000000..f855af6
--- /dev/null
+++ b/validator/src/com/android/tools/idea/validator/ValidatorHierarchy.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 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.idea.validator;
+
+import com.android.tools.layoutlib.annotations.Nullable;
+
+import com.google.android.apps.common.testing.accessibility.framework.Parameters;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchyAndroid;
+
+/**
+ * Hierarchical data required for running the ATF scanner checks.
+ * Creation of the hierarchical data is pretty quick.
+ */
+public class ValidatorHierarchy {
+    /** Contains meta data (such as src map) that is required for building result */
+    public @Nullable ValidatorResult.Builder mBuilder = null;
+    /** Contains view hierarchy data related to a11y */
+    public @Nullable AccessibilityHierarchyAndroid mView = null;
+    /** Contains screen capture of the view */
+    public @Nullable Parameters mParameters = null;
+
+    public @Nullable String mErrorMessage = null;
+
+    /** Returns true if hierarchical data is available to build results. */
+    public boolean isHierarchyBuilt() {
+        return mBuilder != null && mView != null;
+    }
+
+}
diff --git a/validator/src/com/android/tools/idea/validator/ValidatorResult.java b/validator/src/com/android/tools/idea/validator/ValidatorResult.java
index 8cc5c3d..7f25d46 100644
--- a/validator/src/com/android/tools/idea/validator/ValidatorResult.java
+++ b/validator/src/com/android/tools/idea/validator/ValidatorResult.java
@@ -104,28 +104,44 @@
         /** Error message. If null no error was thrown. */
         public String mErrorMessage = null;
 
-        /** Records how long validation took */
-        public long mElapsedMs = 0;
+        /** Record how long hierarchy creation took */
+        public long mHierarchyCreationMs = 0;
+
+        /** Record how long generating results took */
+        public long mGenerateResultsMs = 0;
 
         /** How many new memories (bytes) validator creates for images. */
         public long mImageMemoryBytes = 0;
 
-        private long mStart;
+        /** Debugging purpose only. Use it with {@link LayoutValidator#shouldSaveCroppedImages()} */
+        public List<ImageSize> mImageSizes = new ArrayList<>();
+
+        private long mHierarchyCreationTimeStart;
+
+        private long mGenerateRulesTimeStart;
 
         private Metric() { }
 
-        public void startTimer() {
-            mStart = System.currentTimeMillis();
+        public void startHierarchyCreationTimer() {
+            mHierarchyCreationTimeStart = System.currentTimeMillis();
         }
 
-        public void endTimer() {
-            mElapsedMs = System.currentTimeMillis() - mStart;
+        public void recordHierarchyCreationTime() {
+            mHierarchyCreationMs = System.currentTimeMillis() - mHierarchyCreationTimeStart;
+        }
+
+        public void startGenerateResultsTimer() {
+            mGenerateRulesTimeStart = System.currentTimeMillis();
+        }
+
+        public void recordGenerateResultsTime() {
+            mGenerateResultsMs = System.currentTimeMillis() - mGenerateRulesTimeStart;
         }
 
         @Override
         public String toString() {
-            return "Validation result metric: { elapsed=" + mElapsedMs +
-                    "ms, image memory=" + readableBytes() + " }";
+            return "Validation result metric: { hierarchy creation=" + mHierarchyCreationMs
+                    +"ms, image memory=" + readableBytes() + " }";
         }
 
         private String readableBytes() {
@@ -141,4 +157,24 @@
             return mImageMemoryBytes + "bytes";
         }
     }
+
+    public static class ImageSize {
+        private final int mLeft;
+        private final int mTop;
+        private final int mWidth;
+        private final int mHeight;
+
+        public ImageSize(int left, int top, int width, int height) {
+            mLeft = left;
+            mTop = top;
+            mWidth = width;
+            mHeight = height;
+        }
+
+        @Override
+        public String toString() {
+            return "ImageSize{" + "mLeft=" + mLeft + ", mTop=" + mTop + ", mWidth=" + mWidth +
+                    ", mHeight=" + mHeight + '}';
+        }
+    }
 }
diff --git a/validator/src/com/android/tools/idea/validator/ValidatorUtil.java b/validator/src/com/android/tools/idea/validator/ValidatorUtil.java
new file mode 100644
index 0000000..0e944f1
--- /dev/null
+++ b/validator/src/com/android/tools/idea/validator/ValidatorUtil.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2020 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.idea.validator;
+
+import com.android.tools.idea.validator.ValidatorData.CompoundFix;
+import com.android.tools.idea.validator.ValidatorData.Issue;
+import com.android.tools.idea.validator.ValidatorData.Issue.IssueBuilder;
+import com.android.tools.idea.validator.ValidatorData.Level;
+import com.android.tools.idea.validator.ValidatorData.RemoveViewAttributeFix;
+import com.android.tools.idea.validator.ValidatorData.SetViewAttributeFix;
+import com.android.tools.idea.validator.ValidatorData.Type;
+import com.android.tools.idea.validator.ValidatorResult.Builder;
+import com.android.tools.idea.validator.hierarchy.CustomHierarchyHelper;
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
+
+import android.view.View;
+
+import java.awt.image.BufferedImage;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheck;
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckPreset;
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResult.AccessibilityCheckResultType;
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck;
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheckResult;
+import com.google.android.apps.common.testing.accessibility.framework.Parameters;
+import com.google.android.apps.common.testing.accessibility.framework.checks.EditableContentDescCheck;
+import com.google.android.apps.common.testing.accessibility.framework.checks.SpeakableTextPresentCheck;
+import com.google.android.apps.common.testing.accessibility.framework.checks.TextContrastCheck;
+import com.google.android.apps.common.testing.accessibility.framework.checks.TouchTargetSizeCheck;
+import com.google.android.apps.common.testing.accessibility.framework.strings.StringManager;
+import com.google.android.apps.common.testing.accessibility.framework.suggestions.CompoundFixSuggestions;
+import com.google.android.apps.common.testing.accessibility.framework.suggestions.FixSuggestion;
+import com.google.android.apps.common.testing.accessibility.framework.suggestions.FixSuggestionPreset;
+import com.google.android.apps.common.testing.accessibility.framework.suggestions.RemoveViewAttributeFixSuggestion;
+import com.google.android.apps.common.testing.accessibility.framework.suggestions.SetViewAttributeFixSuggestion;
+import com.google.android.apps.common.testing.accessibility.framework.suggestions.ViewAttribute;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchy;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchyAndroid;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.CustomViewBuilderAndroid;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.DefaultCustomViewBuilderAndroid;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.ViewHierarchyElementAndroid;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public class ValidatorUtil {
+
+    static {
+        /**
+         * Overriding default ResourceBundle ATF uses. ATF would use generic Java resources
+         * instead of Android's .xml.
+         *
+         * By default ATF generates ResourceBundle to support Android specific env/ classloader,
+         * which is quite different from Layoutlib, which supports multiple classloader depending
+         * on env (testing vs in studio).
+         *
+         * To support ATF in Layoutlib, easiest way is to convert resources from Android xml to
+         * generic Java resources (strings.properties), and have the default ResourceBundle ATF
+         * uses be redirected.
+         */
+        StringManager.setResourceBundleProvider(locale -> ResourceBundle.getBundle("strings"));
+    }
+
+    // Visible for testing.
+    protected static DefaultCustomViewBuilderAndroid sDefaultCustomViewBuilderAndroid =
+            new DefaultCustomViewBuilderAndroid();
+
+    /**
+     * Fixes could be only provided for a {@link AccessibilityHierarchyCheckResult}s generated by
+     * a predefined set of {@link AccessibilityHierarchyCheck}s.
+     */
+    private final static ImmutableSet<Class<? extends AccessibilityHierarchyCheck>>
+            sAllowedCheckResultClassSet4Fix = ImmutableSet.of(SpeakableTextPresentCheck.class,
+            TextContrastCheck.class, TouchTargetSizeCheck.class,  EditableContentDescCheck.class);
+
+    /**
+     * @param policy policy to apply for the hierarchy
+     * @param view root view to build hierarchy from
+     * @param image screenshot image that matches the view
+     * @param scaleX scaling done via layoutlib in x coord
+     * @param scaleY scaling done via layoutlib in y coord
+     * @return The hierarchical data required for running the ATF checks.
+     */
+    public static ValidatorHierarchy buildHierarchy(
+            @NotNull ValidatorData.Policy policy,
+            @NotNull View view,
+            @Nullable BufferedImage image,
+            float scaleX,
+            float scaleY) {
+        ValidatorHierarchy hierarchy = new ValidatorHierarchy();
+        if (!policy.mTypes.contains(Type.ACCESSIBILITY)) {
+            return hierarchy;
+        }
+
+        ValidatorResult.Builder builder = new ValidatorResult.Builder();
+        @Nullable Parameters parameters = null;
+        builder.mMetric.startHierarchyCreationTimer();
+        try {
+            hierarchy.mView = AccessibilityHierarchyAndroid
+                    .newBuilder(view)
+                    .setViewOriginMap(builder.mSrcMap)
+                    .setObtainCharacterLocations(LayoutValidator.obtainCharacterLocations())
+                    .setCustomViewBuilder(new CustomViewBuilderAndroid() {
+                        @Override
+                        public Class<?> getClassByName(
+                                ViewHierarchyElementAndroid viewHierarchyElementAndroid,
+                                String className) {
+                            Class<?> toReturn = sDefaultCustomViewBuilderAndroid.getClassByName(
+                                    viewHierarchyElementAndroid, className);
+                            if (toReturn == null) {
+                                toReturn = CustomHierarchyHelper.getClassByName(className);
+                            }
+                            return toReturn;
+                        }
+
+                        @Override
+                        public boolean isCheckable(View view) {
+                            return CustomHierarchyHelper.isCheckable(view);
+                        }
+                    }).build();
+            if (image != null) {
+                parameters = new Parameters();
+                parameters.putScreenCapture(
+                        new AtfBufferedImage(image, builder.mMetric, scaleX, scaleY));
+            }
+        } finally {
+            builder.mMetric.recordHierarchyCreationTime();
+        }
+
+        hierarchy.mBuilder = builder;
+        hierarchy.mParameters = parameters;
+        return hierarchy;
+    }
+
+    /**
+     * @param hierarchy to build result from. If {@link ValidatorHierarchy#isHierarchyBuilt()}
+     * is false, returns a result with an internal error.
+     * @return Returns ValidatorResult with given hierarchical data.
+     */
+    public static ValidatorResult generateResults(
+            @NotNull ValidatorData.Policy policy,
+            @NotNull ValidatorHierarchy hierarchy) {
+        ValidatorResult.Builder builder = hierarchy.mBuilder;
+        try {
+            if (!hierarchy.isHierarchyBuilt()) {
+                // Unable to build.
+                builder = new Builder();
+                String errorMsg = hierarchy.mErrorMessage != null ? hierarchy.mErrorMessage :
+                        "Hierarchy is not built yet.";
+                builder.mIssues.add(new IssueBuilder()
+                        .setCategory("Accessibility")
+                        .setType(Type.INTERNAL_ERROR)
+                        .setMsg(errorMsg)
+                        .setLevel(Level.ERROR)
+                        .setSourceClass("ValidatorHierarchy")
+                        .build());
+                return builder.build();
+            }
+            builder.mMetric.startGenerateResultsTimer();
+
+            AccessibilityHierarchyAndroid view = hierarchy.mView;
+            Parameters parameters = hierarchy.mParameters;
+
+            EnumSet<Level> filter = policy.mLevels;
+            ArrayList<AccessibilityHierarchyCheckResult> a11yResults = new ArrayList<>();
+
+            HashSet<AccessibilityHierarchyCheck> policyChecks = policy.mChecks;
+            @NotNull Set<AccessibilityHierarchyCheck> checks = policyChecks.isEmpty() ?
+                    AccessibilityCheckPreset.getAccessibilityHierarchyChecksForPreset(
+                            AccessibilityCheckPreset.LATEST) : policyChecks;
+
+            for (AccessibilityHierarchyCheck check : checks) {
+                a11yResults.addAll(check.runCheckOnHierarchy(view, null, parameters));
+            }
+
+            for (AccessibilityHierarchyCheckResult result : a11yResults) {
+                // TODO: b/183726816 replace this with
+                // AccessibilityCheckPreset.getHierarchyCheckForClassName(checkClassName)
+                // .getTitleMessage(Locale.ENGLISH)
+                String category = ValidatorUtil.getCheckClassCategory(result.getSourceCheckClass());
+
+                ValidatorData.Level level = ValidatorUtil.convertLevel(result.getType());
+                if (!filter.contains(level)) {
+                    continue;
+                }
+
+                try {
+                    IssueBuilder issueBuilder = new IssueBuilder().setCategory(category).setMsg(
+                            result.getMessage(Locale.ENGLISH).toString()).setLevel(level).setFix(
+                            ValidatorUtil.generateFix(result, view, parameters)).setSourceClass(
+                            result.getSourceCheckClass().getSimpleName());
+                    if (result.getElement() != null) {
+                        issueBuilder.setSrcId(result.getElement().getCondensedUniqueId());
+                    }
+                    AccessibilityHierarchyCheck subclass =
+                            AccessibilityCheckPreset.getHierarchyCheckForClass(
+                                    result.getSourceCheckClass().asSubclass(
+                                            AccessibilityHierarchyCheck.class));
+                    if (subclass != null) {
+                        issueBuilder.setHelpfulUrl(subclass.getHelpUrl());
+                    }
+                    builder.mIssues.add(issueBuilder.build());
+                } catch (Exception e) {
+                    StringWriter sw = new StringWriter();
+                    PrintWriter pw = new PrintWriter(sw);
+                    e.printStackTrace(pw);
+                    builder.mIssues.add(new IssueBuilder()
+                            .setCategory(category)
+                            .setType(Type.INTERNAL_ERROR)
+                            .setMsg(sw.toString())
+                            .setLevel(Level.ERROR)
+                            .setSourceClass("ValidatorHierarchy").build());
+                }
+            }
+        } finally {
+            builder.mMetric.recordGenerateResultsTime();
+        }
+        return builder.build();
+    }
+
+    /**
+     * @return the list of internal errors in results. Useful for testing and debugging.
+     */
+    public static List<Issue> filterInternalErrors(List<ValidatorData.Issue> results) {
+        return filterByTypes(results, EnumSet.of(Type.INTERNAL_ERROR));
+    }
+
+    /**
+     * @return the list filtered by the level. Useful for testing and debugging.
+     */
+    public static List<Issue> filter(List<ValidatorData.Issue> results, EnumSet<Level> errors) {
+        return results.stream().filter(
+                issue -> errors.contains(issue.mLevel)).collect(Collectors.toList());
+    }
+
+    /**
+     * @return the list filtered by the source class name. Useful for testing and debugging.
+     */
+    public static List<Issue> filter(
+            List<ValidatorData.Issue> results, String sourceClass) {
+        return results.stream().filter(
+                issue -> sourceClass.equals(issue.mSourceClass)).collect(Collectors.toList());
+    }
+
+    /**
+     * @return the list filtered by the source class name. Useful for testing and debugging.
+     */
+    public static List<Issue> filterByTypes(
+            List<ValidatorData.Issue> results, EnumSet<Type> types) {
+        return results.stream().filter(
+                issue -> types.contains(issue.mType)).collect(Collectors.toList());
+    }
+
+    /**
+     * @param checkClass classes expected to extend AccessibilityHierarchyCheck
+     * @return {@link AccessibilityCheck.Category} of the class.
+     */
+    @NotNull
+    private static String getCheckClassCategory(@NotNull Class<?> checkClass) {
+        try {
+            Class<? extends AccessibilityHierarchyCheck> subClass =
+                    checkClass.asSubclass(AccessibilityHierarchyCheck.class);
+            AccessibilityHierarchyCheck check =
+                    AccessibilityCheckPreset.getHierarchyCheckForClass(subClass);
+            return (check == null) ? "Accessibility" : check.getCategory().name();
+        } catch (ClassCastException e) {
+            return "Accessibility";
+        }
+    }
+
+    /** Convert {@link AccessibilityCheckResultType} to {@link ValidatorData.Level} */
+    @NotNull
+    private static ValidatorData.Level convertLevel(@NotNull AccessibilityCheckResultType type) {
+        switch (type) {
+            case ERROR:
+                return Level.ERROR;
+            case WARNING:
+                return Level.WARNING;
+            case INFO:
+                return Level.INFO;
+            // TODO: Maybe useful later?
+            case SUPPRESSED:
+            case NOT_RUN:
+            default:
+                return Level.VERBOSE;
+        }
+    }
+
+    /**
+     * Create a {@link ValidatorData.Fix} for the given result, or {@code null} if there is no
+     * fixes available.
+     *
+     * <p>If there are multiple fixes available, return the first fix which is considered to be the
+     * best fix available.
+     *
+     * @param result to generate a fix from.
+     * @param hierarchy The hierarchy from which the result is generated from.
+     * @param parameters Optional input data or preferences.
+     */
+    @Nullable
+    private static ValidatorData.Fix generateFix(
+            @NotNull AccessibilityHierarchyCheckResult result,
+            @NotNull AccessibilityHierarchy hierarchy,
+            @Nullable Parameters parameters) {
+        if (sAllowedCheckResultClassSet4Fix.contains(result.getSourceCheckClass())) {
+            ImmutableList<FixSuggestion> fixSuggestions =
+                    FixSuggestionPreset.provideFixSuggestions(result, hierarchy, parameters);
+            return fixSuggestions.isEmpty() ? null : convertFix(fixSuggestions.get(0));
+        }
+        return null;
+    }
+
+    /** Convert {@link FixSuggestion} to {@link ValidatorData.Fix} */
+    @Nullable
+    private static ValidatorData.Fix convertFix(@NotNull FixSuggestion fixSuggestion) {
+        if (fixSuggestion instanceof CompoundFixSuggestions) {
+            CompoundFixSuggestions compoundFixSuggestions = (CompoundFixSuggestions)fixSuggestion;
+            List<ValidatorData.Fix> fixes =
+                    compoundFixSuggestions
+                            .getFixSuggestions()
+                            .stream()
+                            .map(ValidatorUtil::convertFix)
+                            .collect(Collectors.toList());
+            return new CompoundFix(
+                    fixes,
+                    compoundFixSuggestions.getDescription(Locale.ENGLISH));
+        } else if (fixSuggestion instanceof RemoveViewAttributeFixSuggestion) {
+            RemoveViewAttributeFixSuggestion removeViewAttributeFix =
+                    (RemoveViewAttributeFixSuggestion)fixSuggestion;
+            return new RemoveViewAttributeFix(
+                    convertViewAttribute(removeViewAttributeFix.getViewAttribute()),
+                    removeViewAttributeFix.getDescription(Locale.ENGLISH));
+        } else if (fixSuggestion instanceof SetViewAttributeFixSuggestion) {
+            SetViewAttributeFixSuggestion setViewAttributeFixSuggestion =
+                    (SetViewAttributeFixSuggestion)fixSuggestion;
+            return new SetViewAttributeFix(
+                    convertViewAttribute(setViewAttributeFixSuggestion.getViewAttribute()),
+                    setViewAttributeFixSuggestion.getSuggestedValue(),
+                    setViewAttributeFixSuggestion.getDescription(Locale.ENGLISH));
+        }
+        return null;
+    }
+
+    /** Convert {@link ViewAttribute} to {@link ValidatorData.ViewAttribute} */
+    @NotNull
+    private static ValidatorData.ViewAttribute convertViewAttribute(
+            @NotNull ViewAttribute viewAttribute) {
+        return new ValidatorData.ViewAttribute(
+                viewAttribute.getNamespaceUri(),
+                viewAttribute.getNamespace(),
+                viewAttribute.getAttributeName());
+    }
+}
diff --git a/validator/src/com/android/tools/idea/validator/accessibility/AccessibilityValidator.java b/validator/src/com/android/tools/idea/validator/accessibility/AccessibilityValidator.java
deleted file mode 100644
index 859e5bc..0000000
--- a/validator/src/com/android/tools/idea/validator/accessibility/AccessibilityValidator.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2020 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.idea.validator.accessibility;
-
-import com.android.tools.idea.validator.ValidatorData;
-import com.android.tools.idea.validator.ValidatorData.Fix;
-import com.android.tools.idea.validator.ValidatorData.Issue.IssueBuilder;
-import com.android.tools.idea.validator.ValidatorData.Level;
-import com.android.tools.idea.validator.ValidatorData.Type;
-import com.android.tools.idea.validator.ValidatorResult;
-import com.android.tools.idea.validator.ValidatorResult.Metric;
-import com.android.tools.layoutlib.annotations.NotNull;
-import com.android.tools.layoutlib.annotations.Nullable;
-
-import android.view.View;
-
-import java.awt.image.BufferedImage;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.ResourceBundle;
-import java.util.Set;
-
-import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckPreset;
-import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResult.AccessibilityCheckResultType;
-import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck;
-import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheckResult;
-import com.google.android.apps.common.testing.accessibility.framework.Parameters;
-import com.google.android.apps.common.testing.accessibility.framework.strings.StringManager;
-import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchyAndroid;
-import com.google.common.collect.BiMap;
-
-/**
- * Validator specific for running Accessibility specific issues.
- */
-public class AccessibilityValidator {
-
-    static {
-        /**
-         * Overriding default ResourceBundle ATF uses. ATF would use generic Java resources
-         * instead of Android's .xml.
-         *
-         * By default ATF generates ResourceBundle to support Android specific env/ classloader,
-         * which is quite different from Layoutlib, which supports multiple classloader depending
-         * on env (testing vs in studio).
-         *
-         * To support ATF in Layoutlib, easiest way is to convert resources from Android xml to
-         * generic Java resources (strings.properties), and have the default ResourceBundle ATF
-         * uses be redirected.
-         */
-        StringManager.setResourceBundleProvider(locale -> ResourceBundle.getBundle("strings"));
-    }
-
-    /**
-     * Run Accessibility specific validation test and receive results.
-     * @param view the root view
-     * @param image the output image of the view. Null if not available.
-     * @param policy e.g: list of levels to allow
-     * @return results with all the accessibility issues and warnings.
-     */
-    @NotNull
-    public static ValidatorResult validateAccessibility(
-            @NotNull View view,
-            @Nullable BufferedImage image,
-            @NotNull ValidatorData.Policy policy) {
-
-        EnumSet<Level> filter = policy.mLevels;
-        ValidatorResult.Builder builder = new ValidatorResult.Builder();
-        builder.mMetric.startTimer();
-        if (!policy.mTypes.contains(Type.ACCESSIBILITY)) {
-            return builder.build();
-        }
-
-        List<AccessibilityHierarchyCheckResult> results = getHierarchyCheckResults(
-                builder.mMetric,
-                view,
-                builder.mSrcMap,
-                image,
-                policy.mChecks);
-
-        for (AccessibilityHierarchyCheckResult result : results) {
-            String category = getCheckClassCategory(result.getSourceCheckClass());
-
-            ValidatorData.Level level = convertLevel(result.getType());
-            if (!filter.contains(level)) {
-                continue;
-            }
-
-            try {
-                IssueBuilder issueBuilder = new IssueBuilder()
-                        .setCategory(category)
-                        .setMsg(result.getMessage(Locale.ENGLISH).toString())
-                        .setLevel(level)
-                        .setFix(generateFix(result))
-                        .setSourceClass(result.getSourceCheckClass().getSimpleName());
-                if (result.getElement() != null) {
-                    issueBuilder.setSrcId(result.getElement().getCondensedUniqueId());
-                }
-                AccessibilityHierarchyCheck subclass = AccessibilityCheckPreset
-                        .getHierarchyCheckForClass(result
-                                .getSourceCheckClass()
-                                .asSubclass(AccessibilityHierarchyCheck.class));
-                if (subclass != null) {
-                    issueBuilder.setHelpfulUrl(subclass.getHelpUrl());
-                }
-                builder.mIssues.add(issueBuilder.build());
-            } catch (Exception e) {
-                builder.mIssues.add(new IssueBuilder()
-                        .setCategory(category)
-                        .setType(Type.INTERNAL_ERROR)
-                        .setMsg(e.getMessage())
-                        .setLevel(Level.ERROR)
-                        .setSourceClass("AccessibilityValidator").build());
-            }
-        }
-        builder.mMetric.endTimer();
-        return builder.build();
-    }
-
-    @NotNull
-    private static String getCheckClassCategory(@NotNull Class<?> checkClass) {
-        try {
-            Class<? extends AccessibilityHierarchyCheck> subClass =
-                    checkClass.asSubclass(AccessibilityHierarchyCheck.class);
-            AccessibilityHierarchyCheck check =
-                    AccessibilityCheckPreset.getHierarchyCheckForClass(subClass);
-            return (check == null) ? "Accessibility" : check.getCategory().name();
-        } catch (ClassCastException e) {
-            return "Accessibility";
-        }
-    }
-
-    @NotNull
-    private static ValidatorData.Level convertLevel(@NotNull AccessibilityCheckResultType type) {
-        switch (type) {
-            case ERROR:
-                return Level.ERROR;
-            case WARNING:
-                return Level.WARNING;
-            case INFO:
-                return Level.INFO;
-            // TODO: Maybe useful later?
-            case SUPPRESSED:
-            case NOT_RUN:
-            default:
-                return Level.VERBOSE;
-        }
-    }
-
-    @Nullable
-    private static ValidatorData.Fix generateFix(@NotNull AccessibilityHierarchyCheckResult result) {
-        // TODO: Once ATF is ready to return us with appropriate fix, build proper fix here.
-        return new Fix("");
-    }
-
-    @NotNull
-    private static List<AccessibilityHierarchyCheckResult> getHierarchyCheckResults(
-            @NotNull Metric metric,
-            @NotNull View view,
-            @NotNull BiMap<Long, View> originMap,
-            @Nullable BufferedImage image,
-            HashSet<AccessibilityHierarchyCheck> policyChecks) {
-
-        @NotNull Set<AccessibilityHierarchyCheck> checks = policyChecks.isEmpty()
-                ? AccessibilityCheckPreset
-                        .getAccessibilityHierarchyChecksForPreset(AccessibilityCheckPreset.LATEST)
-                : policyChecks;
-
-        @NotNull AccessibilityHierarchyAndroid hierarchy = AccessibilityHierarchyAndroid
-                .newBuilder(view)
-                .setViewOriginMap(originMap)
-                .build();
-        ArrayList<AccessibilityHierarchyCheckResult> a11yResults = new ArrayList();
-
-        Parameters parameters = null;
-        if (image != null) {
-            parameters = new Parameters();
-            parameters.putScreenCapture(new AtfBufferedImage(image, metric));
-        }
-
-        for (AccessibilityHierarchyCheck check : checks) {
-            a11yResults.addAll(check.runCheckOnHierarchy(hierarchy, null, parameters));
-        }
-
-        return a11yResults;
-    }
-}
diff --git a/validator/src/com/android/tools/idea/validator/accessibility/AtfBufferedImage.java b/validator/src/com/android/tools/idea/validator/accessibility/AtfBufferedImage.java
deleted file mode 100644
index 59d20a8..0000000
--- a/validator/src/com/android/tools/idea/validator/accessibility/AtfBufferedImage.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2020 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.idea.validator.accessibility;
-
-import com.android.tools.idea.validator.ValidatorResult.Metric;
-import com.android.tools.layoutlib.annotations.NotNull;
-
-import java.awt.image.BufferedImage;
-import java.awt.image.DataBufferInt;
-import java.awt.image.WritableRaster;
-
-import com.google.android.apps.common.testing.accessibility.framework.utils.contrast.Image;
-
-import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
-
-/**
- * Image implementation to be used in Accessibility Test Framework.
- */
-public class AtfBufferedImage implements Image {
-
-    // The source buffered image, expected to contain the full screen rendered image of the layout.
-    @NotNull private final BufferedImage mBufferedImage;
-    // Metrics to be returned
-    @NotNull private final Metric mMetric;
-
-    private final int mLeft;
-    private final int mTop;
-    private final int mWidth;
-    private final int mHeight;
-
-    AtfBufferedImage(@NotNull BufferedImage image, @NotNull Metric metric) {
-        assert(image.getType() == TYPE_INT_ARGB);
-        mBufferedImage = image;
-        mMetric = metric;
-        mWidth = mBufferedImage.getWidth();
-        mHeight = mBufferedImage.getHeight();
-        mLeft = 0;
-        mTop = 0;
-    }
-
-    private AtfBufferedImage(
-            @NotNull BufferedImage image,
-            @NotNull Metric metric,
-            int left,
-            int top,
-            int width,
-            int height) {
-        mBufferedImage = image;
-        mMetric = metric;
-        mLeft = left;
-        mTop = top;
-        mWidth = width;
-        mHeight = height;
-    }
-
-    @Override
-    public int getHeight() {
-        return mHeight;
-    }
-
-    @Override
-    public int getWidth() {
-        return mWidth;
-    }
-
-    @Override
-    @NotNull
-    public Image crop(int left, int top, int width, int height) {
-        return new AtfBufferedImage(mBufferedImage, mMetric, left, top, width, height);
-    }
-
-    @Override
-    @NotNull
-    public int[] getPixels() {
-        // ATF unfortunately writes in-place on returned int[] for color analysis.
-        // It must return copied list otherwise it won't work.
-        BufferedImage cropped = mBufferedImage.getSubimage(mLeft, mTop, mWidth, mHeight);
-        WritableRaster raster = cropped.copyData(
-                cropped.getRaster().createCompatibleWritableRaster());
-        int[] toReturn = ((DataBufferInt) raster.getDataBuffer()).getData();
-        mMetric.mImageMemoryBytes += toReturn.length * 4;
-        return toReturn;
-    }
-}
diff --git a/validator/src/com/android/tools/idea/validator/hierarchy/CustomHierarchyHelper.java b/validator/src/com/android/tools/idea/validator/hierarchy/CustomHierarchyHelper.java
new file mode 100644
index 0000000..eee2f32
--- /dev/null
+++ b/validator/src/com/android/tools/idea/validator/hierarchy/CustomHierarchyHelper.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 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.idea.validator.hierarchy;
+
+import com.android.ide.common.rendering.api.LayoutlibCallback;
+
+import android.view.View;
+import android.widget.Checkable;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/** Helper for support lib dependencies. */
+public class CustomHierarchyHelper {
+    public static LayoutlibCallback sLayoutlibCallback;
+
+    /** Get the class instance from the studio based on the string class name. */
+    public static Class<?> getClassByName(String className) {
+        try {
+            return sLayoutlibCallback.findClass(className);
+        } catch (ClassNotFoundException ignore) {
+        }
+        return null;
+    }
+
+    /** Returns true if the view is of {@link Checkable} instance. False otherwise. */
+    public static boolean isCheckable(View fromView) {
+        LayoutlibCallback callback = sLayoutlibCallback;
+        if (callback == null) {
+            return false;
+        }
+
+        try {
+            // This is required as layoutlib does not know the support library such as
+            // MaterialButton. LayoutlibCallback calls for studio which understands all the maven
+            // pulled library.
+            Class button = callback.findClass(
+                    "com.google.android.material.button.MaterialButton");
+            if (button.isInstance(fromView)) {
+                Method isCheckable = button.getMethod("isCheckable");
+                Object toReturn = isCheckable.invoke(fromView);
+                return (toReturn instanceof Boolean) && ((Boolean) toReturn);
+            }
+        } catch (ClassNotFoundException |
+                 NoSuchMethodException |
+                 IllegalAccessException |
+                 InvocationTargetException ignore) {
+        }
+        return fromView instanceof Checkable;
+    }
+}
diff --git a/validator/src/com/google/android/apps/common/testing/accessibility/framework/uielement/AccessibilityHierarchyAndroid_ViewElementClassNamesAndroid_Delegate.java b/validator/src/com/google/android/apps/common/testing/accessibility/framework/uielement/AccessibilityHierarchyAndroid_ViewElementClassNamesAndroid_Delegate.java
deleted file mode 100644
index 025bd77..0000000
--- a/validator/src/com/google/android/apps/common/testing/accessibility/framework/uielement/AccessibilityHierarchyAndroid_ViewElementClassNamesAndroid_Delegate.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2020 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.apps.common.testing.accessibility.framework.uielement;
-
-import com.android.ide.common.rendering.api.LayoutlibCallback;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-public class AccessibilityHierarchyAndroid_ViewElementClassNamesAndroid_Delegate {
-
-    public static LayoutlibCallback sLayoutlibCallback;
-
-    @LayoutlibDelegate
-    public static Class<?> getClassByName(ViewHierarchyElementAndroid view, String className) {
-        Class toReturn = AccessibilityHierarchyAndroid
-                .ViewElementClassNamesAndroid.getClassByName_Original(view, className);
-        if (toReturn == null && sLayoutlibCallback != null) {
-            try {
-                return sLayoutlibCallback.findClass(className);
-            } catch (ClassNotFoundException ignore) {
-            }
-        }
-
-        return toReturn;
-    }
-}