Merge Android R

Bug: 168057903
Merged-In: Iaf511a01e9a73da3d82251eae2ea49f39f78627d
Change-Id: Ie133933bdb9e7b7bc4d432cd0d19733f2aab9f07
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 35961a2..da10743 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -1,11 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="CompilerConfiguration">
-    <option name="DEFAULT_COMPILER" value="Javac" />
     <excludeFromCompile>
-      <directory url="file://$PROJECT_DIR$/create/tests/mock_data" includeSubdirectories="true" />
+      <directory url="file://$PROJECT_DIR$/create/tests/res/mock_data" includeSubdirectories="true" />
     </excludeFromCompile>
-    <resourceExtensions />
     <wildcardResourcePatterns>
       <entry name="!?*.java" />
       <entry name="!?*.form" />
@@ -16,10 +14,5 @@
       <entry name="!?*.kt" />
       <entry name="!?*.clj" />
     </wildcardResourcePatterns>
-    <annotationProcessing>
-      <profile default="true" name="Default" enabled="false">
-        <processorPath useClasspath="true" />
-      </profile>
-    </annotationProcessing>
   </component>
 </project>
\ No newline at end of file
diff --git a/.idea/libraries/framework_jar.xml b/.idea/libraries/framework_jar.xml
index 6688b81..2ad7916 100644
--- a/.idea/libraries/framework_jar.xml
+++ b/.idea/libraries/framework_jar.xml
@@ -1,7 +1,7 @@
 <component name="libraryTable">
   <library name="framework.jar">
     <CLASSES>
-      <root url="jar://$PROJECT_DIR$/../../out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/classes.jar!/" />
+      <root url="jar://$PROJECT_DIR$/../../out/soong/.intermediates/frameworks/layoutlib/temp_layoutlib/linux_glibc_common/gen/temp_layoutlib.jar!/" />
     </CLASSES>
     <JAVADOC />
     <SOURCES>
diff --git a/.idea/libraries/guava.xml b/.idea/libraries/guava.xml
new file mode 100644
index 0000000..0c18a01
--- /dev/null
+++ b/.idea/libraries/guava.xml
@@ -0,0 +1,11 @@
+<component name="libraryTable">
+    <library name="guava">
+        <CLASSES>
+            <root url="jar://$PROJECT_DIR$/../../out/soong/.intermediates/external/guava/guava-jre/linux_glibc_common/javac/guava-jre.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+            <root url="file://$PROJECT_DIR$/../../external/guava/guava/src" />
+        </SOURCES>
+    </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/hamcrest.xml b/.idea/libraries/hamcrest.xml
new file mode 100644
index 0000000..0eebff2
--- /dev/null
+++ b/.idea/libraries/hamcrest.xml
@@ -0,0 +1,11 @@
+<component name="libraryTable">
+    <library name="hamcrest">
+        <CLASSES>
+            <root url="jar://$PROJECT_DIR$/../../out/soong/.intermediates/external/hamcrest/hamcrest-core/hamcrest/linux_glibc_common/javac/hamcrest.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+            <root url="file://$PROJECT_DIR$/../../external/hamcrest/hamcrest-core/src/main/java" />
+        </SOURCES>
+    </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/junit.xml b/.idea/libraries/junit.xml
index 420a6f5..b48ed46 100644
--- a/.idea/libraries/junit.xml
+++ b/.idea/libraries/junit.xml
@@ -1,7 +1,7 @@
 <component name="libraryTable">
   <library name="junit">
     <CLASSES>
-      <root url="jar://$PROJECT_DIR$/../../out/host/common/obj/JAVA_LIBRARIES/junit-host_intermediates/javalib.jar!/" />
+      <root url="jar://$PROJECT_DIR$/../../out/soong/.intermediates/external/junit/junit/linux_glibc_common/javac/junit.jar!/" />
     </CLASSES>
     <JAVADOC />
     <SOURCES>
diff --git a/.idea/libraries/mockito.xml b/.idea/libraries/mockito.xml
index abaad21..516f451 100644
--- a/.idea/libraries/mockito.xml
+++ b/.idea/libraries/mockito.xml
@@ -1,9 +1,11 @@
 <component name="libraryTable">
   <library name="mockito">
     <CLASSES>
-      <root url="jar://$PROJECT_DIR$/../../out/host/common/obj/JAVA_LIBRARIES/mockito-host_intermediates/javalib.jar!/" />
+      <root url="jar://$PROJECT_DIR$/../../out/soong/.intermediates/external/mockito/mockito/linux_glibc_common/combined/mockito.jar!/" />
     </CLASSES>
     <JAVADOC />
-    <SOURCES />
+    <SOURCES>
+      <root url="file://$PROJECT_DIR$/../../external/mockito/src/main/java" />
+    </SOURCES>
   </library>
 </component>
\ No newline at end of file
diff --git a/.idea/libraries/objenesis.xml b/.idea/libraries/objenesis.xml
index 04f67ff..fbf09e1 100644
--- a/.idea/libraries/objenesis.xml
+++ b/.idea/libraries/objenesis.xml
@@ -1,7 +1,7 @@
 <component name="libraryTable">
   <library name="objenesis">
     <CLASSES>
-      <root url="jar://$PROJECT_DIR$/../../out/host/common/obj/JAVA_LIBRARIES/objenesis-host_intermediates/javalib.jar!/" />
+      <root url="jar://$PROJECT_DIR$/../../out/soong/.intermediates/external/objenesis/objenesis/linux_glibc_common/javac/objenesis.jar!/" />
     </CLASSES>
     <JAVADOC />
     <SOURCES />
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 44b47f2..22a2093 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="EntryPointsManager">
-    <entry_points version="2.0" />
     <list size="1">
       <item index="0" class="java.lang.String" itemvalue="com.android.tools.layoutlib.annotations.LayoutlibDelegate" />
     </list>
@@ -14,30 +13,43 @@
     <option name="myDefaultNotNull" value="android.annotation.NonNull" />
     <option name="myNullables">
       <value>
-        <list size="6">
+        <list size="13">
           <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" />
           <item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
           <item index="4" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
           <item index="5" class="java.lang.String" itemvalue="android.annotation.Nullable" />
+          <item index="6" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
+          <item index="7" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
+          <item index="8" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
+          <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" />
         </list>
       </value>
     </option>
     <option name="myNotNulls">
       <value>
-        <list size="6">
+        <list size="12">
           <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" />
           <item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
           <item index="4" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
           <item index="5" class="java.lang.String" itemvalue="android.annotation.NonNull" />
+          <item index="6" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
+          <item index="7" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
+          <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" />
         </list>
       </value>
     </option>
   </component>
-  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_9" default="false" 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 8d61f6c..50836c1 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -5,12 +5,12 @@
       <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$/legacy/legacy.iml" filepath="$PROJECT_DIR$/legacy/legacy.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" />
       <module fileurl="file://$PROJECT_DIR$/remote/tests/remote tests.iml" filepath="$PROJECT_DIR$/remote/tests/remote tests.iml" group="remote" />
       <module fileurl="file://$PROJECT_DIR$/studio-custom-widgets/studio-android-widgets.iml" filepath="$PROJECT_DIR$/studio-custom-widgets/studio-android-widgets.iml" />
+      <module fileurl="file://$PROJECT_DIR$/validator/validator.iml" filepath="$PROJECT_DIR$/validator/validator.iml" />
     </modules>
   </component>
 </project>
\ No newline at end of file
diff --git a/.idea/runConfigurations/All_in_bridge.xml b/.idea/runConfigurations/All_in_bridge.xml
index 07e39cd..1e82efc 100644
--- a/.idea/runConfigurations/All_in_bridge.xml
+++ b/.idea/runConfigurations/All_in_bridge.xml
@@ -2,7 +2,7 @@
   <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="1.8.0_60-b23" />
+    <option name="ALTERNATIVE_JRE_PATH" value="11" />
     <option name="PACKAGE_NAME" value="" />
     <option name="MAIN_CLASS_NAME" value="" />
     <option name="METHOD_NAME" value="" />
diff --git a/.idea/runConfigurations/Bridge_quick.xml b/.idea/runConfigurations/Bridge_quick.xml
index b402849..e9797b0 100644
--- a/.idea/runConfigurations/Bridge_quick.xml
+++ b/.idea/runConfigurations/Bridge_quick.xml
@@ -1,22 +1,14 @@
 <component name="ProjectRunConfigurationManager">
   <configuration default="false" name="Bridge quick" type="JUnit" factoryName="JUnit">
-    <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
     <module name="bridge" />
-    <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
-    <option name="ALTERNATIVE_JRE_PATH" value="" />
-    <option name="PACKAGE_NAME" />
+    <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="" />
     <option name="TEST_OBJECT" value="pattern" />
-    <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 />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
     <patterns>
       <pattern testClass="com.android.layoutlib.bridge.TestDelegates" />
       <pattern testClass="android.graphics.Matrix_DelegateTest" />
@@ -24,6 +16,8 @@
     </patterns>
     <RunnerSettings RunnerId="Run" />
     <ConfigurationWrapper RunnerId="Run" />
-    <method />
+    <method v="2">
+      <option name="Make" enabled="true" />
+    </method>
   </configuration>
-</component>
+</component>
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 565a709..b38baa2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -25,9 +25,10 @@
     tools: ["layoutlib_create"],
     out: ["temp_layoutlib.jar"],
     srcs: [
+        ":atf-prebuilt{.jar}",
         ":core-icu4j{.jar}",
         ":core-libart{.jar}",
-        ":framework{.jar}",
+        ":framework-all{.jar}",
         ":ext{.jar}",
         ":icu4j-icudata-jarjar{.jar}", // HOST
         ":icu4j-icutzdata-jarjar{.jar}", // HOST
diff --git a/README b/README
index 0fea9bd..3b111c6 100644
--- a/README
+++ b/README
@@ -1,4 +1,8 @@
-Layoutlib is a custom version of the android View framework designed to run inside Eclipse.
-The goal of the library is to provide layout rendering in Eclipse that are very very close to their rendering on devices.
+Layoutlib is a custom version of the android View framework designed to run inside Android Studio.
+The goal of the library is to provide a preview of a layout in Android Studio that is very close
+to its rendering on devices.
 
-None of the com.android.* or android.* classes in layoutlib run on devices.
\ No newline at end of file
+None of the com.android.* or android.* classes in layoutlib run on devices.
+
+For more details on layoutlib building process see create/README.txt
+
diff --git a/bridge/.idea/bridge.iml b/bridge/.idea/bridge.iml
deleted file mode 100644
index d6ebd48..0000000
--- a/bridge/.idea/bridge.iml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?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$" />
-    <orderEntry type="inheritedJdk" />
-    <orderEntry type="sourceFolder" forTests="false" />
-  </component>
-</module>
\ No newline at end of file
diff --git a/bridge/.idea/compiler.xml b/bridge/.idea/compiler.xml
deleted file mode 100644
index 40ed937..0000000
--- a/bridge/.idea/compiler.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="CompilerConfiguration">
-    <wildcardResourcePatterns>
-      <entry name="!?*.java" />
-      <entry name="!?*.form" />
-      <entry name="!?*.class" />
-      <entry name="!?*.groovy" />
-      <entry name="!?*.scala" />
-      <entry name="!?*.flex" />
-      <entry name="!?*.kt" />
-      <entry name="!?*.clj" />
-    </wildcardResourcePatterns>
-  </component>
-</project>
\ No newline at end of file
diff --git a/bridge/.idea/encodings.xml b/bridge/.idea/encodings.xml
deleted file mode 100644
index 97626ba..0000000
--- a/bridge/.idea/encodings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="Encoding">
-    <file url="PROJECT" charset="UTF-8" />
-  </component>
-</project>
\ No newline at end of file
diff --git a/bridge/.idea/misc.xml b/bridge/.idea/misc.xml
deleted file mode 100644
index 54f4c9f..0000000
--- a/bridge/.idea/misc.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="MavenImportPreferences">
-    <option name="generalSettings">
-      <MavenGeneralSettings>
-        <option name="mavenHome" value="Bundled (Maven 3)" />
-      </MavenGeneralSettings>
-    </option>
-  </component>
-  <component name="ProjectDictionaryState">
-    <dictionary name="diegoperez" />
-  </component>
-  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" />
-</project>
\ No newline at end of file
diff --git a/bridge/.idea/modules.xml b/bridge/.idea/modules.xml
deleted file mode 100644
index 4683164..0000000
--- a/bridge/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="ProjectModuleManager">
-    <modules>
-      <module fileurl="file://$PROJECT_DIR$/.idea/bridge.iml" filepath="$PROJECT_DIR$/.idea/bridge.iml" />
-    </modules>
-  </component>
-</project>
\ No newline at end of file
diff --git a/bridge/.idea/workspace.xml b/bridge/.idea/workspace.xml
deleted file mode 100644
index b48406a..0000000
--- a/bridge/.idea/workspace.xml
+++ /dev/null
@@ -1,297 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="ChangeListManager">
-    <list default="true" id="8a92e766-359b-4589-8d1f-ac1a44fb0c89" name="Default" comment="" />
-    <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
-    <option name="TRACKING_ENABLED" value="true" />
-    <option name="SHOW_DIALOG" value="false" />
-    <option name="HIGHLIGHT_CONFLICTS" value="true" />
-    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
-    <option name="LAST_RESOLUTION" value="IGNORE" />
-  </component>
-  <component name="FileEditorManager">
-    <leaf />
-  </component>
-  <component name="GradleLocalSettings">
-    <option name="modificationStamps">
-      <map>
-        <entry key="$PROJECT_DIR$/../../../../nyc-dev/tools/base/layoutlib-api" value="1486045798848" />
-      </map>
-    </option>
-    <option name="externalProjectsViewState">
-      <projects_view />
-    </option>
-  </component>
-  <component name="ProjectFrameBounds">
-    <option name="x" value="2560" />
-    <option name="y" value="18" />
-    <option name="width" value="3840" />
-    <option name="height" value="2123" />
-  </component>
-  <component name="ProjectInspectionProfilesVisibleTreeState">
-    <entry key="Project Default">
-      <profile-state>
-        <expanded-state>
-          <State>
-            <id />
-          </State>
-          <State>
-            <id>Android</id>
-          </State>
-          <State>
-            <id>Android Lint</id>
-          </State>
-        </expanded-state>
-        <selected-state>
-          <State>
-            <id>Android</id>
-          </State>
-        </selected-state>
-      </profile-state>
-    </entry>
-  </component>
-  <component name="ProjectView">
-    <navigator currentView="ProjectPane" proportions="" version="1">
-      <flattenPackages />
-      <showMembers />
-      <showModules />
-      <showLibraryContents />
-      <hideEmptyPackages />
-      <abbreviatePackageNames />
-      <autoscrollToSource />
-      <autoscrollFromSource />
-      <sortByType />
-      <manualOrder />
-      <foldersAlwaysOnTop value="true" />
-    </navigator>
-    <panes>
-      <pane id="PackagesPane" />
-      <pane id="Scratches" />
-      <pane id="Scope" />
-      <pane id="ProjectPane">
-        <subPane>
-          <expand>
-            <path>
-              <item name="bridge" type="b2602c69:ProjectViewProjectNode" />
-              <item name="bridge" type="462c0819:PsiDirectoryNode" />
-            </path>
-          </expand>
-          <select />
-        </subPane>
-      </pane>
-    </panes>
-  </component>
-  <component name="PropertiesComponent">
-    <property name="GoToClass.includeLibraries" value="false" />
-    <property name="GoToClass.toSaveIncludeLibraries" value="false" />
-    <property name="GoToFile.includeJavaFiles" value="false" />
-    <property name="MemberChooser.sorted" value="false" />
-    <property name="MemberChooser.showClasses" value="true" />
-    <property name="MemberChooser.copyJavadoc" value="false" />
-    <property name="options.lastSelected" value="Errors" />
-    <property name="options.splitter.main.proportions" value="0.3" />
-    <property name="options.splitter.details.proportions" value="0.2" />
-    <property name="options.searchVisible" value="true" />
-    <property name="settings.editor.selected.configurable" value="editor.preferences.import" />
-    <property name="settings.editor.splitter.proportion" value="0.2" />
-    <property name="last_opened_file_path" value="$PROJECT_DIR$" />
-  </component>
-  <component name="RunDashboard">
-    <option name="ruleStates">
-      <list>
-        <RuleState>
-          <option name="name" value="ConfigurationTypeDashboardGroupingRule" />
-        </RuleState>
-        <RuleState>
-          <option name="name" value="StatusDashboardGroupingRule" />
-        </RuleState>
-      </list>
-    </option>
-  </component>
-  <component name="RunManager">
-    <configuration default="true" type="Applet" factoryName="Applet">
-      <option name="WIDTH" value="400" />
-      <option name="HEIGHT" value="300" />
-      <option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy" />
-      <module />
-    </configuration>
-    <configuration default="true" type="Application" factoryName="Application">
-      <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
-      <option name="MAIN_CLASS_NAME" />
-      <option name="VM_PARAMETERS" />
-      <option name="PROGRAM_PARAMETERS" />
-      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
-      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
-      <option name="ALTERNATIVE_JRE_PATH" />
-      <option name="ENABLE_SWING_INSPECTOR" value="false" />
-      <option name="ENV_VARIABLES" />
-      <option name="PASS_PARENT_ENVS" value="true" />
-      <module name="" />
-      <envs />
-    </configuration>
-    <configuration default="true" type="JUnit" factoryName="JUnit">
-      <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
-      <module name="" />
-      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
-      <option name="ALTERNATIVE_JRE_PATH" />
-      <option name="PACKAGE_NAME" />
-      <option name="MAIN_CLASS_NAME" />
-      <option name="METHOD_NAME" />
-      <option name="TEST_OBJECT" value="class" />
-      <option name="VM_PARAMETERS" value="-ea" />
-      <option name="PARAMETERS" />
-      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
-      <option name="ENV_VARIABLES" />
-      <option name="PASS_PARENT_ENVS" value="true" />
-      <option name="TEST_SEARCH_SCOPE">
-        <value defaultName="moduleWithDependencies" />
-      </option>
-      <envs />
-      <patterns />
-    </configuration>
-    <configuration default="true" type="Remote" factoryName="Remote">
-      <option name="USE_SOCKET_TRANSPORT" value="true" />
-      <option name="SERVER_MODE" value="false" />
-      <option name="SHMEM_ADDRESS" value="javadebug" />
-      <option name="HOST" value="localhost" />
-      <option name="PORT" value="5005" />
-    </configuration>
-    <configuration default="true" type="TestNG" factoryName="TestNG">
-      <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
-      <module name="" />
-      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
-      <option name="ALTERNATIVE_JRE_PATH" />
-      <option name="SUITE_NAME" />
-      <option name="PACKAGE_NAME" />
-      <option name="MAIN_CLASS_NAME" />
-      <option name="METHOD_NAME" />
-      <option name="GROUP_NAME" />
-      <option name="TEST_OBJECT" value="CLASS" />
-      <option name="VM_PARAMETERS" value="-ea" />
-      <option name="PARAMETERS" />
-      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
-      <option name="OUTPUT_DIRECTORY" />
-      <option name="ANNOTATION_TYPE" />
-      <option name="ENV_VARIABLES" />
-      <option name="PASS_PARENT_ENVS" value="true" />
-      <option name="TEST_SEARCH_SCOPE">
-        <value defaultName="moduleWithDependencies" />
-      </option>
-      <option name="USE_DEFAULT_REPORTERS" value="false" />
-      <option name="PROPERTIES_FILE" />
-      <envs />
-      <properties />
-      <listeners />
-    </configuration>
-    <configuration default="true" type="#org.jetbrains.idea.devkit.run.PluginConfigurationType" factoryName="Plugin">
-      <module name="" />
-      <option name="VM_PARAMETERS" value="-Xmx512m -Xms256m -XX:MaxPermSize=250m -ea" />
-      <option name="PROGRAM_PARAMETERS" />
-      <predefined_log_file id="idea.log" enabled="true" />
-    </configuration>
-    <configuration name="&lt;template&gt;" type="WebApp" default="true" selected="false">
-      <Host>localhost</Host>
-      <Port>5050</Port>
-    </configuration>
-  </component>
-  <component name="ShelveChangesManager" show_recycled="false">
-    <option name="remove_strategy" value="false" />
-  </component>
-  <component name="TaskManager">
-    <task active="true" id="Default" summary="Default task">
-      <changelist id="8a92e766-359b-4589-8d1f-ac1a44fb0c89" name="Default" comment="" />
-      <created>1511883242491</created>
-      <option name="number" value="Default" />
-      <option name="presentableId" value="Default" />
-      <updated>1511883242491</updated>
-    </task>
-    <servers />
-  </component>
-  <component name="ToolWindowManager">
-    <frame x="2560" y="18" width="3840" height="2123" extended-state="0" />
-    <layout>
-      <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
-      <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32860184" sideWeight="0.49526584" order="6" side_tool="false" content_ui="tabs" />
-      <window_info id="Palette&#9;" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
-      <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.34767523" sideWeight="0.43609673" order="7" side_tool="true" content_ui="tabs" />
-      <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.34698126" sideWeight="0.5639033" order="2" side_tool="false" content_ui="tabs" />
-      <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="false" weight="0.32338655" sideWeight="0.4821803" order="9" side_tool="false" content_ui="tabs" />
-      <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.35600278" sideWeight="0.49666223" order="10" side_tool="false" content_ui="tabs" />
-      <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
-      <window_info id="Project" active="true" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.3227345" sideWeight="0.48438585" order="0" side_tool="false" content_ui="tabs" />
-      <window_info id="JProfiler" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4338588" sideWeight="0.47117063" order="12" side_tool="false" content_ui="tabs" />
-      <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.24960127" sideWeight="0.49757785" order="1" side_tool="false" content_ui="tabs" />
-      <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
-      <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
-      <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.3650243" sideWeight="0.85578585" order="3" side_tool="false" content_ui="tabs" />
-      <window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.16108453" sideWeight="0.51561415" order="7" side_tool="true" content_ui="tabs" />
-      <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
-      <window_info id="Nl-Palette" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
-      <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
-      <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="SLIDING" type="SLIDING" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
-      <window_info id="Gerrit" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.333564" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" />
-      <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" />
-      <window_info id="Analyze Dataflow to" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32941177" sideWeight="0.49821427" order="13" side_tool="false" content_ui="tabs" />
-      <window_info id="Properties" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
-      <window_info id="Capture Tool" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
-      <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.22288676" sideWeight="0.49111968" order="2" side_tool="false" content_ui="combo" />
-      <window_info id="Memory View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.00996016" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
-      <window_info id="Inspection Results" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.3293623" sideWeight="0.49248666" order="16" side_tool="false" content_ui="tabs" />
-      <window_info id="Dynamic Properties" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32988048" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" />
-      <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.13046496" sideWeight="0.26683939" order="8" side_tool="false" content_ui="tabs" />
-      <window_info id="Image Layers" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
-      <window_info id="Capture Analysis" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
-      <window_info id="Run Dashboard" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" />
-      <window_info id="Documentation" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="FLOATING" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" x="1292" y="82" width="2360" height="1327" />
-      <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
-      <window_info id="Coverage" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25976095" sideWeight="0.5061444" order="11" side_tool="true" content_ui="tabs" />
-      <window_info id="Runtime Dashboard" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" />
-      <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.2984039" sideWeight="0.8713299" order="1" side_tool="false" content_ui="tabs" />
-      <window_info id="Theme Preview" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="10" side_tool="false" content_ui="tabs" />
-    </layout>
-  </component>
-  <component name="VcsContentAnnotationSettings">
-    <option name="myLimit" value="2678400000" />
-  </component>
-  <component name="XDebuggerManager">
-    <breakpoint-manager />
-    <watches-manager />
-  </component>
-  <component name="editorHistoryManager">
-    <entry file="file://$PROJECT_DIR$/bridge.iml">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="0">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-  </component>
-  <component name="masterDetails">
-    <states>
-      <state key="ProjectJDKs.UI">
-        <settings>
-          <last-edited>1.6</last-edited>
-          <splitter-proportions>
-            <option name="proportions">
-              <list>
-                <option value="0.2" />
-              </list>
-            </option>
-          </splitter-proportions>
-        </settings>
-      </state>
-      <state key="ScopeChooserConfigurable.UI">
-        <settings>
-          <splitter-proportions>
-            <option name="proportions">
-              <list>
-                <option value="0.23280424" />
-              </list>
-            </option>
-          </splitter-proportions>
-        </settings>
-      </state>
-    </states>
-  </component>
-</project>
\ No newline at end of file
diff --git a/bridge/.settings/README.txt b/bridge/.settings/README.txt
deleted file mode 100644
index 9120b20..0000000
--- a/bridge/.settings/README.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Copy this in eclipse project as a .settings folder at the root.
-This ensure proper compilation compliance and warning/error levels.
\ No newline at end of file
diff --git a/bridge/.settings/org.eclipse.jdt.core.prefs b/bridge/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index 5381a0e..0000000
--- a/bridge/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,93 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.annotation.nonnull=com.android.annotations.NonNull
-org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=com.android.annotations.NonNullByDefault
-org.eclipse.jdt.core.compiler.annotation.nonnullisdefault=disabled
-org.eclipse.jdt.core.compiler.annotation.nullable=com.android.annotations.Nullable
-org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.6
-org.eclipse.jdt.core.compiler.debug.lineNumber=generate
-org.eclipse.jdt.core.compiler.debug.localVariable=generate
-org.eclipse.jdt.core.compiler.debug.sourceFile=generate
-org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
-org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
-org.eclipse.jdt.core.compiler.problem.deadCode=warning
-org.eclipse.jdt.core.compiler.problem.deprecation=warning
-org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
-org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
-org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
-org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
-org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
-org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
-org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
-org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
-org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
-org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
-org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
-org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
-org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
-org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
-org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
-org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
-org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
-org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
-org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
-org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
-org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
-org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
-org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
-org.eclipse.jdt.core.compiler.problem.nullReference=error
-org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo=warning
-org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
-org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
-org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
-org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
-org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
-org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation=error
-org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning
-org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
-org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
-org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
-org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
-org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
-org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
-org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
-org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
-org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
-org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
-org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
-org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
-org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled
-org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
-org.eclipse.jdt.core.compiler.problem.unclosedCloseable=error
-org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
-org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
-org.eclipse.jdt.core.compiler.problem.unusedImport=warning
-org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
-org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
-org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
-org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
-org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
-org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
-org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
-org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
-org.eclipse.jdt.core.compiler.source=1.6
diff --git a/bridge/Android.bp b/bridge/Android.bp
index f0958dd..fa07cb8 100644
--- a/bridge/Android.bp
+++ b/bridge/Android.bp
@@ -18,10 +18,10 @@
     name: "layoutlib",
 
     srcs: ["src/**/*.java"],
-    java_version: "1.8",
     java_resource_dirs: ["resources"],
 
     libs: [
+        "kxml2-2.3.0",
         "layoutlib_api-prebuilt",
         "tools-common-prebuilt",
         "guava",
@@ -31,6 +31,7 @@
         "temp_layoutlib",
         "ninepatch-prebuilt",
         "layoutlib-common",
+        "layoutlib-validator",
     ],
 
     dist: {
@@ -42,10 +43,10 @@
     name: "layoutlib-no-framework",
 
     srcs: ["src/**/*.java"],
-    java_version: "1.8",
     java_resource_dirs: ["resources"],
 
     libs: [
+        "kxml2-2.3.0",
         "temp_layoutlib",
         "guava",
     ],
@@ -56,6 +57,7 @@
         "tools-common-prebuilt",
         "ninepatch-prebuilt",
         "layoutlib-common",
+        "layoutlib-validator",
     ],
 
     dist: {
diff --git a/bridge/bridge.iml b/bridge/bridge.iml
index 8a65f99..9358a54 100644
--- a/bridge/bridge.iml
+++ b/bridge/bridge.iml
@@ -10,6 +10,7 @@
       <sourceFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/src/main/myapplication.widgets" isTestSource="true" />
       <excludeFolder url="file://$MODULE_DIR$/.settings" />
       <excludeFolder url="file://$MODULE_DIR$/bin" />
+      <excludeFolder url="file://$MODULE_DIR$/out" />
       <excludeFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/.gradle" />
       <excludeFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/.idea" />
       <excludeFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/build/generated" />
@@ -27,17 +28,6 @@
     <orderEntry type="sourceFolder" forTests="false" />
     <orderEntry type="library" name="layoutlib_api-prebuilt" level="project" />
     <orderEntry type="module-library">
-      <library name="ninepatch-prebuilt">
-        <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/ninepatch/ninepatch-prebuilt.jar!/" />
-        </CLASSES>
-        <JAVADOC />
-        <SOURCES>
-          <root url="file://$ANDROID_SRC$/tools/base/ninepatch/src/main/java" />
-        </SOURCES>
-      </library>
-    </orderEntry>
-    <orderEntry type="module-library">
       <library name="tools-common-prebuilt">
         <ANNOTATIONS>
           <root url="file://$MODULE_DIR$/.." />
@@ -52,28 +42,7 @@
       </library>
     </orderEntry>
     <orderEntry type="library" name="framework.jar" level="project" />
-    <orderEntry type="module-library" scope="TEST">
-      <library name="kxml2-2.3.0">
-        <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/kxml2/kxml2-2.3.0.jar!/" />
-        </CLASSES>
-        <JAVADOC />
-        <SOURCES>
-          <root url="file://$MODULE_DIR$/../../../libcore/xml/src/main/java" />
-        </SOURCES>
-      </library>
-    </orderEntry>
-    <orderEntry type="module-library">
-      <library name="guava">
-        <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/22.0/guava-22.0.jar!/" />
-        </CLASSES>
-        <JAVADOC />
-        <SOURCES>
-          <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/22.0/guava-22.0-sources.jar!/" />
-        </SOURCES>
-      </library>
-    </orderEntry>
+    <orderEntry type="library" name="guava" level="project" />
     <orderEntry type="module-library" scope="TEST">
       <library name="sdk-common">
         <CLASSES>
@@ -89,5 +58,49 @@
     <orderEntry type="library" scope="TEST" name="mockito" level="project" />
     <orderEntry type="library" scope="TEST" name="objenesis" level="project" />
     <orderEntry type="module" module-name="common" />
+    <orderEntry type="module-library">
+      <library name="kxml2-2.3.0">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/kxml2/kxml2-2.3.0.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="file://$MODULE_DIR$/../../../libcore/xml/src/main/java" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="library" scope="TEST" name="hamcrest" level="project" />
+    <orderEntry type="module-library">
+      <library name="ninepatch-prebuilt">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/ninepatch/ninepatch-prebuilt.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/ninepatch/ninepatch-prebuilt-sources.jar!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library" scope="TEST">
+      <library name="trove4j">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/net/sf/trove4j/trove4j/1.1/trove4j-1.1.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/net/sf/trove4j/trove4j/1.1/trove4j-1.1-sources.jar!/" />
+        </SOURCES>
+      </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>
   </component>
 </module>
\ No newline at end of file
diff --git a/bridge/resources/bars/v28/hdpi/stat_sys_battery_100.png b/bridge/resources/bars/v28/hdpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..f17189a
--- /dev/null
+++ b/bridge/resources/bars/v28/hdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/bridge/resources/bars/v28/mdpi/stat_sys_battery_100.png b/bridge/resources/bars/v28/mdpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..2a9757d
--- /dev/null
+++ b/bridge/resources/bars/v28/mdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/bridge/resources/bars/v28/xhdpi/stat_sys_battery_100.png b/bridge/resources/bars/v28/xhdpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..555bcd9
--- /dev/null
+++ b/bridge/resources/bars/v28/xhdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/bridge/resources/bars/v28/xhdpi/stat_sys_wifi_signal_4_fully.xml b/bridge/resources/bars/v28/xhdpi/stat_sys_wifi_signal_4_fully.xml
new file mode 100644
index 0000000..5d3dbae
--- /dev/null
+++ b/bridge/resources/bars/v28/xhdpi/stat_sys_wifi_signal_4_fully.xml
@@ -0,0 +1,29 @@
+<!--
+  ~ 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="16.31dp"
+        android:height="15dp"
+        android:viewportWidth="21.75"
+        android:viewportHeight="20.0">
+    <group
+        android:translateX="0.80"
+        android:translateY="1.25">
+        <path
+            android:pathData="M19.95,5c0.4,-0.49 0.3,-1.22 -0.23,-1.56 -1.6,-1.05 -5.04,-2.9 -9.62,-2.9 -4.59,0 -8.03,1.85 -9.62,2.9C-0.05,3.78 -0.16,4.51 0.24,5l9.02,11.08c0.42,0.52 1.22,0.52 1.64,0L19.95,5z"
+            android:fillColor="#FFFFFFFF"/>
+    </group>
+</vector>
diff --git a/bridge/resources/bars/v28/xxhdpi/stat_sys_battery_100.png b/bridge/resources/bars/v28/xxhdpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..6474aad
--- /dev/null
+++ b/bridge/resources/bars/v28/xxhdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/bridge/src/android/animation/PropertyValuesHolder_Delegate.java b/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
index 1d7026c..556a063 100644
--- a/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
+++ b/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
@@ -40,9 +40,7 @@
  * implementation of the PropertyValuesHolder won't be able to access protected methods.
  *
  */
-/*package*/
-@SuppressWarnings("unused")
-class PropertyValuesHolder_Delegate {
+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
@@ -70,8 +68,11 @@
             Long methodId = METHOD_NAME_TO_ID.get(methodIndexName);
 
             if (methodId != null) {
-                // The method was already registered
-                return methodId;
+                // 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];
@@ -87,7 +88,9 @@
             }
 
             if (method != null) {
-                methodId = sNextId++;
+                if (methodId == null) {
+                    methodId = sNextId++;
+                }
                 ID_TO_METHOD.put(methodId, method);
                 METHOD_NAME_TO_ID.put(methodIndexName, methodId);
 
@@ -107,7 +110,8 @@
             method.setAccessible(true);
             method.invoke(target, args);
         } catch (IllegalAccessException | InvocationTargetException e) {
-            Bridge.getLog().error(null, "Unable to update property during animation", e, null);
+            Bridge.getLog().error(null, "Unable to update property during animation", e, null,
+                    null);
         }
     }
 
@@ -192,4 +196,9 @@
         }
         callMethod(target, methodID, params);
     }
+
+    public static void clearCaches() {
+        ID_TO_METHOD.clear();
+        METHOD_NAME_TO_ID.clear();
+    }
 }
diff --git a/bridge/src/android/app/Fragment_Delegate.java b/bridge/src/android/app/Fragment_Delegate.java
index f7654ce..8b216f0 100644
--- a/bridge/src/android/app/Fragment_Delegate.java
+++ b/bridge/src/android/app/Fragment_Delegate.java
@@ -13,10 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.app;
 
 import com.android.ide.common.rendering.api.LayoutlibCallback;
+import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.content.Context;
@@ -30,21 +30,9 @@
  *
  * The methods being re-implemented are the ones responsible for instantiating Fragment objects.
  * Because the classes of these objects are found in the project, these methods need access to
- * {@link LayoutlibCallback} object. They are however static methods, so the callback is set
- * before the inflation through {@link #setLayoutlibCallback(LayoutlibCallback)}.
+ * {@link LayoutlibCallback} object.
  */
 public class Fragment_Delegate {
-
-    private static LayoutlibCallback sLayoutlibCallback;
-
-    /**
-     * Sets the current {@link LayoutlibCallback} to be used to instantiate classes coming
-     * from the project being rendered.
-     */
-    public static void setLayoutlibCallback(LayoutlibCallback layoutlibCallback) {
-        sLayoutlibCallback = layoutlibCallback;
-    }
-
     /**
      * Like {@link #instantiate(Context, String, Bundle)} but with a null
      * argument Bundle.
@@ -71,8 +59,9 @@
     @LayoutlibDelegate
     /*package*/ static Fragment instantiate(Context context, String fname, Bundle args) {
         try {
-            if (sLayoutlibCallback != null) {
-                Fragment f = (Fragment) sLayoutlibCallback.loadView(fname,
+            if (context instanceof BridgeContext) {
+                BridgeContext bc = (BridgeContext) context;
+                Fragment f = (Fragment) bc.getLayoutlibCallback().loadView(fname,
                         new Class[0], new Object[0]);
 
                 if (args != null) {
@@ -83,18 +72,6 @@
             }
 
             return null;
-        } catch (ClassNotFoundException e) {
-            throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname
-                    + ": make sure class name exists, is public, and has an"
-                    + " empty constructor that is public", e);
-        } catch (java.lang.InstantiationException e) {
-            throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname
-                    + ": make sure class name exists, is public, and has an"
-                    + " empty constructor that is public", e);
-        } catch (IllegalAccessException e) {
-            throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname
-                    + ": make sure class name exists, is public, and has an"
-                    + " empty constructor that is public", e);
         } catch (Exception e) {
             throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname
                     + ": make sure class name exists, is public, and has an"
diff --git a/bridge/src/android/app/SystemServiceRegistry_Accessor.java b/bridge/src/android/app/SystemServiceRegistry_Accessor.java
deleted file mode 100644
index 591aee0..0000000
--- a/bridge/src/android/app/SystemServiceRegistry_Accessor.java
+++ /dev/null
@@ -1,29 +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.app;
-
-/**
- * Class to allow accessing {@link SystemServiceRegistry#getSystemServiceName}
- */
-public class SystemServiceRegistry_Accessor {
-    /**
-     * Gets the name of the system-level service that is represented by the specified class.
-     */
-    public static String getSystemServiceName(Class<?> serviceClass) {
-        return SystemServiceRegistry.getSystemServiceName(serviceClass);
-    }
-}
diff --git a/bridge/src/android/content/ClipboardManager.java b/bridge/src/android/content/ClipboardManager.java
new file mode 100644
index 0000000..f833175
--- /dev/null
+++ b/bridge/src/android/content/ClipboardManager.java
@@ -0,0 +1,127 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+
+public class ClipboardManager extends android.text.ClipboardManager {
+
+    /**
+     * Defines a listener callback that is invoked when the primary clip on the clipboard changes.
+     * Objects that want to register a listener call
+     * {@link android.content.ClipboardManager#addPrimaryClipChangedListener(OnPrimaryClipChangedListener)
+     * addPrimaryClipChangedListener()} with an
+     * object that implements OnPrimaryClipChangedListener.
+     *
+     */
+    public interface OnPrimaryClipChangedListener {
+
+        /**
+         * Callback that is invoked by {@link android.content.ClipboardManager} when the primary
+         * clip changes.
+         */
+        void onPrimaryClipChanged();
+    }
+
+    /** {@hide} */
+    public ClipboardManager(Context context, Handler handler) { }
+
+    /**
+     * Sets the current primary clip on the clipboard.  This is the clip that
+     * is involved in normal cut and paste operations.
+     *
+     * @param clip The clipped data item to set.
+     * @see #getPrimaryClip()
+     * @see #clearPrimaryClip()
+     */
+    public void setPrimaryClip(@NonNull ClipData clip) { }
+
+    /**
+     * Clears any current primary clip on the clipboard.
+     *
+     * @see #setPrimaryClip(ClipData)
+     */
+    public void clearPrimaryClip() { }
+
+    /**
+     * Returns the current primary clip on the clipboard.
+     *
+     * <em>If the application is not the default IME or does not have input focus this return
+     * {@code null}.</em>
+     *
+     * @see #setPrimaryClip(ClipData)
+     */
+    public @Nullable ClipData getPrimaryClip() {
+        return null;
+    }
+
+    /**
+     * Returns a description of the current primary clip on the clipboard
+     * but not a copy of its data.
+     *
+     * <em>If the application is not the default IME or does not have input focus this return
+     * {@code null}.</em>
+     *
+     * @see #setPrimaryClip(ClipData)
+     */
+    public @Nullable ClipDescription getPrimaryClipDescription() {
+        return null;
+    }
+
+    /**
+     * Returns true if there is currently a primary clip on the clipboard.
+     *
+     * <em>If the application is not the default IME or the does not have input focus this will
+     * return {@code false}.</em>
+     */
+    public boolean hasPrimaryClip() {
+        return false;
+    }
+
+    public void addPrimaryClipChangedListener(OnPrimaryClipChangedListener what) { }
+
+    public void removePrimaryClipChangedListener(OnPrimaryClipChangedListener what) { }
+
+    /**
+     * @deprecated Use {@link #getPrimaryClip()} instead.  This retrieves
+     * the primary clip and tries to coerce it to a string.
+     */
+    @Deprecated
+    public CharSequence getText() {
+        return null;
+    }
+
+    /**
+     * @deprecated Use {@link #setPrimaryClip(ClipData)} instead.  This
+     * creates a ClippedItem holding the given text and sets it as the
+     * primary clip.  It has no label or icon.
+     */
+    @Deprecated
+    public void setText(CharSequence text) { }
+
+    /**
+     * @deprecated Use {@link #hasPrimaryClip()} instead.
+     */
+    @Deprecated
+    public boolean hasText() {
+        return false;
+    }
+
+    void reportPrimaryClipChanged() { }
+}
diff --git a/bridge/src/android/content/res/AssetManager_Delegate.java b/bridge/src/android/content/res/AssetManager_Delegate.java
index 6b71752..c27df09 100644
--- a/bridge/src/android/content/res/AssetManager_Delegate.java
+++ b/bridge/src/android/content/res/AssetManager_Delegate.java
@@ -21,9 +21,6 @@
 
 import android.util.SparseArray;
 
-import java.io.IOException;
-import java.io.InputStream;
-
 /**
  * Delegate used to provide implementation of a select few native methods of {@link AssetManager}
  * <p/>
@@ -38,10 +35,6 @@
     private static final DelegateManager<AssetManager_Delegate> sManager =
             new DelegateManager<>(AssetManager_Delegate.class);
 
-    public static DelegateManager<AssetManager_Delegate> getDelegateManager() {
-        return sManager;
-    }
-
     // ---- delegate methods. ----
 
     @LayoutlibDelegate
@@ -56,20 +49,6 @@
     }
 
     @LayoutlibDelegate
-    public static InputStream open(AssetManager mgr, String fileName) throws IOException {
-        return mgr.open_Original(fileName);
-    }
-
-    @LayoutlibDelegate
-    public static InputStream open(AssetManager mgr, String fileName, int accessMode)
-            throws IOException {
-        if (!(mgr instanceof BridgeAssetManager)) {
-            return mgr.open_Original(fileName, accessMode);
-        }
-        return ((BridgeAssetManager) mgr).getAssetRepository().openAsset(fileName, accessMode);
-    }
-
-    @LayoutlibDelegate
     /*package*/ static long nativeThemeCreate(long ptr) {
         return Resources_Theme_Delegate.getDelegateManager()
                 .addNewDelegate(new Resources_Theme_Delegate());
@@ -86,6 +65,12 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static SparseArray<String> getAssignedPackageIdentifiers(AssetManager manager,
+            boolean includeOverlays, boolean includeLoaders) {
+        return new SparseArray<>();
+    }
+
+    @LayoutlibDelegate
     /*package*/ static String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid() {
         // AssetManager requires this not to be null
         return new String[0];
diff --git a/bridge/src/android/content/res/BridgeAssetManager.java b/bridge/src/android/content/res/BridgeAssetManager.java
index b67d3b6..c9b7095 100644
--- a/bridge/src/android/content/res/BridgeAssetManager.java
+++ b/bridge/src/android/content/res/BridgeAssetManager.java
@@ -20,6 +20,9 @@
 import com.android.ide.common.rendering.api.AssetRepository;
 import com.android.layoutlib.bridge.Bridge;
 
+import java.io.IOException;
+import java.io.InputStream;
+
 public class BridgeAssetManager extends AssetManager {
     @Nullable private AssetRepository mAssetRepository;
 
@@ -67,6 +70,17 @@
         return mAssetRepository;
     }
 
+    @Override
+    public InputStream open(String fileName, int accessMode) throws IOException {
+        return getAssetRepository().openAsset(fileName, accessMode);
+    }
+
+    @Override
+    public InputStream openNonAsset(int cookie, String fileName, int accessMode)
+            throws IOException {
+        return getAssetRepository().openNonAsset(cookie, fileName, accessMode);
+    }
+
     public BridgeAssetManager() {
     }
 }
diff --git a/bridge/src/android/content/res/BridgeTypedArray.java b/bridge/src/android/content/res/BridgeTypedArray.java
index bbb6405..5023dcb 100644
--- a/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/bridge/src/android/content/res/BridgeTypedArray.java
@@ -25,6 +25,7 @@
 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.ide.common.rendering.api.TextResourceValue;
 import com.android.internal.util.XmlUtils;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
@@ -38,6 +39,7 @@
 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;
@@ -47,6 +49,7 @@
 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;
@@ -192,8 +195,25 @@
      */
     @Override
     public CharSequence getText(int index) {
-        // FIXME: handle styled strings!
-        return getString(index);
+        if (!hasValue(index)) {
+            return null;
+        }
+        // As unfortunate as it is, it's possible to use enums with all attribute formats,
+        // not just integers/enums. So, we need to search the enums always. In case
+        // enums are used, the returned value is an integer.
+        Integer v = resolveEnumAttribute(index);
+        if (v != null) {
+            return String.valueOf((int) v);
+        }
+        ResourceValue resourceValue = mResourceData[index];
+        String value = resourceValue.getValue();
+        if (resourceValue instanceof TextResourceValue) {
+            String rawValue = resourceValue.getRawXmlValue();
+            if (rawValue != null && !rawValue.equals(value)) {
+                return Html.fromHtml(rawValue, FROM_HTML_MODE_COMPACT);
+            }
+        }
+        return value;
     }
 
     /**
@@ -248,7 +268,7 @@
             Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
                     String.format("\"%1$s\" in attribute \"%2$s\" is not a valid integer",
                             s, mNames[index]),
-                    null);
+                    null, null);
         }
         return defValue;
     }
@@ -271,7 +291,7 @@
             Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
                     String.format("\"%1$s\" in attribute \"%2$s\" cannot be converted to float.",
                             s, mNames[index]),
-                    null);
+                    null, null);
         }
         return defValue;
     }
@@ -429,7 +449,8 @@
                 // looks like we were unable to resolve the dimension value
                 Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
                         String.format("\"%1$s\" in attribute \"%2$s\" is not a valid format.",
-                                s, mNames[index]), null);
+                                s, mNames[index]),
+                        null, null);
             }
 
             return defValue;
@@ -460,7 +481,8 @@
             }
 
             Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
-                    "You must supply a " + name + " attribute.", null);
+                    "You must supply a " + name + " attribute.",
+                    null, null);
 
             return 0;
         }
@@ -531,7 +553,8 @@
         Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
                 String.format(
                         "\"%1$s\" in attribute \"%2$s\" cannot be converted to a fraction.",
-                        value, mNames[index]), null);
+                        value, mNames[index]),
+                null, null);
 
         return defValue;
     }
diff --git a/bridge/src/android/content/res/Resources_Delegate.java b/bridge/src/android/content/res/Resources_Delegate.java
index f127992..a5216bc 100644
--- a/bridge/src/android/content/res/Resources_Delegate.java
+++ b/bridge/src/android/content/res/Resources_Delegate.java
@@ -51,9 +51,9 @@
 import android.annotation.Nullable;
 import android.content.res.Resources.NotFoundException;
 import android.content.res.Resources.Theme;
-import android.graphics.Color;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.DrawableInflater_Delegate;
 import android.icu.text.PluralRules;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
@@ -121,6 +121,7 @@
         sDrawableCache.evictAll();
         sContexts.clear();
         sLayoutlibCallbacks.clear();
+        DrawableInflater_Delegate.clearConstructorCache();
         Resources.mSystem = null;
     }
 
@@ -225,7 +226,7 @@
                 } else {
                     message = e.getMessage();
                 }
-                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, message, e, null);
+                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, message, e, null, null);
                 return 0;
             }
         }
@@ -366,23 +367,23 @@
                     try {
                         if (element.startsWith("#")) {
                             // This integer represents a color (starts with #).
-                            values[i] = Color.parseColor(element);
+                            values[i] = ResourceHelper.getColor(element);
                         } else {
                             values[i] = getInt(element);
                         }
                     } catch (NumberFormatException e) {
                         Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
                                 "Integer resource array contains non-integer value: \"" + element +
-                                        "\"", null);
+                                        "\"", null, null);
                     } catch (IllegalArgumentException e) {
                         Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
                                 "Integer resource array contains wrong color format: \"" + element +
-                                        "\"", null);
+                                        "\"", null, null);
                     }
                 } else {
                     Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
                             "Integer resource array contains non-integer value: \"" +
-                                    resValue.getElement(i) + "\"", null);
+                                    resValue.getElement(i) + "\"", null, null);
                 }
             }
             return values;
@@ -396,13 +397,13 @@
             } catch (NumberFormatException e) {
                 Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
                         "Integer resource array contains non-integer value: \"" + firstValue + "\"",
-                        null);
+                        null, null);
                 return new int[1];
             }
         } else {
             Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
                     "Integer resource array contains non-integer value: \"" +
-                            rv.getValue() + "\"", null);
+                            rv.getValue() + "\"", null, null);
             return new int[1];
         }
     }
@@ -435,13 +436,13 @@
                             String.format(
                                     "Resource with id 0x%1$X is not an array resource, but %2$s",
                                     id, type == null ? "null" : type.getDisplayName()),
-                            null);
+                            null, null);
                     return null;
                 }
                 if (!(resValue instanceof ArrayResourceValue)) {
                     Bridge.getLog().warning(LayoutLog.TAG_UNSUPPORTED,
                             "Obtaining resource arrays via getTextArray, getStringArray or getIntArray is not fully supported in this version of the IDE.",
-                            null);
+                            null, null);
                 }
                 return resValue;
             }
@@ -488,7 +489,7 @@
                 }
             } catch (XmlPullParserException e) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                        "Failed to parse " + value.getValue(), e, null /*data*/);
+                        "Failed to parse " + value.getValue(), e, null, null /*data*/);
                 // we'll return null below.
             }
         }
@@ -511,7 +512,7 @@
                 return ResourceHelper.getXmlBlockParser(getContext(resources), value);
             } catch (XmlPullParserException e) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                        "Failed to parse " + value.getValue(), e, null /*data*/);
+                        "Failed to parse " + value.getValue(), e, null, null /*data*/);
                 // we'll return null below.
             }
         }
@@ -946,7 +947,7 @@
                 return ResourceHelper.getXmlBlockParser(getContext(resources), value);
             } catch (XmlPullParserException e) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                        "Failed to parse " + value.getValue(), e, null /*data*/);
+                        "Failed to parse " + value.getValue(), e, null, null /*data*/);
                 // we'll return null below.
             }
         }
diff --git a/bridge/src/android/graphics/BaseCanvas_Delegate.java b/bridge/src/android/graphics/BaseCanvas_Delegate.java
index a7e36f0..df28305 100644
--- a/bridge/src/android/graphics/BaseCanvas_Delegate.java
+++ b/bridge/src/android/graphics/BaseCanvas_Delegate.java
@@ -168,7 +168,7 @@
     /*package*/ static void nDrawPaint(long nativeCanvas, long paint) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawPaint is not supported.", null, null /*data*/);
+                "Canvas.drawPaint is not supported.", null,null, null /*data*/);
     }
 
     @LayoutlibDelegate
@@ -421,7 +421,7 @@
             long nativePaint) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Some canvas paths may not be drawn", null, null);
+                "Some canvas paths may not be drawn", null, null, null);
     }
 
     @LayoutlibDelegate
@@ -513,7 +513,7 @@
             int colorOffset, long nPaint) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawBitmapMesh is not supported.", null, null /*data*/);
+                "Canvas.drawBitmapMesh is not supported.", null, null, null /*data*/);
     }
 
     @LayoutlibDelegate
@@ -525,7 +525,7 @@
             int indexCount, long nPaint) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawVertices is not supported.", null, null /*data*/);
+                "Canvas.drawVertices is not supported.", null, null, null /*data*/);
     }
 
     @LayoutlibDelegate
@@ -574,7 +574,7 @@
             long paint) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
+                "Canvas.drawTextOnPath is not supported.", null, null, null /*data*/);
     }
 
     @LayoutlibDelegate
@@ -585,7 +585,7 @@
             int bidiFlags, long paint) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
+                "Canvas.drawTextOnPath is not supported.", null, null, null /*data*/);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/bridge/src/android/graphics/BidiRenderer.java b/bridge/src/android/graphics/BidiRenderer.java
index 204022d..1de2a45 100644
--- a/bridge/src/android/graphics/BidiRenderer.java
+++ b/bridge/src/android/graphics/BidiRenderer.java
@@ -223,7 +223,8 @@
 
     private static void logFontWarning() {
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
-                "Some fonts could not be loaded. The rendering may not be perfect.", null, null);
+                "Some fonts could not be loaded. The rendering may not be perfect.", null, null,
+                null);
     }
 
     /**
diff --git a/bridge/src/android/graphics/BitmapFactory_Delegate.java b/bridge/src/android/graphics/BitmapFactory_Delegate.java
index 80c6761..b344451 100644
--- a/bridge/src/android/graphics/BitmapFactory_Delegate.java
+++ b/bridge/src/android/graphics/BitmapFactory_Delegate.java
@@ -93,7 +93,7 @@
                 bm = Bitmap_Delegate.createBitmap(is, bitmapCreateFlags, density);
             }
         } catch (IOException e) {
-            Bridge.getLog().error(null, "Failed to load image", e, null);
+            Bridge.getLog().error(null, "Failed to load image", e, null, null);
         }
 
         return bm;
diff --git a/bridge/src/android/graphics/BitmapShader_Delegate.java b/bridge/src/android/graphics/BitmapShader_Delegate.java
index 0e3e9a8..53b1db6 100644
--- a/bridge/src/android/graphics/BitmapShader_Delegate.java
+++ b/bridge/src/android/graphics/BitmapShader_Delegate.java
@@ -119,7 +119,7 @@
                 canvasMatrix = xform.createInverse();
             } catch (NoninvertibleTransformException e) {
                 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
-                        "Unable to inverse matrix in BitmapShader", e, null /*data*/);
+                        "Unable to inverse matrix in BitmapShader", e, null, null /*data*/);
                 canvasMatrix = new AffineTransform();
             }
 
@@ -128,7 +128,7 @@
                 localMatrix = localMatrix.createInverse();
             } catch (NoninvertibleTransformException e) {
                 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
-                        "Unable to inverse matrix in BitmapShader", e, null /*data*/);
+                        "Unable to inverse matrix in BitmapShader", e, null, null /*data*/);
                 localMatrix = new AffineTransform();
             }
 
diff --git a/bridge/src/android/graphics/Bitmap_Delegate.java b/bridge/src/android/graphics/Bitmap_Delegate.java
index c3d9665..7fc952d 100644
--- a/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -300,14 +300,14 @@
     /*package*/ static void nativeReconfigure(long nativeBitmap, int width, int height,
             int config, boolean isPremultiplied) {
         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
-                "Bitmap.reconfigure() is not supported", null /*data*/);
+                "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(LayoutLog.TAG_UNSUPPORTED,
-                "Bitmap.compress() is not supported", null /*data*/);
+                "Bitmap.compress() is not supported", null, null /*data*/);
         return true;
     }
 
@@ -427,14 +427,14 @@
     /*package*/ static void nativeCopyPixelsToBuffer(long nativeBitmap, Buffer dst) {
         // FIXME implement native delegate
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Bitmap.copyPixelsToBuffer is not supported.", null, null /*data*/);
+                "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(LayoutLog.TAG_UNSUPPORTED,
-                "Bitmap.copyPixelsFromBuffer is not supported.", null, null /*data*/);
+                "Bitmap.copyPixelsFromBuffer is not supported.", null, null, null /*data*/);
     }
 
     @LayoutlibDelegate
@@ -453,18 +453,17 @@
         // used during aidl call so really this should not be called.
         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
                 "AIDL is not suppored, and therefore Bitmaps cannot be created from parcels.",
-                null /*data*/);
+                null, null /*data*/);
         return null;
     }
 
     @LayoutlibDelegate
-    /*package*/ static boolean nativeWriteToParcel(long nativeBitmap, boolean isMutable,
-            int density, Parcel p) {
+    /*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(LayoutLog.TAG_UNSUPPORTED,
                 "AIDL is not suppored, and therefore Bitmaps cannot be written to parcels.",
-                null /*data*/);
+                null, null /*data*/);
         return false;
     }
 
@@ -482,7 +481,7 @@
         if (paint != null && paint.getMaskFilter() != null) {
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
                     "MaskFilter not supported in Bitmap.extractAlpha",
-                    null, null /*data*/);
+                    null, null, null /*data*/);
         }
 
         int alpha = paint != null ? paint.getAlpha() : 0xFF;
@@ -639,27 +638,27 @@
     @LayoutlibDelegate
     /*package*/ static boolean nativeIsSRGB(long nativeBitmap) {
         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
-                "Color spaces are not supported", null /*data*/);
+                "Color spaces are not supported", null, null /*data*/);
         return false;
     }
 
     @LayoutlibDelegate
     /*package*/ static ColorSpace nativeComputeColorSpace(long nativePtr) {
         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
-                "Color spaces are not supported", null /*data*/);
+                "Color spaces are not supported", null, null /*data*/);
         return null;
     }
 
     @LayoutlibDelegate
     /*package*/ static void nativeSetColorSpace(long nativePtr, long nativeColorSpace) {
         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
-                "Color spaces are not supported", null /*data*/);
+                "Color spaces are not supported", null, null /*data*/);
     }
 
     @LayoutlibDelegate
     /*package*/ static boolean nativeIsSRGBLinear(long nativePtr) {
         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
-                "Color spaces are not supported", null /*data*/);
+                "Color spaces are not supported", null, null /*data*/);
         return false;
     }
 
@@ -681,6 +680,13 @@
         return !bmpDelegate.mIsMutable;
     }
 
+    @LayoutlibDelegate
+    /*package*/ static HardwareBuffer nativeGetHardwareBuffer(long nativeBitmap) {
+        Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
+                "HardwareBuffer is not supported", null, null /*data*/);
+        return null;
+    }
+
     // ---- Private delegate/helper methods ----
 
     private Bitmap_Delegate(BufferedImage image, Config config) {
diff --git a/bridge/src/android/graphics/Canvas_Delegate.java b/bridge/src/android/graphics/Canvas_Delegate.java
index 2d18d7f..afcb40c 100644
--- a/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/bridge/src/android/graphics/Canvas_Delegate.java
@@ -204,6 +204,12 @@
     }
 
     @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.
@@ -350,7 +356,7 @@
             assert false;
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_AFFINE,
                     "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " +
-                    "supports affine transformations.", null, null /*data*/);
+                    "supports affine transformations.", null, null, null /*data*/);
         }
     }
 
@@ -396,7 +402,7 @@
 
         if (canvasDelegate.mDrawFilter != null && !canvasDelegate.mDrawFilter.isSupported()) {
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_DRAWFILTER,
-                    canvasDelegate.mDrawFilter.getSupportMessage(), null, null /*data*/);
+                    canvasDelegate.mDrawFilter.getSupportMessage(), null, null, null /*data*/);
         }
     }
 
diff --git a/bridge/src/android/graphics/FontFamily_Delegate.java b/bridge/src/android/graphics/FontFamily_Delegate.java
index db2cd1b..afcf9bd 100644
--- a/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -16,7 +16,6 @@
 
 package android.graphics;
 
-import com.android.ide.common.rendering.api.AssetRepository;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
@@ -24,16 +23,12 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.res.AssetManager;
-import android.content.res.BridgeAssetManager;
 import android.graphics.fonts.FontVariationAxis;
 
 import java.awt.Font;
 import java.awt.FontFormatException;
 import java.io.File;
 import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -47,7 +42,6 @@
 import java.util.logging.Logger;
 
 import libcore.util.NativeAllocationRegistry_Delegate;
-import sun.font.FontUtilities;
 
 import static android.graphics.Typeface.RESOLVE_BY_FONT_TABLE;
 import static android.graphics.Typeface_Delegate.SYSTEM_FONTS;
@@ -191,7 +185,7 @@
         } catch (FileNotFoundException e) {
             Bridge.getLog().error(LayoutLog.TAG_BROKEN,
                     "Unable to load the list of fonts. Try re-installing the SDK Platform from the SDK Manager.",
-                    e, null);
+                    e, null, null);
         } finally {
             if (scanner != null) {
                 scanner.close();
@@ -276,12 +270,12 @@
                 }
                 Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
                         String.format("Unable to load font %1$s", relativePath),
-                        e, null);
+                        e, null, null);
             }
         } else {
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                     "Only platform fonts located in " + SYSTEM_FONTS + "can be loaded.",
-                    null, null);
+                    null, null, null);
         }
 
         return null;
@@ -371,94 +365,6 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static boolean nAddFontFromAssetManager(long builderPtr, AssetManager mgr, String path,
-            int cookie, boolean isAsset, int ttcIndex, int weight, int isItalic) {
-        FontFamily_Delegate ffd = sManager.getDelegate(builderPtr);
-        if (ffd == null) {
-            return false;
-        }
-        ffd.mValid = true;
-        if (mgr == null) {
-            return false;
-        }
-        if (mgr instanceof BridgeAssetManager) {
-            InputStream fontStream = null;
-            try {
-                AssetRepository assetRepository = ((BridgeAssetManager) mgr).getAssetRepository();
-                if (assetRepository == null) {
-                    Bridge.getLog().error(LayoutLog.TAG_MISSING_ASSET, "Asset not found: " + path,
-                            null);
-                    return false;
-                }
-                if (!assetRepository.isSupported()) {
-                    // Don't log any warnings on unsupported IDEs.
-                    return false;
-                }
-                // Check cache
-                FontInfo fontInfo = sCache.get(path);
-                if (fontInfo != null) {
-                    // renew the font's lease.
-                    sCache.put(path, fontInfo);
-                    ffd.addFont(fontInfo);
-                    return true;
-                }
-                fontStream = isAsset ?
-                        assetRepository.openAsset(path, AssetManager.ACCESS_STREAMING) :
-                        assetRepository.openNonAsset(cookie, path, AssetManager.ACCESS_STREAMING);
-                if (fontStream == null) {
-                    Bridge.getLog().error(LayoutLog.TAG_MISSING_ASSET, "Asset not found: " + path,
-                            path);
-                    return false;
-                }
-                Font font = Font.createFont(Font.TRUETYPE_FONT, fontStream);
-                fontInfo = new FontInfo();
-                fontInfo.mFont = font;
-                if (weight == RESOLVE_BY_FONT_TABLE) {
-                    fontInfo.mWeight = FontUtilities.getFont2D(font).getWeight();
-                } else {
-                    fontInfo.mWeight = weight;
-                }
-                if (isItalic == RESOLVE_BY_FONT_TABLE) {
-                    fontInfo.mIsItalic =
-                            (FontUtilities.getFont2D(font).getStyle() & Font.ITALIC) != 0;
-                } else {
-                    fontInfo.mIsItalic = isItalic == 1;
-                }
-                ffd.addFont(fontInfo);
-                return true;
-            } catch (IOException e) {
-                Bridge.getLog().error(LayoutLog.TAG_MISSING_ASSET, "Unable to load font " + path, e,
-                        path);
-            } catch (FontFormatException e) {
-                if (path.endsWith(EXTENSION_OTF)) {
-                    // otf fonts are not supported on the user's config (JRE version + OS)
-                    Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                            "OpenType fonts are not supported yet: " + path, null, path);
-                } else {
-                    Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                            "Unable to load font " + path, e, path);
-                }
-            } finally {
-                if (fontStream != null) {
-                    try {
-                        fontStream.close();
-                    } catch (IOException ignored) {
-                    }
-                }
-            }
-            return false;
-        }
-        // This should never happen. AssetManager is a final class (from user's perspective), and
-        // we've replaced every creation of AssetManager with our implementation. We create an
-        // exception and log it, but continue with rest of the rendering, without loading this font.
-        Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                "You have found a bug in the rendering library. Please file a bug at b.android.com.",
-                new RuntimeException("Asset Manager is not an instance of BridgeAssetManager"),
-                null);
-        return false;
-    }
-
-    @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.
diff --git a/bridge/src/android/graphics/LinearGradient_Delegate.java b/bridge/src/android/graphics/LinearGradient_Delegate.java
index 8dcf374..7b544fe 100644
--- a/bridge/src/android/graphics/LinearGradient_Delegate.java
+++ b/bridge/src/android/graphics/LinearGradient_Delegate.java
@@ -127,7 +127,7 @@
                 canvasMatrix = xform.createInverse();
             } catch (java.awt.geom.NoninvertibleTransformException e) {
                 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
-                        "Unable to inverse matrix in LinearGradient", e, null /*data*/);
+                        "Unable to inverse matrix in LinearGradient", e, null, null /*data*/);
                 canvasMatrix = new java.awt.geom.AffineTransform();
             }
 
@@ -136,7 +136,7 @@
                 localMatrix = localMatrix.createInverse();
             } catch (java.awt.geom.NoninvertibleTransformException e) {
                 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
-                        "Unable to inverse matrix in LinearGradient", e, null /*data*/);
+                        "Unable to inverse matrix in LinearGradient", e, null, null /*data*/);
                 localMatrix = new java.awt.geom.AffineTransform();
             }
 
diff --git a/bridge/src/android/graphics/Matrix_Delegate.java b/bridge/src/android/graphics/Matrix_Delegate.java
index 64d8864..ca3cc96 100644
--- a/bridge/src/android/graphics/Matrix_Delegate.java
+++ b/bridge/src/android/graphics/Matrix_Delegate.java
@@ -600,7 +600,7 @@
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                 "Matrix.setPolyToPoly is not supported.",
-                null, null /*data*/);
+                null, null, null /*data*/);
         return false;
     }
 
diff --git a/bridge/src/android/graphics/NinePatch_Delegate.java b/bridge/src/android/graphics/NinePatch_Delegate.java
index a6cd51e..ca2a9ca 100644
--- a/bridge/src/android/graphics/NinePatch_Delegate.java
+++ b/bridge/src/android/graphics/NinePatch_Delegate.java
@@ -80,7 +80,8 @@
             oos = new ObjectOutputStream(baos);
             oos.writeObject(chunk);
         } catch (IOException e) {
-            Bridge.getLog().error(null, "Failed to serialize NinePatchChunk.", e, null /*data*/);
+            Bridge.getLog().error(null, "Failed to serialize NinePatchChunk.", e, null,
+                    null /*data*/);
             return null;
         } finally {
             if (oos != null) {
@@ -120,11 +121,11 @@
                 }
             } catch (IOException e) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                        "Failed to deserialize NinePatchChunk content.", e, null /*data*/);
+                        "Failed to deserialize NinePatchChunk content.", e, null, null /*data*/);
                 return null;
             } catch (ClassNotFoundException e) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                        "Failed to deserialize NinePatchChunk class.", e, null /*data*/);
+                        "Failed to deserialize NinePatchChunk class.", e, null, null /*data*/);
                 return null;
             }
         }
diff --git a/bridge/src/android/graphics/Paint_Delegate.java b/bridge/src/android/graphics/Paint_Delegate.java
index df22d63..0512edf 100644
--- a/bridge/src/android/graphics/Paint_Delegate.java
+++ b/bridge/src/android/graphics/Paint_Delegate.java
@@ -230,7 +230,7 @@
             } else {
                 Bridge.getLog().fidelityWarning(LayoutLog.TAG_PATHEFFECT,
                         mPathEffect.getSupportMessage(),
-                        null, null /*data*/);
+                        null, null, null /*data*/);
             }
         }
 
@@ -456,14 +456,14 @@
             float radius, float dx, float dy, long colorSpaceHandle,
             long shadowColor) {
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Paint.setShadowLayer is not supported.", null, null /*data*/);
+                "Paint.setShadowLayer is not supported.", null, null, null /*data*/);
     }
 
     @LayoutlibDelegate
     /*package*/ static boolean nHasShadowLayer(long paint) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Paint.hasShadowLayer is not supported.", null, null /*data*/);
+                "Paint.hasShadowLayer is not supported.", null, null, null /*data*/);
         return false;
     }
 
@@ -848,7 +848,7 @@
         // Log warning if it's not supported.
         if (delegate.mColorFilter != null && !delegate.mColorFilter.isSupported()) {
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_COLORFILTER,
-                    delegate.mColorFilter.getSupportMessage(), null, null /*data*/);
+                    delegate.mColorFilter.getSupportMessage(), null, null, null /*data*/);
         }
 
         return filter;
@@ -889,7 +889,7 @@
         // since none of those are supported, display a fidelity warning right away
         if (delegate.mMaskFilter != null && !delegate.mMaskFilter.isSupported()) {
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
-                    delegate.mMaskFilter.getSupportMessage(), null, null /*data*/);
+                    delegate.mMaskFilter.getSupportMessage(), null, null, null /*data*/);
         }
 
         return maskfilter;
@@ -985,7 +985,7 @@
             int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Paint.getTextRunCursor is not supported.", null, null /*data*/);
+                "Paint.getTextRunCursor is not supported.", null, null, null /*data*/);
         return 0;
     }
 
@@ -994,7 +994,7 @@
             int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Paint.getTextRunCursor is not supported.", null, null /*data*/);
+                "Paint.getTextRunCursor is not supported.", null, null, null /*data*/);
         return 0;
     }
 
@@ -1003,7 +1003,7 @@
             int index, int count, float x, float y, long path) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Paint.getTextPath is not supported.", null, null /*data*/);
+                "Paint.getTextPath is not supported.", null, null, null /*data*/);
     }
 
     @LayoutlibDelegate
@@ -1011,7 +1011,7 @@
             int end, float x, float y, long path) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Paint.getTextPath is not supported.", null, null /*data*/);
+                "Paint.getTextPath is not supported.", null, null, null /*data*/);
     }
 
     @LayoutlibDelegate
@@ -1057,7 +1057,7 @@
     @LayoutlibDelegate
     /*package*/ static void nSetLetterSpacing(long nativePaint, float letterSpacing) {
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING,
-                "Paint.setLetterSpacing() not supported.", null, null);
+                "Paint.setLetterSpacing() not supported.", null, null, null);
         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
         if (delegate == null) {
             return;
@@ -1086,7 +1086,7 @@
     @LayoutlibDelegate
     /*package*/ static void nSetFontFeatureSettings(long nativePaint, String settings) {
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING,
-                "Paint.setFontFeatureSettings() not supported.", null, null);
+                "Paint.setFontFeatureSettings() not supported.", null, null, null);
     }
 
     @LayoutlibDelegate
@@ -1136,7 +1136,7 @@
         }
         if (string.length() > 1) {
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING,
-                    "Paint.hasGlyph() is not supported for ligatures.", null, null);
+                    "Paint.hasGlyph() is not supported for ligatures.", null, null, null);
             return false;
         }
 
diff --git a/bridge/src/android/graphics/PathMeasure_Delegate.java b/bridge/src/android/graphics/PathMeasure_Delegate.java
index 7f707c9..7276c45 100644
--- a/bridge/src/android/graphics/PathMeasure_Delegate.java
+++ b/bridge/src/android/graphics/PathMeasure_Delegate.java
@@ -80,7 +80,7 @@
     /*package*/ static boolean native_getPosTan(long native_instance, float distance, float pos[],
             float tan[]) {
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "PathMeasure.getPostTan is not supported.", null, null);
+                "PathMeasure.getPostTan is not supported.", null, null, null);
         return false;
     }
 
@@ -88,14 +88,14 @@
     /*package*/ static boolean native_getMatrix(long native_instance, float distance, long
             native_matrix, int flags) {
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "PathMeasure.getMatrix is not supported.", null, null);
+                "PathMeasure.getMatrix is not supported.", null, null, null);
         return false;
     }
 
     @LayoutlibDelegate
     /*package*/ static boolean native_nextContour(long native_instance) {
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "PathMeasure.nextContour is not supported.", null, null);
+                "PathMeasure.nextContour is not supported.", null, null, null);
         return false;
     }
 
diff --git a/bridge/src/android/graphics/Path_Delegate.java b/bridge/src/android/graphics/Path_Delegate.java
index 2f86026..df83d65 100644
--- a/bridge/src/android/graphics/Path_Delegate.java
+++ b/bridge/src/android/graphics/Path_Delegate.java
@@ -158,7 +158,7 @@
     @LayoutlibDelegate
     /*package*/ static boolean nIsConvex(long nPath) {
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Path.isConvex is not supported.", null, null);
+                "Path.isConvex is not supported.", null, null, null);
         return true;
     }
 
@@ -475,7 +475,7 @@
 
     @LayoutlibDelegate
     /*package*/ static boolean nOp(long nPath1, long nPath2, int op, long result) {
-        Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, "Path.op() not supported", null);
+        Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, "Path.op() not supported", null, null);
         return false;
     }
 
@@ -891,7 +891,7 @@
             assert false;
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_AFFINE,
                     "android.graphics.Path#transform() only " +
-                    "supports affine transformations.", null, null /*data*/);
+                    "supports affine transformations.", null, null, null /*data*/);
         }
 
         GeneralPath newPath = new GeneralPath();
diff --git a/bridge/src/android/graphics/RadialGradient_Delegate.java b/bridge/src/android/graphics/RadialGradient_Delegate.java
index a42d654..cd46b31 100644
--- a/bridge/src/android/graphics/RadialGradient_Delegate.java
+++ b/bridge/src/android/graphics/RadialGradient_Delegate.java
@@ -117,7 +117,7 @@
                 canvasMatrix = xform.createInverse();
             } catch (java.awt.geom.NoninvertibleTransformException e) {
                 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
-                        "Unable to inverse matrix in RadialGradient", e, null /*data*/);
+                        "Unable to inverse matrix in RadialGradient", e, null, null /*data*/);
                 canvasMatrix = new java.awt.geom.AffineTransform();
             }
 
@@ -126,7 +126,7 @@
                 localMatrix = localMatrix.createInverse();
             } catch (java.awt.geom.NoninvertibleTransformException e) {
                 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
-                        "Unable to inverse matrix in RadialGradient", e, null /*data*/);
+                        "Unable to inverse matrix in RadialGradient", e, null, null /*data*/);
                 localMatrix = new java.awt.geom.AffineTransform();
             }
 
diff --git a/bridge/src/android/graphics/Region_Delegate.java b/bridge/src/android/graphics/Region_Delegate.java
index edb7025..f1f44b9 100644
--- a/bridge/src/android/graphics/Region_Delegate.java
+++ b/bridge/src/android/graphics/Region_Delegate.java
@@ -438,7 +438,7 @@
         // used during aidl call so really this should not be called.
         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
                 "AIDL is not suppored, and therefore Regions cannot be created from parcels.",
-                null /*data*/);
+                null, null /*data*/);
         return 0;
     }
 
@@ -449,7 +449,7 @@
         // be called.
         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
                 "AIDL is not suppored, and therefore Regions cannot be written to parcels.",
-                null /*data*/);
+                null, null /*data*/);
         return false;
     }
 
diff --git a/bridge/src/android/graphics/SweepGradient_Delegate.java b/bridge/src/android/graphics/SweepGradient_Delegate.java
index d29307a..e02144d 100644
--- a/bridge/src/android/graphics/SweepGradient_Delegate.java
+++ b/bridge/src/android/graphics/SweepGradient_Delegate.java
@@ -112,7 +112,7 @@
                 canvasMatrix = xform.createInverse();
             } catch (java.awt.geom.NoninvertibleTransformException e) {
                 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
-                        "Unable to inverse matrix in SweepGradient", e, null /*data*/);
+                        "Unable to inverse matrix in SweepGradient", e, null, null /*data*/);
                 canvasMatrix = new java.awt.geom.AffineTransform();
             }
 
@@ -121,7 +121,7 @@
                 localMatrix = localMatrix.createInverse();
             } catch (java.awt.geom.NoninvertibleTransformException e) {
                 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE,
-                        "Unable to inverse matrix in SweepGradient", e, null /*data*/);
+                        "Unable to inverse matrix in SweepGradient", e, null, null /*data*/);
                 localMatrix = new java.awt.geom.AffineTransform();
             }
 
diff --git a/bridge/src/android/graphics/Typeface_Delegate.java b/bridge/src/android/graphics/Typeface_Delegate.java
index c728513..fb044d9 100644
--- a/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/bridge/src/android/graphics/Typeface_Delegate.java
@@ -143,7 +143,7 @@
 
         if (newInstance != 0) {
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                    "nativeCreateFromTypefaceWithVariation is not supported", null, null);
+                    "nativeCreateFromTypefaceWithVariation is not supported", null, null, null);
         }
         return newInstance;
     }
@@ -257,7 +257,7 @@
             }
 
             if (parser != null) {
-                // TODO(namespaces): The aapt namespace should not matter for parsing font files?
+                // TODO(b/156609434): The aapt namespace should not matter for parsing font files?
                 BridgeXmlBlockParser blockParser =
                         new BridgeXmlBlockParser(
                                 parser, context, ResourceNamespace.fromBoolean(isFramework));
@@ -266,17 +266,18 @@
                             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 /*data*/);
+                    Bridge.getLog().error(null, "Failed to parse file " + path, e, null, null /*data
+                    */);
                 } finally {
                     blockParser.ensurePopped();
                 }
             } else {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
                         String.format("File %s does not exist (or is not a file)", path),
-                        null /*data*/);
+                        null, null /*data*/);
             }
         } else {
-            typeface = new Typeface.Builder(context.getAssets(), path).build();
+            typeface = new Typeface.Builder(context.getAssets(), path, false, 0).build();
         }
 
         return typeface;
diff --git a/bridge/src/com/android/internal/view/animation/NativeInterpolatorFactoryHelper_Delegate.java b/bridge/src/android/graphics/animation/NativeInterpolatorFactory_Delegate.java
similarity index 95%
rename from bridge/src/com/android/internal/view/animation/NativeInterpolatorFactoryHelper_Delegate.java
rename to bridge/src/android/graphics/animation/NativeInterpolatorFactory_Delegate.java
index da1ab27..38ad602 100644
--- a/bridge/src/com/android/internal/view/animation/NativeInterpolatorFactoryHelper_Delegate.java
+++ b/bridge/src/android/graphics/animation/NativeInterpolatorFactory_Delegate.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.internal.view.animation;
+package android.graphics.animation;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
@@ -36,13 +36,13 @@
 
 /**
  * Delegate used to provide new implementation of a select few methods of {@link
- * NativeInterpolatorFactoryHelper}
+ * NativeInterpolatorFactory}
  * <p>
- * Through the layoutlib_create tool, the original  methods of NativeInterpolatorFactoryHelper have
+ * 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 NativeInterpolatorFactoryHelper_Delegate {
+public class NativeInterpolatorFactory_Delegate {
     private static final DelegateManager<Interpolator> sManager = new DelegateManager<>
             (Interpolator.class);
 
diff --git a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
index ad2c564..be78091 100644
--- a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
+++ b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
@@ -17,7 +17,6 @@
 package android.graphics.drawable;
 
 import com.android.ide.common.rendering.api.LayoutLog;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper_Delegate;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
@@ -28,6 +27,7 @@
 import android.animation.PropertyValuesHolder;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.animation.NativeInterpolatorFactory_Delegate;
 import android.graphics.drawable.AnimatedVectorDrawable.VectorDrawableAnimatorRT;
 import android.graphics.drawable.VectorDrawable_Delegate.VFullPath_Delegate;
 import android.graphics.drawable.VectorDrawable_Delegate.VGroup_Delegate;
@@ -73,7 +73,7 @@
         ObjectAnimator animator = new ObjectAnimator();
         animator.setValues(holder.getValues());
         animator.setInterpolator(
-                NativeInterpolatorFactoryHelper_Delegate.getDelegate(nativeInterpolator));
+                NativeInterpolatorFactory_Delegate.getDelegate(nativeInterpolator));
         animator.setStartDelay(startDelay);
         animator.setDuration(duration);
         animator.setRepeatCount(repeatCount);
@@ -100,7 +100,7 @@
     /*package*/ static long nCreatePathDataPropertyHolder(long nativePtr, long startValuePtr,
             long endValuePtr) {
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, "AnimatedVectorDrawable path " +
-                "animations are not supported.", null, null);
+                "animations are not supported.", null, null, null);
         return 0;
     }
 
diff --git a/bridge/src/android/graphics/drawable/DrawableInflater_Delegate.java b/bridge/src/android/graphics/drawable/DrawableInflater_Delegate.java
new file mode 100644
index 0000000..5d2d03b
--- /dev/null
+++ b/bridge/src/android/graphics/drawable/DrawableInflater_Delegate.java
@@ -0,0 +1,76 @@
+/*
+ * 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.drawable;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.content.res.Resources_Delegate;
+import android.util.LruCache;
+import android.view.InflateException;
+
+import java.lang.reflect.Constructor;
+
+public class DrawableInflater_Delegate {
+    private static final LruCache<String, Constructor<? extends Drawable>> CONSTRUCTOR_MAP =
+            new LruCache<>(20);
+
+    /**
+     * This is identical to the original method except that it uses LayoutlibCallback to
+     * load the drawable class, which enables loading custom drawables from the project.
+     */
+    @LayoutlibDelegate
+    /* package */ static Drawable inflateFromClass(DrawableInflater thisInflater,
+            String className) {
+        try {
+            Constructor<? extends Drawable> constructor;
+            synchronized (CONSTRUCTOR_MAP) {
+                constructor = CONSTRUCTOR_MAP.get(className);
+                if (constructor == null) {
+                    final Class<? extends Drawable> clazz =
+                            Resources_Delegate.getLayoutlibCallback(thisInflater.mRes)
+                                    .findClass(className).asSubclass(Drawable.class);
+                    constructor = clazz.getConstructor();
+                    CONSTRUCTOR_MAP.put(className, constructor);
+                }
+            }
+            return constructor.newInstance();
+        } catch (NoSuchMethodException e) {
+            final InflateException ie = new InflateException("Error inflating class " + className);
+            ie.initCause(e);
+            throw ie;
+        } catch (ClassCastException e) {
+            // If loaded class is not a Drawable subclass.
+            final InflateException ie =
+                    new InflateException("Class is not a Drawable " + className);
+            ie.initCause(e);
+            throw ie;
+        } catch (ClassNotFoundException e) {
+            // If loadClass fails, we should propagate the exception.
+            final InflateException ie = new InflateException("Class not found " + className);
+            ie.initCause(e);
+            throw ie;
+        } catch (Exception e) {
+            final InflateException ie = new InflateException("Error inflating class " + className);
+            ie.initCause(e);
+            throw ie;
+        }
+    }
+
+    public static void clearConstructorCache() {
+        CONSTRUCTOR_MAP.evictAll();
+    }
+}
diff --git a/bridge/src/android/graphics/fonts/FontFamily_Builder_Delegate.java b/bridge/src/android/graphics/fonts/FontFamily_Builder_Delegate.java
index 06096ff..32f1157 100644
--- a/bridge/src/android/graphics/fonts/FontFamily_Builder_Delegate.java
+++ b/bridge/src/android/graphics/fonts/FontFamily_Builder_Delegate.java
@@ -29,6 +29,7 @@
 
 import java.awt.Font;
 import java.io.ByteArrayInputStream;
+import java.io.File;
 import java.nio.ByteBuffer;
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -77,10 +78,20 @@
 
     @LayoutlibDelegate
     /*package*/ static void nAddFont(long builderPtr, long fontPtr) {
-        FontFamily_Builder_Delegate builder = sBuilderManager.getDelegate(builderPtr);
-        Font_Builder_Delegate font = Font_Builder_Delegate.sBuilderManager.getDelegate(fontPtr);
-        if (builder != null && font != null) {
-            builder.addFont(font.mBuffer, font.mTtcIndex, font.mWeight, font.mItalic);
+        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);
         }
     }
 
@@ -159,16 +170,7 @@
 
     // ---- private helper methods ----
 
-    private void addFont(final ByteBuffer buffer, int ttcIndex, int weight, boolean italic) {
-        addFont(buffer, weight, italic);
-    }
-
-    private void addFont(@NonNull ByteBuffer buffer, int weight, boolean italic) {
-        // Set valid to true, even if the font fails to load.
-        Font font = loadFont(buffer);
-        if (font == null) {
-            return;
-        }
+    private void addFont(@NonNull Font font, int weight, boolean italic) {
         FontInfo fontInfo = new FontInfo();
         fontInfo.mFont = font;
         fontInfo.mWeight = weight;
@@ -180,15 +182,27 @@
         mFonts.putIfAbsent(fontInfo, fontInfo.mFont);
     }
 
-    private static Font loadFont(@NonNull ByteBuffer buffer) {
+    private static Font loadFontBuffer(@NonNull ByteBuffer buffer) {
         try {
             byte[] byteArray = new byte[buffer.limit()];
-            buffer.get(byteArray);
             buffer.rewind();
+            buffer.get(byteArray);
             return Font.createFont(Font.TRUETYPE_FONT, new ByteArrayInputStream(byteArray));
         } catch (Exception e) {
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN, "Unable to load font",
-                    e, null);
+                    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(LayoutLog.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 afa7dca..f58f027 100644
--- a/bridge/src/android/graphics/fonts/Font_Builder_Delegate.java
+++ b/bridge/src/android/graphics/fonts/Font_Builder_Delegate.java
@@ -24,10 +24,12 @@
 import android.annotation.NonNull;
 import android.content.res.AssetManager;
 
-import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 import java.nio.ByteBuffer;
-import java.nio.file.Files;
+import java.nio.ByteOrder;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
 
 import libcore.util.NativeAllocationRegistry_Delegate;
 
@@ -46,10 +48,7 @@
 public class Font_Builder_Delegate {
     protected static final DelegateManager<Font_Builder_Delegate> sBuilderManager =
             new DelegateManager<>(Font_Builder_Delegate.class);
-    private static final DelegateManager<String> sAssetManager =
-            new DelegateManager<>(String.class);
     private static long sFontFinalizer = -1;
-    private static long sAssetFinalizer = -1;
 
     protected ByteBuffer mBuffer;
     protected int mWeight;
@@ -63,36 +62,23 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static long nGetNativeAsset(
-            @NonNull AssetManager am, @NonNull String path, boolean isAsset, int cookie) {
-        return sAssetManager.addNewDelegate(path);
-    }
+    /*package*/ static ByteBuffer createBuffer(@NonNull AssetManager am, @NonNull String path,
+            boolean isAsset, int cookie) throws IOException {
+        try (InputStream assetStream = isAsset ? am.open(path, AssetManager.ACCESS_BUFFER)
+                : am.openNonAsset(cookie, path, AssetManager.ACCESS_BUFFER)) {
 
-    @LayoutlibDelegate
-    /*package*/ static ByteBuffer nGetAssetBuffer(long nativeAsset) {
-        String fullPath = sAssetManager.getDelegate(nativeAsset);
-        if (fullPath == null) {
-            return null;
-        }
-        try {
-            byte[] byteArray = Files.readAllBytes(new File(fullPath).toPath());
-            return ByteBuffer.wrap(byteArray);
-        } catch (IOException e) {
-            Bridge.getLog().error(LayoutLog.TAG_MISSING_ASSET,
-                    "Error mapping font file " + fullPath, null, null, null);
-            return null;
-        }
-    }
+            int capacity = assetStream.available();
+            ByteBuffer buffer = ByteBuffer.allocateDirect(capacity);
+            buffer.order(ByteOrder.nativeOrder());
+            ReadableByteChannel channel = Channels.newChannel(assetStream);
+            channel.read(buffer);
 
-    @LayoutlibDelegate
-    /*package*/ static long nGetReleaseNativeAssetFunc() {
-        synchronized (Font_Builder_Delegate.class) {
-            if (sAssetFinalizer == -1) {
-                sAssetFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
-                        sAssetManager::removeJavaReferenceFor);
+            if (assetStream.read() != -1) {
+                throw new IOException("Unable to access full contents of " + path);
             }
+
+            return buffer;
         }
-        return sAssetFinalizer;
     }
 
     @LayoutlibDelegate
diff --git a/bridge/src/android/graphics/fonts/SystemFonts_Delegate.java b/bridge/src/android/graphics/fonts/SystemFonts_Delegate.java
index 6937093..df976d0 100644
--- a/bridge/src/android/graphics/fonts/SystemFonts_Delegate.java
+++ b/bridge/src/android/graphics/fonts/SystemFonts_Delegate.java
@@ -49,7 +49,7 @@
             @NonNull ArrayMap<String, FontFamily[]> fallbackMap,
             @NonNull ArrayList<Font> availableFonts) {
         Bridge.sIsTypefaceInitialized = true;
-        return SystemFonts.buildSystemFallback_Original(getFontLocation() + "/fonts.xml",
+        return SystemFonts.buildSystemFallback_Original(getFontLocation() + "/standard/fonts.xml",
                 getFontLocation() + "/", oemCustomization, fallbackMap, availableFonts);
     }
 }
diff --git a/bridge/src/android/os/SystemProperties_Delegate.java b/bridge/src/android/os/SystemProperties_Delegate.java
index d299add..c6a7657 100644
--- a/bridge/src/android/os/SystemProperties_Delegate.java
+++ b/bridge/src/android/os/SystemProperties_Delegate.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
@@ -34,11 +35,6 @@
 public class SystemProperties_Delegate {
 
     @LayoutlibDelegate
-    /*package*/ static String native_get(String key) {
-        return native_get(key, "");
-    }
-
-    @LayoutlibDelegate
     /*package*/ static String native_get(String key, String def) {
         Map<String, String> properties = Bridge.getPlatformProperties();
         String value = properties.get(key);
@@ -107,4 +103,39 @@
     /*package*/ static void native_report_sysprop_change() {
         // pass.
     }
+
+    @LayoutlibDelegate
+    /*package*/ static String native_get(long handle) {
+        Bridge.getLog().fidelityWarning(LayoutLog.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(LayoutLog.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(LayoutLog.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(LayoutLog.TAG_UNSUPPORTED,
+                "Layoutlib does not support SystemProperties Handle", null, null, null);
+        return def;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long native_find(String name) {
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Layoutlib does not support SystemProperties Handle", null, null, null);
+        return 0;
+    }
 }
diff --git a/bridge/src/android/util/BridgeXmlPullAttributes.java b/bridge/src/android/util/BridgeXmlPullAttributes.java
index 2f4b0ca..8a78fa3 100644
--- a/bridge/src/android/util/BridgeXmlPullAttributes.java
+++ b/bridge/src/android/util/BridgeXmlPullAttributes.java
@@ -104,15 +104,11 @@
             return Bridge.getResourceId(ResourceType.ATTR, name);
         }
 
-        // this is not an attribute in the android namespace, we query the customviewloader, if
-        // the namespaces match.
-        if (mContext.getLayoutlibCallback().getNamespace().equals(ns)) {
-            // TODO(namespaces): cache the namespace objects.
-            ResourceNamespace namespace = ResourceNamespace.fromNamespaceUri(ns);
-            if (namespace != null) {
-                return mContext.getLayoutlibCallback().getOrGenerateResourceId(
-                        ResourceReference.attr(namespace, name));
-            }
+        // TODO(b/156609434): cache the namespace objects.
+        ResourceNamespace namespace = ResourceNamespace.fromNamespaceUri(ns);
+        if (namespace != null) {
+            return mContext.getLayoutlibCallback().getOrGenerateResourceId(
+                    ResourceReference.attr(namespace, name));
         }
 
         return 0;
diff --git a/bridge/src/android/util/LruCache.java b/bridge/src/android/util/LruCache.java
deleted file mode 100644
index 5208606..0000000
--- a/bridge/src/android/util/LruCache.java
+++ /dev/null
@@ -1,391 +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 java.util.LinkedHashMap;
-import java.util.Map;
-
-/**
- * BEGIN LAYOUTLIB CHANGE
- * This is a custom version that doesn't use the non standard LinkedHashMap#eldest.
- * END LAYOUTLIB CHANGE
- *
- * A cache that holds strong references to a limited number of values. Each time
- * a value is accessed, it is moved to the head of a queue. When a value is
- * added to a full cache, the value at the end of that queue is evicted and may
- * become eligible for garbage collection.
- *
- * <p>If your cached values hold resources that need to be explicitly released,
- * override {@link #entryRemoved}.
- *
- * <p>If a cache miss should be computed on demand for the corresponding keys,
- * override {@link #create}. This simplifies the calling code, allowing it to
- * assume a value will always be returned, even when there's a cache miss.
- *
- * <p>By default, the cache size is measured in the number of entries. Override
- * {@link #sizeOf} to size the cache in different units. For example, this cache
- * is limited to 4MiB of bitmaps:
- * <pre>   {@code
- *   int cacheSize = 4 * 1024 * 1024; // 4MiB
- *   LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) {
- *       protected int sizeOf(String key, Bitmap value) {
- *           return value.getByteCount();
- *       }
- *   }}</pre>
- *
- * <p>This class is thread-safe. Perform multiple cache operations atomically by
- * synchronizing on the cache: <pre>   {@code
- *   synchronized (cache) {
- *     if (cache.get(key) == null) {
- *         cache.put(key, value);
- *     }
- *   }}</pre>
- *
- * <p>This class does not allow null to be used as a key or value. A return
- * value of null from {@link #get}, {@link #put} or {@link #remove} is
- * unambiguous: the key was not in the cache.
- *
- * <p>This class appeared in Android 3.1 (Honeycomb MR1); it's available as part
- * of <a href="http://developer.android.com/sdk/compatibility-library.html">Android's
- * Support Package</a> for earlier releases.
- */
-public class LruCache<K, V> {
-    private final LinkedHashMap<K, V> map;
-
-    /** Size of this cache in units. Not necessarily the number of elements. */
-    private int size;
-    private int maxSize;
-
-    private int putCount;
-    private int createCount;
-    private int evictionCount;
-    private int hitCount;
-    private int missCount;
-
-    /**
-     * @param maxSize for caches that do not override {@link #sizeOf}, this is
-     *     the maximum number of entries in the cache. For all other caches,
-     *     this is the maximum sum of the sizes of the entries in this cache.
-     */
-    public LruCache(int maxSize) {
-        if (maxSize <= 0) {
-            throw new IllegalArgumentException("maxSize <= 0");
-        }
-        this.maxSize = maxSize;
-        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
-    }
-
-    /**
-     * Sets the size of the cache.
-     * @param maxSize The new maximum size.
-     *
-     * @hide
-     */
-    public void resize(int maxSize) {
-        if (maxSize <= 0) {
-            throw new IllegalArgumentException("maxSize <= 0");
-        }
-
-        synchronized (this) {
-            this.maxSize = maxSize;
-        }
-        trimToSize(maxSize);
-    }
-
-    /**
-     * Returns the value for {@code key} if it exists in the cache or can be
-     * created by {@code #create}. If a value was returned, it is moved to the
-     * head of the queue. This returns null if a value is not cached and cannot
-     * be created.
-     */
-    public final V get(K key) {
-        if (key == null) {
-            throw new NullPointerException("key == null");
-        }
-
-        V mapValue;
-        synchronized (this) {
-            mapValue = map.get(key);
-            if (mapValue != null) {
-                hitCount++;
-                return mapValue;
-            }
-            missCount++;
-        }
-
-        /*
-         * Attempt to create a value. This may take a long time, and the map
-         * may be different when create() returns. If a conflicting value was
-         * added to the map while create() was working, we leave that value in
-         * the map and release the created value.
-         */
-
-        V createdValue = create(key);
-        if (createdValue == null) {
-            return null;
-        }
-
-        synchronized (this) {
-            createCount++;
-            mapValue = map.put(key, createdValue);
-
-            if (mapValue != null) {
-                // There was a conflict so undo that last put
-                map.put(key, mapValue);
-            } else {
-                size += safeSizeOf(key, createdValue);
-            }
-        }
-
-        if (mapValue != null) {
-            entryRemoved(false, key, createdValue, mapValue);
-            return mapValue;
-        } else {
-            trimToSize(maxSize);
-            return createdValue;
-        }
-    }
-
-    /**
-     * Caches {@code value} for {@code key}. The value is moved to the head of
-     * the queue.
-     *
-     * @return the previous value mapped by {@code key}.
-     */
-    public final V put(K key, V value) {
-        if (key == null || value == null) {
-            throw new NullPointerException("key == null || value == null");
-        }
-
-        V previous;
-        synchronized (this) {
-            putCount++;
-            size += safeSizeOf(key, value);
-            previous = map.put(key, value);
-            if (previous != null) {
-                size -= safeSizeOf(key, previous);
-            }
-        }
-
-        if (previous != null) {
-            entryRemoved(false, key, previous, value);
-        }
-
-        trimToSize(maxSize);
-        return previous;
-    }
-
-    /**
-     * @param maxSize the maximum size of the cache before returning. May be -1
-     *     to evict even 0-sized elements.
-     */
-    private void trimToSize(int maxSize) {
-        while (true) {
-            K key;
-            V value;
-            synchronized (this) {
-                if (size < 0 || (map.isEmpty() && size != 0)) {
-                    throw new IllegalStateException(getClass().getName()
-                            + ".sizeOf() is reporting inconsistent results!");
-                }
-
-                if (size <= maxSize) {
-                    break;
-                }
-
-                // BEGIN LAYOUTLIB CHANGE
-                // get the last item in the linked list.
-                // This is not efficient, the goal here is to minimize the changes
-                // compared to the platform version.
-                Map.Entry<K, V> toEvict = null;
-                for (Map.Entry<K, V> entry : map.entrySet()) {
-                    toEvict = entry;
-                }
-                // END LAYOUTLIB CHANGE
-
-                if (toEvict == null) {
-                    break;
-                }
-
-                key = toEvict.getKey();
-                value = toEvict.getValue();
-                map.remove(key);
-                size -= safeSizeOf(key, value);
-                evictionCount++;
-            }
-
-            entryRemoved(true, key, value, null);
-        }
-    }
-
-    /**
-     * Removes the entry for {@code key} if it exists.
-     *
-     * @return the previous value mapped by {@code key}.
-     */
-    public final V remove(K key) {
-        if (key == null) {
-            throw new NullPointerException("key == null");
-        }
-
-        V previous;
-        synchronized (this) {
-            previous = map.remove(key);
-            if (previous != null) {
-                size -= safeSizeOf(key, previous);
-            }
-        }
-
-        if (previous != null) {
-            entryRemoved(false, key, previous, null);
-        }
-
-        return previous;
-    }
-
-    /**
-     * Called for entries that have been evicted or removed. This method is
-     * invoked when a value is evicted to make space, removed by a call to
-     * {@link #remove}, or replaced by a call to {@link #put}. The default
-     * implementation does nothing.
-     *
-     * <p>The method is called without synchronization: other threads may
-     * access the cache while this method is executing.
-     *
-     * @param evicted true if the entry is being removed to make space, false
-     *     if the removal was caused by a {@link #put} or {@link #remove}.
-     * @param newValue the new value for {@code key}, if it exists. If non-null,
-     *     this removal was caused by a {@link #put}. Otherwise it was caused by
-     *     an eviction or a {@link #remove}.
-     */
-    protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
-
-    /**
-     * Called after a cache miss to compute a value for the corresponding key.
-     * Returns the computed value or null if no value can be computed. The
-     * default implementation returns null.
-     *
-     * <p>The method is called without synchronization: other threads may
-     * access the cache while this method is executing.
-     *
-     * <p>If a value for {@code key} exists in the cache when this method
-     * returns, the created value will be released with {@link #entryRemoved}
-     * and discarded. This can occur when multiple threads request the same key
-     * at the same time (causing multiple values to be created), or when one
-     * thread calls {@link #put} while another is creating a value for the same
-     * key.
-     */
-    protected V create(K key) {
-        return null;
-    }
-
-    private int safeSizeOf(K key, V value) {
-        int result = sizeOf(key, value);
-        if (result < 0) {
-            throw new IllegalStateException("Negative size: " + key + "=" + value);
-        }
-        return result;
-    }
-
-    /**
-     * Returns the size of the entry for {@code key} and {@code value} in
-     * user-defined units.  The default implementation returns 1 so that size
-     * is the number of entries and max size is the maximum number of entries.
-     *
-     * <p>An entry's size must not change while it is in the cache.
-     */
-    protected int sizeOf(K key, V value) {
-        return 1;
-    }
-
-    /**
-     * Clear the cache, calling {@link #entryRemoved} on each removed entry.
-     */
-    public final void evictAll() {
-        trimToSize(-1); // -1 will evict 0-sized elements
-    }
-
-    /**
-     * For caches that do not override {@link #sizeOf}, this returns the number
-     * of entries in the cache. For all other caches, this returns the sum of
-     * the sizes of the entries in this cache.
-     */
-    public synchronized final int size() {
-        return size;
-    }
-
-    /**
-     * For caches that do not override {@link #sizeOf}, this returns the maximum
-     * number of entries in the cache. For all other caches, this returns the
-     * maximum sum of the sizes of the entries in this cache.
-     */
-    public synchronized final int maxSize() {
-        return maxSize;
-    }
-
-    /**
-     * Returns the number of times {@link #get} returned a value that was
-     * already present in the cache.
-     */
-    public synchronized final int hitCount() {
-        return hitCount;
-    }
-
-    /**
-     * Returns the number of times {@link #get} returned null or required a new
-     * value to be created.
-     */
-    public synchronized final int missCount() {
-        return missCount;
-    }
-
-    /**
-     * Returns the number of times {@link #create(Object)} returned a value.
-     */
-    public synchronized final int createCount() {
-        return createCount;
-    }
-
-    /**
-     * Returns the number of times {@link #put} was called.
-     */
-    public synchronized final int putCount() {
-        return putCount;
-    }
-
-    /**
-     * Returns the number of values that have been evicted.
-     */
-    public synchronized final int evictionCount() {
-        return evictionCount;
-    }
-
-    /**
-     * Returns a copy of the current contents of the cache, ordered from least
-     * recently accessed to most recently accessed.
-     */
-    public synchronized final Map<K, V> snapshot() {
-        return new LinkedHashMap<K, V>(map);
-    }
-
-    @Override public synchronized final String toString() {
-        int accesses = hitCount + missCount;
-        int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
-        return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
-                maxSize, hitCount, missCount, hitPercent);
-    }
-}
diff --git a/bridge/src/android/util/PathParser_Delegate.java b/bridge/src/android/util/PathParser_Delegate.java
index 4010b67..96c093c 100644
--- a/bridge/src/android/util/PathParser_Delegate.java
+++ b/bridge/src/android/util/PathParser_Delegate.java
@@ -123,7 +123,7 @@
         if (length != to.mPathDataNodes.length) {
             Bridge.getLog().error(LayoutLog.TAG_BROKEN,
                     "Cannot interpolate path data with different lengths (from " + length + " to " +
-                            to.mPathDataNodes.length + ").", null);
+                            to.mPathDataNodes.length + ").", null, null);
             return false;
         }
         if (out.mPathDataNodes.length != length) {
diff --git a/bridge/src/android/view/BridgeInflater.java b/bridge/src/android/view/BridgeInflater.java
index 0421f58..f2cbfa0 100644
--- a/bridge/src/android/view/BridgeInflater.java
+++ b/bridge/src/android/view/BridgeInflater.java
@@ -28,12 +28,10 @@
 import com.android.layoutlib.bridge.MockView;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
-import com.android.layoutlib.bridge.android.UnresolvedResourceValue;
 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.resources.ResourceType;
 import com.android.tools.layoutlib.annotations.NotNull;
 import com.android.tools.layoutlib.annotations.Nullable;
 
@@ -256,17 +254,18 @@
                         constructor.setAccessible(true);
                         Object inflater = constructor.newInstance();
                         Method method = getCreateViewMethod(inflaterClass);
-                        Context finalContext = context;
                         mCustomInflater = (viewName, attributeSet) -> {
                             try {
-                                return (View) method.invoke(inflater, null, viewName, finalContext,
+                                return (View) method.invoke(inflater, null, viewName,
+                                        mConstructorArgs[0],
                                         attributeSet,
                                         false,
                                         false /*readAndroidTheme*/, // No need after L
                                         true /*readAppTheme*/,
                                         true /*wrapContext*/);
                             } catch (IllegalAccessException | InvocationTargetException e) {
-                                assert false : "Call to createView failed";
+                                Bridge.getLog().error(LayoutLog.TAG_BROKEN, e.getMessage(), e,
+                                        null, null);
                             }
                             return null;
                         };
@@ -297,7 +296,7 @@
 
             if (name == null) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to inflate view tag without " +
-                  "class attribute", null);
+                  "class attribute", null, null);
                 // We weren't able to resolve the view so we just pass a mock View to be able to
                 // continue rendering.
                 view = new MockView(context, attrs);
@@ -324,7 +323,7 @@
                 // There is some unknown inflation exception in inflating a View that was found.
                 view = new MockView(context, attrs);
                 ((MockView) view).setText(name);
-                Bridge.getLog().error(LayoutLog.TAG_BROKEN, e.getMessage(), e, null);
+                Bridge.getLog().error(LayoutLog.TAG_BROKEN, e.getMessage(), e, null, null);
             } else {
                 final Object lastContext = mConstructorArgs[0];
                 mConstructorArgs[0] = context;
@@ -384,7 +383,7 @@
                     return inflate(bridgeParser, root);
                 } catch (Exception e) {
                     Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
-                            "Failed to parse file " + path, e, null);
+                            "Failed to parse file " + path, e, null, null);
 
                     return null;
                 }
diff --git a/bridge/src/android/view/LayoutInflater_Delegate.java b/bridge/src/android/view/LayoutInflater_Delegate.java
index 3f364f9..66a94fa 100644
--- a/bridge/src/android/view/LayoutInflater_Delegate.java
+++ b/bridge/src/android/view/LayoutInflater_Delegate.java
@@ -107,7 +107,7 @@
                 final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
                 if (value == null || value.length() <= 0) {
                     Bridge.getLog().error(LayoutLog.TAG_BROKEN, "You must specify a layout in the"
-                            + " include tag: <include layout=\"@layout/layoutID\" />", null);
+                            + " include tag: <include layout=\"@layout/layoutID\" />", null, null);
                     LayoutInflater.consumeChildElements(parser);
                     return;
                 }
@@ -130,10 +130,10 @@
                 final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
                 if (value == null) {
                     Bridge.getLog().error(LayoutLog.TAG_BROKEN, "You must specify a layout in the"
-                            + " include tag: <include layout=\"@layout/layoutID\" />", null);
+                            + " include tag: <include layout=\"@layout/layoutID\" />", null, null);
                 } else {
                     Bridge.getLog().error(LayoutLog.TAG_BROKEN, "You must specify a valid layout "
-                            + "reference. The layout ID " + value + " is not valid.", null);
+                            + "reference. The layout ID " + value + " is not valid.", null, null);
                 }
             } else {
                 final XmlResourceParser childParser =
@@ -150,7 +150,7 @@
                     if (type != XmlPullParser.START_TAG) {
                         Bridge.getLog().error(LayoutLog.TAG_BROKEN,
                                 childParser.getPositionDescription() + ": No start tag found!",
-                                null);
+                                null, null);
                         LayoutInflater.consumeChildElements(parser);
                         return;
                     }
@@ -228,7 +228,7 @@
         } else {
             Bridge.getLog().error(LayoutLog.TAG_BROKEN,
                     "<include /> can only be used inside of a ViewGroup",
-                    null);
+                    null, null);
         }
 
         LayoutInflater.consumeChildElements(parser);
diff --git a/bridge/src/android/view/MenuInflater_Delegate.java b/bridge/src/android/view/MenuInflater_Delegate.java
index d16d851..83586a1 100644
--- a/bridge/src/android/view/MenuInflater_Delegate.java
+++ b/bridge/src/android/view/MenuInflater_Delegate.java
@@ -65,7 +65,7 @@
             // We suppress this error for AppCompat menus since we do not support them in the menu
             // editor yet.
             Bridge.getLog().warning(LayoutLog.TAG_BROKEN,
-                    "Action Bar Menu rendering may be incorrect.", null);
+                    "Action Bar Menu rendering may be incorrect.", null, null);
         }
 
     }
diff --git a/bridge/src/android/view/ViewGroup_Delegate.java b/bridge/src/android/view/ViewGroup_Delegate.java
index a34ef22..e71af61 100644
--- a/bridge/src/android/view/ViewGroup_Delegate.java
+++ b/bridge/src/android/view/ViewGroup_Delegate.java
@@ -17,7 +17,6 @@
 package android.view;
 
 import com.android.layoutlib.bridge.android.BridgeContext;
-import com.android.layoutlib.bridge.android.RenderParamsFlags;
 import com.android.resources.Density;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
@@ -28,7 +27,6 @@
 import android.graphics.Outline;
 import android.graphics.Path_Delegate;
 import android.graphics.Rect;
-import android.graphics.Region.Op;
 import android.view.animation.Transformation;
 import android.view.shadow.HighQualityShadowPainter;
 
@@ -74,10 +72,8 @@
         float elevation = getElevation(child, parent);
         Context bridgeContext = parent.getContext();
         if (bridgeContext instanceof BridgeContext) {
-            highQualityShadow = ((BridgeContext) bridgeContext).getLayoutlibCallback()
-                    .getFlag(RenderParamsFlags.FLAG_RENDER_HIGH_QUALITY_SHADOW);
-            enableShadow = ((BridgeContext) bridgeContext).getLayoutlibCallback()
-                    .getFlag(RenderParamsFlags.FLAG_ENABLE_SHADOW);
+            highQualityShadow = ((BridgeContext) bridgeContext).isHighQualityShadows();
+            enableShadow = ((BridgeContext) bridgeContext).isShadowsEnabled();
         }
 
         if (!enableShadow) {
@@ -151,31 +147,19 @@
         }
         concatMatrix |= childHasIdentityMatrix;
 
-        child.computeScroll();
-        int sx = child.mScrollX;
-        int sy = child.mScrollY;
-
-        canvas.translate(child.mLeft - sx, child.mTop - sy);
+        canvas.translate(child.mLeft, child.mTop);
         float alpha = child.getAlpha() * child.getTransitionAlpha();
 
         if (transformToApply != null || alpha < 1 || !childHasIdentityMatrix) {
             if (transformToApply != null || !childHasIdentityMatrix) {
-                int transX = -sx;
-                int transY = -sy;
 
                 if (transformToApply != null) {
                     if (concatMatrix) {
-                        // Undo the scroll translation, apply the transformation matrix,
-                        // then redo the scroll translate to get the correct result.
-                        canvas.translate(-transX, -transY);
                         canvas.concat(transformToApply.getMatrix());
-                        canvas.translate(transX, transY);
                     }
                 }
                 if (!childHasIdentityMatrix) {
-                    canvas.translate(-transX, -transY);
                     canvas.concat(child.getMatrix());
-                    canvas.translate(transX, transY);
                 }
 
             }
diff --git a/bridge/src/android/view/View_Delegate.java b/bridge/src/android/view/View_Delegate.java
index 5d39e4c..9aae4ae 100644
--- a/bridge/src/android/view/View_Delegate.java
+++ b/bridge/src/android/view/View_Delegate.java
@@ -55,7 +55,7 @@
             // all the layout.
             thisView.draw_Original(canvas);
         } catch (Throwable t) {
-            Bridge.getLog().error(LayoutLog.TAG_BROKEN, "View draw failed", t, null);
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN, "View draw failed", t, null, null);
         }
     }
 
@@ -67,7 +67,7 @@
             // all the layout.
             return thisView.draw_Original(canvas, parent, drawingTime);
         } catch (Throwable t) {
-            Bridge.getLog().error(LayoutLog.TAG_BROKEN, "View draw failed", t, null);
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN, "View draw failed", t, null, null);
         }
         return false;
     }
@@ -79,7 +79,7 @@
             // all the layout.
             thisView.measure_Original(widthMeasureSpec, heightMeasureSpec);
         } catch (Throwable t) {
-            Bridge.getLog().error(LayoutLog.TAG_BROKEN, "View measure failed", t, null);
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN, "View measure failed", t, null, null);
         }
     }
 
@@ -90,7 +90,21 @@
             // all the layout.
             thisView.layout_Original(l, t, r, b);
         } catch (Throwable th) {
-            Bridge.getLog().error(LayoutLog.TAG_BROKEN, "View layout failed", th, null);
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN, "View layout failed", th, null, null);
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void dispatchDetachedFromWindow(View thisView) {
+        try {
+            // This code is run within a try/catch to prevent components from throwing user-visible
+            // exceptions when being disposed.
+            thisView.dispatchDetachedFromWindow_Original();
+        } catch (Throwable t) {
+            Context context = BridgeContext.getBaseContext(thisView.getContext());
+            if (context instanceof BridgeContext) {
+                ((BridgeContext) context).warn("Exception while detaching " + thisView.getClass(), t);
+            }
         }
     }
 }
diff --git a/bridge/src/android/view/accessibility/AccessibilityNodeIdManager.java b/bridge/src/android/view/accessibility/AccessibilityNodeIdManager.java
new file mode 100644
index 0000000..e377117
--- /dev/null
+++ b/bridge/src/android/view/accessibility/AccessibilityNodeIdManager.java
@@ -0,0 +1,61 @@
+/*
+ * 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.accessibility;
+
+import android.view.View;
+
+/**
+ * Class that replaces the original AccessibilityNodeIdManager with a no-op version. This avoids
+ * the accidental leaking of views referenced by the originalin layoutlib.
+ */
+public final class AccessibilityNodeIdManager {
+    private static AccessibilityNodeIdManager sIdManager = new AccessibilityNodeIdManager();
+
+
+    public static synchronized AccessibilityNodeIdManager getInstance() {
+        return sIdManager;
+    }
+
+    private AccessibilityNodeIdManager() {
+    }
+
+    /**
+     * Register view to be kept track of by the accessibility system.
+     * Must be paired with unregisterView, otherwise this will leak.
+     * @param view The view to be registered.
+     * @param id The accessibilityViewId of the view.
+     */
+    public void registerViewWithId(View view, int id) {
+    }
+
+    /**
+     * Unregister view, accessibility won't keep track of this view after this call.
+     * @param id The id returned from registerView when the view as first associated.
+     */
+    public void unregisterViewWithId(int id) {
+    }
+
+    /**
+     * Accessibility uses this to find the view in the hierarchy.
+     * @param id The accessibility view id.
+     * @return The view.
+     */
+    public View findView(int id) {
+       return null;
+    }
+}
+
diff --git a/bridge/src/android/view/math/Math3DHelper.java b/bridge/src/android/view/math/Math3DHelper.java
index 7359c4c..35a1ee1 100644
--- a/bridge/src/android/view/math/Math3DHelper.java
+++ b/bridge/src/android/view/math/Math3DHelper.java
@@ -18,92 +18,8 @@
 
 public class Math3DHelper {
 
-    private static final float EPSILON = 0.0000001f;
-
     private Math3DHelper() { }
 
-    /**
-     * Calculates [p1x+t*(p2x-p1x)=dx*t2+px,p1y+t*(p2y-p1y)=dy*t2+py],[t,t2];
-     *
-     * @param d - dimension in which the poly is represented (supports 2 or 3D)
-     * @return float[]{t2, t, p1} or float[]{Float.NaN}
-     */
-    public static float[] rayIntersectPoly(float[] poly, int polyLength, float px, float py,
-            float dx, float dy, int d) {
-        int p1 = polyLength - 1;
-        for (int p2 = 0; p2 < polyLength; p2++) {
-            float p1x = poly[p1 * d + 0];
-            float p1y = poly[p1 * d + 1];
-            float p2x = poly[p2 * d + 0];
-            float p2y = poly[p2 * d + 1];
-            float div = (dx * (p1y - p2y) + dy * (p2x - p1x));
-            if (div != 0) {
-                float t = (dx * (p1y - py) + dy * (px - p1x)) / div;
-                if (t >= 0 && t <= 1) {
-                    float t2 = (p1x * (py - p2y)
-                            + p2x * (p1y - py)
-                            + px * (p2y - p1y))
-                            / div;
-                    if (t2 > 0) {
-                        return new float[]{t2, t, p1};
-                    }
-                }
-            }
-            p1 = p2;
-        }
-        return new float[]{Float.NaN};
-    }
-
-    public static void centroid2d(float[] poly, int len, float[] ret) {
-        float sumx = 0;
-        float sumy = 0;
-        int p1 = len - 1;
-        float area = 0;
-        for (int p2 = 0; p2 < len; p2++) {
-            float x1 = poly[p1 * 2 + 0];
-            float y1 = poly[p1 * 2 + 1];
-            float x2 = poly[p2 * 2 + 0];
-            float y2 = poly[p2 * 2 + 1];
-            float a = (x1 * y2 - x2 * y1);
-            sumx += (x1 + x2) * a;
-            sumy += (y1 + y2) * a;
-            area += a;
-            p1 = p2;
-        }
-        float centroidx = sumx / (3 * area);
-        float centroidy = sumy / (3 * area);
-        ret[0] = centroidx;
-        ret[1] = centroidy;
-    }
-
-    public static void centroid3d(float[] poly, int len, float[] ret) {
-        int n = len - 1;
-        double area = 0;
-        double cx = 0;
-        double cy = 0;
-        double cz = 0;
-        for (int i = 1; i < n; i++) {
-            int k = i + 1;
-            float a0 = poly[i * 3 + 0] - poly[0 * 3 + 0];
-            float a1 = poly[i * 3 + 1] - poly[0 * 3 + 1];
-            float a2 = poly[i * 3 + 2] - poly[0 * 3 + 2];
-            float b0 = poly[k * 3 + 0] - poly[0 * 3 + 0];
-            float b1 = poly[k * 3 + 1] - poly[0 * 3 + 1];
-            float b2 = poly[k * 3 + 2] - poly[0 * 3 + 2];
-            float c0 = a1 * b2 - b1 * a2;
-            float c1 = a2 * b0 - b2 * a0;
-            float c2 = a0 * b1 - b0 * a1;
-            double areaOfTriangle = Math.sqrt(c0 * c0 + c1 * c1 + c2 * c2);
-            area += areaOfTriangle;
-            cx += areaOfTriangle * (poly[i * 3 + 0] + poly[k * 3 + 0] + poly[0 * 3 + 0]);
-            cy += areaOfTriangle * (poly[i * 3 + 1] + poly[k * 3 + 1] + poly[0 * 3 + 1]);
-            cz += areaOfTriangle * (poly[i * 3 + 2] + poly[k * 3 + 2] + poly[0 * 3 + 2]);
-        }
-        ret[0] = (float) (cx / (3 * area));
-        ret[1] = (float) (cy / (3 * area));
-        ret[2] = (float) (cz / (3 * area));
-    }
-
     public final static int min(int x1, int x2, int x3) {
         return (x1 > x2) ? ((x2 > x3) ? x3 : x2) : ((x1 > x3) ? x3 : x1);
     }
@@ -112,421 +28,6 @@
         return (x1 < x2) ? ((x2 < x3) ? x3 : x2) : ((x1 < x3) ? x3 : x1);
     }
 
-    private static void xsort(float[] points, int pointsLength) {
-        quicksortX(points, 0, pointsLength - 1);
-    }
-
-    public static int hull(float[] points, int pointsLength, float[] retPoly) {
-        xsort(points, pointsLength);
-        int n = pointsLength;
-        float[] lUpper = new float[n * 2];
-        lUpper[0] = points[0];
-        lUpper[1] = points[1];
-        lUpper[2] = points[2];
-        lUpper[3] = points[3];
-
-        int lUpperSize = 2;
-
-        for (int i = 2; i < n; i++) {
-            lUpper[lUpperSize * 2 + 0] = points[i * 2 + 0];
-            lUpper[lUpperSize * 2 + 1] = points[i * 2 + 1];
-            lUpperSize++;
-
-            while (lUpperSize > 2 && !rightTurn(
-                    lUpper[(lUpperSize - 3) * 2], lUpper[(lUpperSize - 3) * 2 + 1],
-                    lUpper[(lUpperSize - 2) * 2], lUpper[(lUpperSize - 2) * 2 + 1],
-                    lUpper[(lUpperSize - 1) * 2], lUpper[(lUpperSize - 1) * 2 + 1])) {
-                // Remove the middle point of the three last
-                lUpper[(lUpperSize - 2) * 2 + 0] = lUpper[(lUpperSize - 1) * 2 + 0];
-                lUpper[(lUpperSize - 2) * 2 + 1] = lUpper[(lUpperSize - 1) * 2 + 1];
-                lUpperSize--;
-            }
-        }
-
-        float[] lLower = new float[n * 2];
-        lLower[0] = points[(n - 1) * 2 + 0];
-        lLower[1] = points[(n - 1) * 2 + 1];
-        lLower[2] = points[(n - 2) * 2 + 0];
-        lLower[3] = points[(n - 2) * 2 + 1];
-
-        int lLowerSize = 2;
-
-        for (int i = n - 3; i >= 0; i--) {
-            lLower[lLowerSize * 2 + 0] = points[i * 2 + 0];
-            lLower[lLowerSize * 2 + 1] = points[i * 2 + 1];
-            lLowerSize++;
-
-            while (lLowerSize > 2 && !rightTurn(
-                    lLower[(lLowerSize - 3) * 2], lLower[(lLowerSize - 3) * 2 + 1],
-                    lLower[(lLowerSize - 2) * 2], lLower[(lLowerSize - 2) * 2 + 1],
-                    lLower[(lLowerSize - 1) * 2], lLower[(lLowerSize - 1) * 2 + 1])) {
-                // Remove the middle point of the three last
-                lLower[(lLowerSize - 2) * 2 + 0] = lLower[(lLowerSize - 1) * 2 + 0];
-                lLower[(lLowerSize - 2) * 2 + 1] = lLower[(lLowerSize - 1) * 2 + 1];
-                lLowerSize--;
-            }
-        }
-        int count = 0;
-
-        for (int i = 0; i < lUpperSize; i++) {
-            retPoly[count * 2 + 0] = lUpper[i * 2 + 0];
-            retPoly[count * 2 + 1] = lUpper[i * 2 + 1];
-            count++;
-        }
-
-        for (int i = 1; i < lLowerSize - 1; i++) {
-            retPoly[count * 2 + 0] = lLower[i * 2 + 0];
-            retPoly[count * 2 + 1] = lLower[i * 2 + 1];
-            count++;
-        }
-
-        return count;
-    }
-
-    private static boolean rightTurn(float ax, float ay, float bx, float by, float cx, float cy) {
-        return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax) > 0.00001;
-    }
-
-    /**
-     * Calculates the intersection of poly1 with poly2 and puts in poly2
-     * @return number of point in poly2
-     */
-    public static int intersection(
-            float[] poly1, int poly1length, float[] poly2, int poly2length) {
-        makeClockwise(poly1, poly1length);
-        makeClockwise(poly2, poly2length);
-        float[] poly = new float[(poly1length * poly2length + 2) * 2];
-        int count = 0;
-        int pcount = 0;
-        for (int i = 0; i < poly1length; i++) {
-            if (pointInsidePolygon(poly1[i * 2], poly1[i * 2 + 1], poly2, poly2length)) {
-                poly[count * 2] = poly1[i * 2];
-                poly[count * 2 + 1] = poly1[i * 2 + 1];
-                count++;
-                pcount++;
-            }
-        }
-        int fromP1 = pcount;
-        for (int i = 0; i < poly2length; i++) {
-            if (pointInsidePolygon(poly2[i * 2], poly2[i * 2 + 1], poly1, poly1length)) {
-                poly[count * 2] = poly2[i * 2];
-                poly[count * 2 + 1] = poly2[i * 2 + 1];
-                count++;
-            }
-        }
-        int fromP2 = count - fromP1;
-        if (fromP1 == poly1length) { // use p1
-            for (int i = 0; i < poly1length; i++) {
-                poly2[i * 2] = poly1[i * 2];
-                poly2[i * 2 + 1] = poly1[i * 2 + 1];
-            }
-            return poly1length;
-        }
-        if (fromP2 == poly2length) { // use p2
-            return poly2length;
-        }
-        float[] intersection = new float[2];
-        for (int i = 0; i < poly2length; i++) {
-            for (int j = 0; j < poly1length; j++) {
-                int i1_by_2 = i * 2;
-                int i2_by_2 = ((i + 1) % poly2length) * 2;
-                int j1_by_2 = j * 2;
-                int j2_by_2 = ((j + 1) % poly1length) * 2;
-                boolean found = lineIntersection(
-                        poly2[i1_by_2], poly2[i1_by_2 + 1],
-                        poly2[i2_by_2], poly2[i2_by_2 + 1],
-                        poly1[j1_by_2], poly1[j1_by_2 + 1],
-                        poly1[j2_by_2], poly1[j2_by_2 + 1], intersection);
-                if (found) {
-                    poly[count * 2] = intersection[0];
-                    poly[count * 2 + 1] = intersection[1];
-                    count++;
-                } else {
-                    float dx = poly2[i * 2] - poly1[j * 2];
-                    float dy = poly2[i * 2 + 1] - poly1[j * 2 + 1];
-
-                    if (dx * dx + dy * dy < 0.01) {
-                        poly[count * 2] = poly2[i * 2];
-                        poly[count * 2 + 1] = poly2[i * 2 + 1];
-                        count++;
-                    }
-                }
-            }
-        }
-        if (count == 0) {
-            return 0;
-        }
-        float avgx = 0;
-        float avgy = 0;
-        for (int i = 0; i < count; i++) {
-            avgx += poly[i * 2];
-            avgy += poly[i * 2 + 1];
-        }
-        avgx /= count;
-        avgy /= count;
-
-        float[] ctr = new float[] { avgx, avgy };
-        sort(poly, count, ctr);
-        int size = count;
-
-        poly2[0] = poly[0];
-        poly2[1] = poly[1];
-
-        count = 1;
-        for (int i = 1; i < size; i++) {
-            float dx = poly[i * 2] - poly[(i - 1) * 2];
-            float dy = poly[i * 2 + 1] - poly[(i - 1) * 2 + 1];
-            if (dx * dx + dy * dy >= 0.01) {
-                poly2[count * 2] = poly[i * 2];
-                poly2[count * 2 + 1] = poly[i * 2 + 1];
-                count++;
-            }
-        }
-        return count;
-    }
-
-    public static void sort(float[] poly, int polyLength, float[] ctr) {
-        quicksortCirc(poly, 0, polyLength - 1, ctr);
-    }
-
-    public static float angle(float x1, float y1, float[] ctr) {
-        return -(float) Math.atan2(x1 - ctr[0], y1 - ctr[1]);
-    }
-
-    private static void swap(float[] points, int i, int j) {
-        float x = points[i * 2];
-        float y = points[i * 2 + 1];
-        points[i * 2] = points[j * 2];
-        points[i * 2 + 1] = points[j * 2 + 1];
-        points[j * 2] = x;
-        points[j * 2 + 1] = y;
-    }
-
-    private static void quicksortCirc(float[] points, int low, int high, float[] ctr) {
-        int i = low, j = high;
-        int p = low + (high - low) / 2;
-        float pivot = angle(points[p * 2], points[p * 2 + 1], ctr);
-        while (i <= j) {
-            while (angle(points[i * 2], points[i * 2 + 1], ctr) < pivot) {
-                i++;
-            }
-            while (angle(points[j * 2], points[j * 2 + 1], ctr) > pivot) {
-                j--;
-            }
-
-            if (i <= j) {
-                swap(points, i, j);
-                i++;
-                j--;
-            }
-        }
-        if (low < j) {
-            quicksortCirc(points, low, j, ctr);
-        }
-        if (i < high) {
-            quicksortCirc(points, i, high, ctr);
-        }
-    }
-
-    private static void quicksortX(float[] points, int low, int high) {
-        int i = low, j = high;
-        int p = low + (high - low) / 2;
-        float pivot = points[p * 2];
-        while (i <= j) {
-            while (points[i * 2] < pivot) {
-                i++;
-            }
-            while (points[j * 2] > pivot) {
-                j--;
-            }
-
-            if (i <= j) {
-                swap(points, i, j);
-                i++;
-                j--;
-            }
-        }
-        if (low < j) {
-            quicksortX(points, low, j);
-        }
-        if (i < high) {
-            quicksortX(points, i, high);
-        }
-    }
-
-    private static boolean pointInsidePolygon(float x, float y, float[] poly, int len) {
-        boolean c = false;
-        float testx = x;
-        float testy = y;
-        for (int i = 0, j = len - 1; i < len; j = i++) {
-            if (((poly[i * 2 + 1] > testy) != (poly[j * 2 + 1] > testy)) &&
-                    (testx < (poly[j * 2] - poly[i * 2]) * (testy - poly[i * 2 + 1])
-                            / (poly[j * 2 + 1] - poly[i * 2 + 1]) + poly[i * 2])) {
-                c = !c;
-            }
-        }
-        return c;
-    }
-
-    private static void makeClockwise(float[] polygon, int len) {
-        if (polygon == null || len == 0) {
-            return;
-        }
-        if (!isClockwise(polygon, len)) {
-            reverse(polygon, len);
-        }
-    }
-
-    private static boolean isClockwise(float[] polygon, int len) {
-        float sum = 0;
-        float p1x = polygon[(len - 1) * 2];
-        float p1y = polygon[(len - 1) * 2 + 1];
-        for (int i = 0; i < len; i++) {
-
-            float p2x = polygon[i * 2];
-            float p2y = polygon[i * 2 + 1];
-            sum += p1x * p2y - p2x * p1y;
-            p1x = p2x;
-            p1y = p2y;
-        }
-        return sum < 0;
-    }
-
-    private static void reverse(float[] polygon, int len) {
-        int n = len / 2;
-        for (int i = 0; i < n; i++) {
-            float tmp0 = polygon[i * 2];
-            float tmp1 = polygon[i * 2 + 1];
-            int k = len - 1 - i;
-            polygon[i * 2] = polygon[k * 2];
-            polygon[i * 2 + 1] = polygon[k * 2 + 1];
-            polygon[k * 2] = tmp0;
-            polygon[k * 2 + 1] = tmp1;
-        }
-    }
-
-    /**
-     * Intersects two lines in parametric form.
-     */
-    private static final boolean lineIntersection(
-            float x1, float y1,
-            float x2, float y2,
-            float x3, float y3,
-            float x4, float y4,
-            float[] ret) {
-
-        float d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
-        if (d == 0.000f) {
-            return false;
-        }
-
-        float dx = (x1 * y2 - y1 * x2);
-        float dy = (x3 * y4 - y3 * x4);
-        float x = (dx * (x3 - x4) - (x1 - x2) * dy) / d;
-        float y = (dx * (y3 - y4) - (y1 - y2) * dy) / d;
-
-        if (((x - x1) * (x - x2) > EPSILON)
-                || ((x - x3) * (x - x4) > EPSILON)
-                || ((y - y1) * (y - y2) > EPSILON)
-                || ((y - y3) * (y - y4) > EPSILON)) {
-
-            return false;
-        }
-        ret[0] = x;
-        ret[1] = y;
-        return true;
-    }
-
-    /**
-     * Imagine a donut shaped image and trying to create triangles from its centroid (like
-     * cutting a pie). This function performs such action (and also edge-case triangle strips
-     * generation) then returns the resulting triangle strips.
-     *
-     * @param retstrips - the resulting triangle strips
-     */
-    public static void donutPie2(float[] penumbra, int penumbraLength,
-            float[] umbra, int umbraLength, int rays, int layers, float strength,
-            float[] retstrips) {
-        int rings = layers + 1;
-
-        double step = Math.PI * 2 / rays;
-        float[] retxy = new float[2];
-        centroid2d(umbra, umbraLength, retxy);
-        float cx = retxy[0];
-        float cy = retxy[1];
-
-        float[] t1 = new float[rays];
-        float[] t2 = new float[rays];
-
-        for (int i = 0; i < rays; i++) {
-            float dx = (float) Math.sin(Math.PI / 4 + step * i);
-            float dy = (float) Math.cos(Math.PI / 4 + step * i);
-            t2[i] = rayIntersectPoly(umbra, umbraLength, cx, cy, dx, dy, 2)[0];
-            t1[i] = rayIntersectPoly(penumbra, penumbraLength, cx, cy, dx, dy, 2)[0];
-        }
-
-        int p = 0;
-        // Calc the vertex
-        for (int r = 0; r < layers; r++) {
-            int startp = p;
-            for (int i = 0; i < rays; i++) {
-                float dx = (float) Math.sin(Math.PI / 4 + step * i);
-                float dy = (float) Math.cos(Math.PI / 4 + step * i);
-
-                for (int j = r; j < (r + 2); j++) {
-                    float jf = j / (float) (rings - 1);
-                    float t = t1[i] + jf * (t2[i] - t1[i]);
-                    float op = (jf + 1 - 1 / (1 + (t - t1[i]) * (t - t1[i]))) / 2;
-
-                    retstrips[p * 3] = dx * t + cx;
-                    retstrips[p * 3 + 1] = dy * t + cy;
-                    retstrips[p * 3 + 2] = jf * op * strength;
-
-                    p++;
-                }
-            }
-            retstrips[p * 3] = retstrips[startp * 3];
-            retstrips[p * 3 + 1] = retstrips[startp * 3 + 1];
-            retstrips[p * 3 + 2] = retstrips[startp * 3 + 2];
-            p++;
-            startp++;
-            retstrips[p * 3] = retstrips[startp * 3];
-            retstrips[p * 3 + 1] = retstrips[startp * 3 + 1];
-            retstrips[p * 3 + 2] = retstrips[startp * 3 + 2];
-            p++;
-        }
-        int oldp = p - 1;
-        retstrips[p * 3] = retstrips[oldp * 3];
-        retstrips[p * 3 + 1] = retstrips[oldp * 3 + 1];
-        retstrips[p * 3 + 2] = retstrips[oldp * 3 + 2];
-        p+=2;
-
-        oldp = p;
-        for (int k = 0; k < rays; k++) {
-            int i = k / 2;
-            if ((k & 1) == 1) { // traverse the inside in a zig zag pattern
-                // for strips
-                i = rays - i - 1;
-            }
-            float dx = (float) Math.sin(Math.PI / 4 + step * i);
-            float dy = (float) Math.cos(Math.PI / 4 + step * i);
-
-            float jf = 1;
-
-            float t = t1[i] + jf * (t2[i] - t1[i]);
-            float op = (jf + 1 - 1 / (1 + (t - t1[i]) * (t - t1[i]))) / 2;
-
-            retstrips[p * 3] = dx * t + cx;
-            retstrips[p * 3 + 1] = dy * t + cy;
-            retstrips[p * 3 + 2] = jf * op * strength;
-            p++;
-        }
-        p = oldp - 1;
-        retstrips[p * 3] = retstrips[oldp * 3];
-        retstrips[p * 3 + 1] = retstrips[oldp * 3 + 1];
-        retstrips[p * 3 + 2] = retstrips[oldp * 3 + 2];
-    }
-
     /**
      * @return Rect bound of flattened (ignoring z). LTRB
      * @param dimension - 2D or 3D
diff --git a/bridge/src/android/view/shadow/AmbientShadowBitmapGenerator.java b/bridge/src/android/view/shadow/AmbientShadowBitmapGenerator.java
deleted file mode 100644
index 57fa2d6..0000000
--- a/bridge/src/android/view/shadow/AmbientShadowBitmapGenerator.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.view.shadow;
-
-import com.android.ide.common.rendering.api.LayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-
-import android.graphics.Bitmap;
-import android.view.math.Math3DHelper;
-
-/**
- * Generates ambient shadow bitmap
- */
-class AmbientShadowBitmapGenerator {
-
-    private final AmbientShadowConfig mShadowConfig;
-    private final TriangleBuffer mTriangleBuffer;
-    private final AmbientShadowVertexCalculator mCalculator;
-
-    private float mTranslateX;
-    private float mTranslateY;
-
-    private boolean mValid;
-
-    public AmbientShadowBitmapGenerator(AmbientShadowConfig shadowConfig) {
-        mShadowConfig = shadowConfig;
-
-        mTriangleBuffer = new TriangleBuffer();
-        mTriangleBuffer.setSize(mShadowConfig.getWidth(), mShadowConfig.getHeight(), 0);
-
-        mCalculator = new AmbientShadowVertexCalculator(mShadowConfig);
-    }
-
-    /**
-     * Populate vertices and fill the triangle buffers. To be called before {@link #getBitmap()}
-     */
-    public void populateShadow() {
-        try {
-            mValid = mCalculator.generateVertex(mShadowConfig.getPolygon());
-            if (!mValid) {
-                Bridge.getLog().warning(LayoutLog.TAG_INFO,  "Arithmetic error while " +
-                                "drawing ambient shadow", null, null);
-                return;
-            }
-
-            float[] shadowBounds = Math3DHelper.flatBound(mCalculator.getVertex(), 2);
-            if (shadowBounds[0] < 0) {
-                // translate to right by the offset amount.
-                mTranslateX = shadowBounds[0] * -1;
-            } else if (shadowBounds[2] > mShadowConfig.getWidth()) {
-                // translate to left by the offset amount.
-                mTranslateX = shadowBounds[2] - mShadowConfig.getWidth();
-            }
-
-            if (shadowBounds[1] < 0) {
-                mTranslateY = shadowBounds[1] * -1;
-            } else if (shadowBounds[3] > mShadowConfig.getHeight()) {
-                mTranslateY = shadowBounds[3] - mShadowConfig.getHeight();
-            }
-
-            Math3DHelper.translate(mCalculator.getVertex(), mTranslateX, mTranslateY, 2);
-
-            mTriangleBuffer.drawTriangles(mCalculator.getIndex(), mCalculator.getVertex(),
-                    mCalculator.getColor(), mShadowConfig.getShadowStrength());
-        } catch (IndexOutOfBoundsException|ArithmeticException mathError) {
-            Bridge.getLog().warning(LayoutLog.TAG_INFO,  "Arithmetic error while drawing " +
-                            "ambient shadow",
-                    mathError);
-        } catch (Exception ex) {
-            Bridge.getLog().warning(LayoutLog.TAG_INFO,  "Error while drawing shadow",
-                    ex);
-        }
-    }
-
-    public boolean isValid() {
-        return mValid;
-    }
-
-    public Bitmap getBitmap() {
-        return mTriangleBuffer.getImage();
-    }
-
-    public float getTranslateX() {
-        return mTranslateX;
-    }
-
-    public float getTranslateY() {
-        return mTranslateY;
-    }
-}
diff --git a/bridge/src/android/view/shadow/AmbientShadowConfig.java b/bridge/src/android/view/shadow/AmbientShadowConfig.java
index b06bd0a..97efd86 100644
--- a/bridge/src/android/view/shadow/AmbientShadowConfig.java
+++ b/bridge/src/android/view/shadow/AmbientShadowConfig.java
@@ -21,35 +21,19 @@
  */
 class AmbientShadowConfig {
 
-    private final int mWidth;
-    private final int mHeight;
-
     private final float mEdgeScale;
     private final float mShadowBoundRatio;
     private final float mShadowStrength;
 
     private final float[] mPolygon;
-
-    private final int mRays;
-    private final int mLayers;
+    private float[] mLightSourcePosition;
 
     private AmbientShadowConfig(Builder builder) {
         mEdgeScale = builder.mEdgeScale;
         mShadowBoundRatio = builder.mShadowBoundRatio;
         mShadowStrength = builder.mShadowStrength;
-        mRays = builder.mRays;
-        mLayers = builder.mLayers;
-        mWidth = builder.mWidth;
-        mHeight = builder.mHeight;
         mPolygon = builder.mPolygon;
-    }
-
-    public int getWidth() {
-        return mWidth;
-    }
-
-    public int getHeight() {
-        return mHeight;
+        mLightSourcePosition = builder.mLightSourcePosition;
     }
 
     /**
@@ -81,18 +65,10 @@
     }
 
     /**
-     * Returns # of rays to use in ray tracing. It determines the accuracy of outline (bounds) of
-     * the shadow.
+     * Returns 2D position of the light source
      */
-    public int getRays() {
-        return mRays;
-    }
-
-    /**
-     * Returns # of layers. It determines the intensity of the pen-umbra.
-     */
-    public int getLayers() {
-        return mLayers;
+    public float[] getLightSourcePosition() {
+        return mLightSourcePosition;
     }
 
     public static class Builder {
@@ -100,13 +76,9 @@
         private float mEdgeScale;
         private float mShadowBoundRatio;
         private float mShadowStrength;
-        private int mRays;
-        private int mLayers;
 
         private float[] mPolygon;
-
-        private int mWidth;
-        private int mHeight;
+        private float[] mLightSourcePosition;
 
         public Builder setEdgeScale(float edgeScale) {
             mEdgeScale = edgeScale;
@@ -123,27 +95,16 @@
             return this;
         }
 
-        public Builder setRays(int rays) {
-            mRays = rays;
-            return this;
-        }
-
-        public Builder setLayers(int layers) {
-            mLayers = layers;
-            return this;
-        }
-
-        public Builder setSize(int width, int height) {
-            mWidth = width;
-            mHeight = height;
-            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
new file mode 100644
index 0000000..630ec30
--- /dev/null
+++ b/bridge/src/android/view/shadow/AmbientShadowTriangulator.java
@@ -0,0 +1,64 @@
+/*
+ * 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.LayoutLog;
+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(LayoutLog.TAG_INFO,  "Arithmetic error while drawing " +
+                            "ambient shadow",
+                    null, mathError);
+        } catch (Exception ex) {
+            Bridge.getLog().warning(LayoutLog.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
index 3bf747c..5928f35 100644
--- a/bridge/src/android/view/shadow/AmbientShadowVertexCalculator.java
+++ b/bridge/src/android/view/shadow/AmbientShadowVertexCalculator.java
@@ -33,98 +33,65 @@
     public AmbientShadowVertexCalculator(AmbientShadowConfig config) {
         mConfig = config;
 
-        int rings = mConfig.getLayers() + 1;
-        int size = mConfig.getRays() * rings;
+        int size = mConfig.getPolygon().length / 3;
 
-        mVertex = new float[size * 2];
-        mColor = new float[size * 4];
-        mIndex = new int[(size * 2 + (mConfig.getRays() - 2)) * 3];
+        mVertex = new float[size * 2 * 2];
+        mColor = new float[size * 2 * 4];
+        mIndex = new int[(size * 3 - 2) * 3];
     }
 
     /**
-     * Generates vertex using the polygon info
-     * @param polygon 3d polygon info in format : {x1, y1, z1, x2, y2, z2 ...}
-     * @return true if vertices are generated with right colour/index. False otherwise.
+     * Generates ambient shadow triangulation using configuration provided
      */
-    public boolean generateVertex(float[] polygon) {
-        // Despite us not using z coord, we want calculations in 3d space as our polygon is using
-        // 3d coord system.
-        float[] centroidxy = new float[3];
+    public void generateVertex() {
+        float[] polygon = mConfig.getPolygon();
+        float cx = mConfig.getLightSourcePosition()[0];
+        float cy = mConfig.getLightSourcePosition()[1];
+
         int polygonLength = polygon.length/3;
 
-        Math3DHelper.centroid3d(polygon, polygonLength, centroidxy);
+        float opacity = .8f * (0.5f / (mConfig.getEdgeScale() / 10f));
 
-        float cx = centroidxy[0];
-        float cy = centroidxy[1];
+        int trShift = 0;
+        for (int i = 0; i < polygonLength; ++i, trShift += 6) {
+            int shift = i * 4;
+            int colorShift = i * 8;
+            int idxShift = i * 2;
 
-        Rays rays = new Rays(mConfig.getRays());
-        int raysLength = rays.dx.length;
-        float rayDist[] = new float[mConfig.getRays()];
+            float px = polygon[3 * i + 0];
+            float py = polygon[3 * i + 1];
+            mVertex[shift + 0] = px;
+            mVertex[shift + 1] = py;
 
-        float[] rayHeights = new float[mConfig.getRays()];
+            // 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();
 
-        for (int i = 0; i < raysLength; i++) {
-            float dx = rays.dx[i];
-            float dy = rays.dy[i];
+            mVertex[shift + 2] = cx + h * (px - cx);
+            mVertex[shift + 3] = cy + h * (py - cy);
 
-            float[] intersection = Math3DHelper.rayIntersectPoly(polygon, polygonLength, cx, cy,
-                    dx, dy, 3);
-            if (intersection.length == 1) {
-                return false;
-            }
-            rayDist[i] = intersection[0];
-            int index = (int) (intersection[2] * 3);
-            int index2 = (int) (((intersection[2] + 1) % polygonLength) * 3);
-            float h1 = polygon[index + 2] * mConfig.getShadowBoundRatio();
-            float h2 = polygon[index2 + 2] * mConfig.getShadowBoundRatio();
-            rayHeights[i] = h1 + intersection[1] * (h2 - h1);
+            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;
 
-        int rings = mConfig.getLayers() + 1;
-        for (int i = 0; i < raysLength; i++) {
-            float dx = rays.dx[i];
-            float dy = rays.dy[i];
-            float cast = rayDist[i] * rayHeights[i];
-
-            float opacity = .8f * (0.5f / (mConfig.getEdgeScale() / 10f));
-            for (int j = 0; j < rings; j++) {
-                int p = i * rings + j;
-                float jf = j / (float) (rings - 1);
-                float t = rayDist[i] + jf * (cast - rayDist[i]);
-
-                mVertex[p * 2 + 0] = dx * t + cx;
-                mVertex[p * 2 + 1] = dy * t + cy;
-                // TODO: we might be able to optimize this in the future.
-                mColor[p * 4 + 0] = 0;
-                mColor[p * 4 + 1] = 0;
-                mColor[p * 4 + 2] = 0;
-                mColor[p * 4 + 3] = (1 - jf) * opacity;
-            }
+        // 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);
         }
-
-        int k = 0;
-        for (int i = 0; i < mConfig.getRays(); i++) {
-            for (int j = 0; j < mConfig.getLayers(); j++) {
-                int r1 = j + rings * i;
-                int r2 = j + rings * ((i + 1) % mConfig.getRays());
-
-                mIndex[k * 3 + 0] = r1;
-                mIndex[k * 3 + 1] = r1 + 1;
-                mIndex[k * 3 + 2] = r2;
-                k++;
-                mIndex[k * 3 + 0] = r2;
-                mIndex[k * 3 + 1] = r1 + 1;
-                mIndex[k * 3 + 2] = r2 + 1;
-                k++;
-            }
-        }
-        int ringOffset = 0;
-        for (int i = 1; i < mConfig.getRays() - 1; i++, k++) {
-            mIndex[k * 3 + 0] = ringOffset;
-            mIndex[k * 3 + 1] = ringOffset + rings * i;
-            mIndex[k * 3 + 2] = ringOffset + rings * (1 + i);
-        }
-        return true;
     }
 
     public int[] getIndex() {
@@ -141,22 +108,4 @@
     public float[] getColor() {
         return mColor;
     }
-
-    private static class Rays {
-        public final float[] dx;
-        public final float[] dy;
-        public final double deltaAngle;
-
-        public Rays(int rays) {
-            dx = new float[rays];
-            dy = new float[rays];
-            deltaAngle = 2 * Math.PI / rays;
-
-            for (int i = 0; i < rays; i++) {
-                dx[i] = (float) Math.sin(deltaAngle * i);
-                dy[i] = (float) Math.cos(deltaAngle * i);
-            }
-        }
-    }
-
 }
diff --git a/bridge/src/android/view/shadow/HighQualityShadowPainter.java b/bridge/src/android/view/shadow/HighQualityShadowPainter.java
index dd555c5..7b8873c 100644
--- a/bridge/src/android/view/shadow/HighQualityShadowPainter.java
+++ b/bridge/src/android/view/shadow/HighQualityShadowPainter.java
@@ -23,11 +23,13 @@
 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() { }
 
@@ -66,11 +68,103 @@
 
         // 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);
 
-        paintAmbientShadow(poly, canvas, width, height, alpha, rectOriginal, radius);
-        paintSpotShadow(poly, rectScaled, elevation / SCALE_DOWN,
-                canvas, densityDpi, width, height, alpha, rectOriginal, 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);
     }
 
     /**
@@ -90,102 +184,14 @@
     }
 
     /**
-     * @param polygon - polygon of the shadow caster
-     * @param canvas - canvas to draw
-     * @param width - scaled canvas (parent) width
-     * @param height - scaled canvas (parent) height
-     * @param alpha - 0-1 scale
-     * @param shadowCasterOutline - unscaled original shadow caster outline.
-     * @param radius
-     */
-    private static void paintAmbientShadow(float[] polygon, Canvas canvas, int width, int height,
-            float alpha, Rect shadowCasterOutline, float radius) {
-        // TODO: Consider re-using the triangle buffer here since the world stays consistent.
-        // TODO: Reduce the buffer size based on shadow bounds.
-
-        AmbientShadowConfig config = new AmbientShadowConfig.Builder()
-                .setSize(width, height)
-                .setPolygon(polygon)
-                .setEdgeScale(ShadowConstants.AMBIENT_SHADOW_EDGE_SCALE)
-                .setShadowBoundRatio(ShadowConstants.AMBIENT_SHADOW_SHADOW_BOUND)
-                .setShadowStrength(ShadowConstants.AMBIENT_SHADOW_STRENGTH * alpha)
-                .setRays(ShadowConstants.AMBIENT_SHADOW_RAYS)
-                .setLayers(ShadowConstants.AMBIENT_SHADOW_LAYERS)
-                .build();
-
-        AmbientShadowBitmapGenerator generator = new AmbientShadowBitmapGenerator(config);
-        generator.populateShadow();
-
-        if (!generator.isValid()) {
-            return;
-        }
-
-        drawScaled(
-                canvas, generator.getBitmap(), (int) generator.getTranslateX(),
-                (int) generator.getTranslateY(), width, height,
-                shadowCasterOutline, radius);
-    }
-
-    /**
-     * @param poly - polygon of the shadow caster
-     * @param rectBound - scaled bounds of shadow caster.
-     * @param canvas - canvas to draw
-     * @param width - scaled canvas (parent) width
-     * @param height - scaled canvas (parent) height
-     * @param alpha - 0-1 scale
-     * @param shadowCasterOutline - unscaled original shadow caster outline.
-     * @param radius
-     */
-    private static void paintSpotShadow(float[] poly, Rect rectBound, float elevation, Canvas canvas,
-            float densityDpi, int width, int height, float alpha, Rect shadowCasterOutline,
-            float radius) {
-
-        // TODO: Use alpha later
-        float lightZHeightPx = ShadowConstants.SPOT_SHADOW_LIGHT_Z_HEIGHT_DP * (densityDpi / DisplayMetrics.DENSITY_DEFAULT);
-        if (lightZHeightPx - elevation < ShadowConstants.SPOT_SHADOW_LIGHT_Z_EPSILON) {
-            // If the view is above or too close to the light source then return.
-            // This is done to somewhat simulate android behaviour.
-            return;
-        }
-
-        float lightX = (rectBound.left + rectBound.right) / 2;
-        float lightY = rectBound.top;
-        // Light shouldn't be bigger than the object by too much.
-        int dynamicLightRadius = Math.min(rectBound.width(), rectBound.height());
-
-        SpotShadowConfig config = new SpotShadowConfig.Builder()
-                .setSize(width, height)
-                .setLayers(ShadowConstants.SPOT_SHADOW_LAYERS)
-                .setRays(ShadowConstants.SPOT_SHADOW_RAYS)
-                .setLightCoord(lightX, lightY, lightZHeightPx)
-                .setLightRadius(dynamicLightRadius)
-                .setLightSourcePoints(ShadowConstants.SPOT_SHADOW_LIGHT_SOURCE_POINTS)
-                .setShadowStrength(ShadowConstants.SPOT_SHADOW_STRENGTH * alpha)
-                .setPolygon(poly, poly.length / ShadowConstants.COORDINATE_SIZE)
-                .build();
-
-        SpotShadowBitmapGenerator generator = new SpotShadowBitmapGenerator(config);
-        generator.populateShadow();
-
-        if (!generator.validate()) {
-            return;
-        }
-
-        drawScaled(canvas, generator.getBitmap(), (int) generator.getTranslateX(),
-                (int) generator.getTranslateY(), width, height, shadowCasterOutline, radius);
-    }
-
-    /**
      * 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 width  - scaled width of canvas (parent)
-     * @param height - scaled height of canvas (parent)
      * @param shadowCaster - unscaled outline of shadow caster
      * @param radius
      */
     private static void drawScaled(Canvas canvas, Bitmap bitmap, int translateX, int translateY,
-            int width, int height, Rect shadowCaster, float radius) {
+            Rect shadowCaster, float radius, boolean isOpaque) {
         int unscaledTranslateX = translateX * SCALE_DOWN;
         int unscaledTranslateY = translateY * SCALE_DOWN;
 
@@ -193,11 +199,16 @@
         Rect dest = new Rect(
                 -unscaledTranslateX,
                 -unscaledTranslateY,
-                (width * SCALE_DOWN) - unscaledTranslateX,
-                (height * SCALE_DOWN) - unscaledTranslateY);
-        Rect destSrc = new Rect(0, 0, width, height);
-
-        if (radius > 0) {
+                (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);
@@ -223,7 +234,13 @@
          * dest == top + left + shadow caster + right + bottom
          * Visually, canvas.drawBitmap(bitmap, destSrc, dest, paint) would achieve the same result.
          */
-        Rect left = new Rect(dest.left, shadowCaster.top, shadowCaster.left, shadowCaster.bottom);
+        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);
@@ -231,10 +248,10 @@
 
         Rect right = new Rect(shadowCaster.right, shadowCaster.top, dest.right,
                 shadowCaster.bottom);
-        int rightScaled = (shadowCaster.right + unscaledTranslateX) / SCALE_DOWN + destSrc.left;
+        int rightScaled = (shadowCaster.right - dest.left) / SCALE_DOWN + destSrc.left;
 
         Rect bottom = new Rect(dest.left, shadowCaster.bottom, dest.right, dest.bottom);
-        int bottomScaled = (bottom.bottom - bottom.height()) / SCALE_DOWN + destSrc.top;
+        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);
diff --git a/bridge/src/android/view/shadow/ShadowConstants.java b/bridge/src/android/view/shadow/ShadowConstants.java
index 83617d8..049a549 100644
--- a/bridge/src/android/view/shadow/ShadowConstants.java
+++ b/bridge/src/android/view/shadow/ShadowConstants.java
@@ -31,17 +31,12 @@
 
     public static final float MIN_ALPHA = 0.2f;
 
-    public static final int SPOT_SHADOW_RAYS = 40;
-    public static final int SPOT_SHADOW_LAYERS = 1;
-    public static final int SPOT_SHADOW_LIGHT_SOURCE_POINTS = 4;
     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 int AMBIENT_SHADOW_RAYS = 120;
-    public static final int AMBIENT_SHADOW_LAYERS = 1;
     public static final float AMBIENT_SHADOW_STRENGTH = 1.0f;
 
     public static final int COORDINATE_SIZE = 3;
diff --git a/bridge/src/android/view/shadow/SpotShadowBitmapGenerator.java b/bridge/src/android/view/shadow/SpotShadowBitmapGenerator.java
deleted file mode 100644
index 1aa2258..0000000
--- a/bridge/src/android/view/shadow/SpotShadowBitmapGenerator.java
+++ /dev/null
@@ -1,150 +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.LayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.tools.layoutlib.annotations.VisibleForTesting;
-
-import android.graphics.Bitmap;
-import android.view.math.Math3DHelper;
-
-/**
- * Generate spot shadow bitmap.
- */
-class SpotShadowBitmapGenerator {
-
-    private final SpotShadowConfig mShadowConfig;
-    private final TriangleBuffer mTriangle;
-    private float[] mStrips;
-    private float[] mLightSources;
-    private float mTranslateX;
-    private float mTranslateY;
-
-    public SpotShadowBitmapGenerator(SpotShadowConfig config) {
-        // TODO: Reduce the buffer size based on shadow bounds.
-        mTriangle = new TriangleBuffer();
-        mShadowConfig = config;
-        // For now assume no change to the world size
-        mTriangle.setSize(config.getWidth(), config.getHeight(), 0);
-    }
-
-    /**
-     * Populate the shadow bitmap.
-     */
-    public void populateShadow() {
-        try {
-            mLightSources = SpotShadowVertexCalculator.calculateLight(
-                    mShadowConfig.getLightRadius(),
-                    mShadowConfig.getLightSourcePoints(),
-                    mShadowConfig.getLightCoord()[0],
-                    mShadowConfig.getLightCoord()[1],
-                    mShadowConfig.getLightCoord()[2]);
-
-            mStrips = new float[3 * SpotShadowVertexCalculator.getStripSize(
-                    mShadowConfig.getRays(),
-                    mShadowConfig.getLayers())];
-
-            if (SpotShadowVertexCalculator.calculateShadow(
-                    mLightSources,
-                    mShadowConfig.getLightSourcePoints(),
-                    mShadowConfig.getPoly(),
-                    mShadowConfig.getPolyLength(),
-                    mShadowConfig.getRays(),
-                    mShadowConfig.getLayers(),
-                    mShadowConfig.getShadowStrength(),
-                    mStrips) != 1) {
-                return;
-            }
-
-            // 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(mStrips, 3);
-
-            if ((shadowBounds[2] - shadowBounds[0]) > mShadowConfig.getWidth() ||
-                    (shadowBounds[3] - shadowBounds[1]) > mShadowConfig.getHeight()) {
-                // Spot shadow to be casted is larger than the parent canvas,
-                // We'll let ambient shadow do the trick and skip spot shadow here.
-                return;
-            }
-
-            mTranslateX = 0;
-            mTranslateY = 0;
-            if (shadowBounds[0] < 0) {
-                // translate to right by the offset amount.
-                mTranslateX = shadowBounds[0] * -1;
-            } else if (shadowBounds[2] > mShadowConfig.getWidth()) {
-                // translate to left by the offset amount.
-                mTranslateX = shadowBounds[2] - mShadowConfig.getWidth();
-            }
-
-            if (shadowBounds[1] < 0) {
-                mTranslateY = shadowBounds[1] * -1;
-            } else if (shadowBounds[3] > mShadowConfig.getHeight()) {
-                mTranslateY = shadowBounds[3] - mShadowConfig.getHeight();
-            }
-            Math3DHelper.translate(mStrips, mTranslateX, mTranslateY, 3);
-
-            mTriangle.drawTriangles(mStrips, mShadowConfig.getShadowStrength());
-        } catch (IndexOutOfBoundsException|ArithmeticException mathError) {
-            Bridge.getLog().warning(LayoutLog.TAG_INFO,  "Arithmetic error while drawing " +
-                            "spot shadow",
-                    mathError);
-        } catch (Exception ex) {
-            Bridge.getLog().warning(LayoutLog.TAG_INFO,  "Error while drawing shadow",
-                    ex);
-        }
-    }
-
-    public float getTranslateX() {
-        return mTranslateX;
-    }
-
-    public float getTranslateY() {
-        return mTranslateY;
-    }
-
-    public void clear() {
-        mTriangle.clear();
-    }
-
-    /**
-     * @return true if generated shadow poly is valid. False otherwise.
-     */
-    public boolean validate() {
-        return mStrips != null && mStrips.length >= 9;
-    }
-
-    /**
-     * @return the bitmap of shadow after it's populated
-     */
-    public Bitmap getBitmap() {
-        return mTriangle.getImage();
-    }
-
-    @VisibleForTesting
-    public float[] getStrips() {
-        return mStrips;
-    }
-
-    @VisibleForTesting
-    public void updateLightSource(float x, float y) {
-        mShadowConfig.setLightCoord(x, y);
-    }
-}
diff --git a/bridge/src/android/view/shadow/SpotShadowConfig.java b/bridge/src/android/view/shadow/SpotShadowConfig.java
index 56a7524..7b4fa4b 100644
--- a/bridge/src/android/view/shadow/SpotShadowConfig.java
+++ b/bridge/src/android/view/shadow/SpotShadowConfig.java
@@ -21,16 +21,10 @@
  * Model for spot shadow rendering. Assumes single light, single object.
  */
 class SpotShadowConfig {
-    private final int mWidth;
-    private final int mHeight;
 
     // No need to be final but making it immutable for now.
     private final int mLightRadius;
-    private final int mLightSourcePoints;
 
-    // No need to be final but making it immutable for now.
-    private final int mRays;
-    private final int mLayers;
 
     // No need to be final but making it immutable for now.
     private final float[] mPoly;
@@ -41,12 +35,7 @@
     private final float mShadowStrength;
 
     private SpotShadowConfig(SpotShadowConfig.Builder builder) {
-        mWidth = builder.mWidth;
-        mHeight = builder.mHeight;
         mLightRadius = builder.mLightRadius;
-        mLightSourcePoints = builder.mLightSourcePoints;
-        mRays = builder.mRays;
-        mLayers = builder.mLayers;
         mPoly = builder.mPoly;
         mPolyLength = builder.mPolyLength;
 
@@ -58,24 +47,6 @@
     }
 
     /**
-     * World width / height
-     */
-    public int getWidth() {
-        return mWidth;
-    }
-
-    public int getHeight() {
-        return mHeight;
-    }
-
-    /**
-     * @return number of light source points to ray trace
-     */
-    public int getLightSourcePoints() {
-        return mLightSourcePoints;
-    }
-
-    /**
      * @return size of the light source radius (light source is always generated as a circular shape)
      */
     public int getLightRadius() {
@@ -97,24 +68,9 @@
     }
 
     /**
-     * @return number of rays to use in raytracing. It determines the accuracy of outline (bounds) of
-     * the shadow.
-     */
-    public int getRays() {
-        return mRays;
-    }
-
-    /**
-     * @return number of layers. It determines the intensity of pen-umbra
-     */
-    public int getLayers() {
-        return mLayers;
-    }
-
-    /**
      * Update the light source coord.
-     * @param x - x in {@link #getWidth()} coordinate
-     * @param y - y in {@link #getHeight()} coordinate
+     * @param x - horizontal coordinate
+     * @param y - vertical coordinate
      */
     public void setLightCoord(float x, float y) {
         mLightCoord[0] = x;
@@ -134,16 +90,8 @@
 
     public static class Builder {
 
-        private int mWidth;
-        private int mHeight;
-
         // No need to be final but making it immutable for now.
         private int mLightRadius;
-        private int mLightSourcePoints;
-
-        // No need to be final but making it immutable for now.
-        private int mRays;
-        private int mLayers;
 
         // No need to be final but making it immutable for now.
         private float[] mPoly;
@@ -163,32 +111,11 @@
             return this;
         }
 
-        public Builder setSize(int width, int height) {
-            mWidth = width;
-            mHeight = height;
-            return this;
-        }
-
         public Builder setLightRadius(int mLightRadius) {
             this.mLightRadius = mLightRadius;
             return this;
         }
 
-        public Builder setLightSourcePoints(int mLightSourcePoints) {
-            this.mLightSourcePoints = mLightSourcePoints;
-            return this;
-        }
-
-        public Builder setRays(int mRays) {
-            this.mRays = mRays;
-            return this;
-        }
-
-        public Builder setLayers(int mLayers) {
-            this.mLayers = mLayers;
-            return this;
-        }
-
         public Builder setPolygon(float[] poly, int polyLength) {
             this.mPoly = poly;
             this.mPolyLength = polyLength;
diff --git a/bridge/src/android/view/shadow/SpotShadowTriangulator.java b/bridge/src/android/view/shadow/SpotShadowTriangulator.java
new file mode 100644
index 0000000..854a4d5
--- /dev/null
+++ b/bridge/src/android/view/shadow/SpotShadowTriangulator.java
@@ -0,0 +1,79 @@
+/*
+ * 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.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.tools.layoutlib.annotations.VisibleForTesting;
+
+import android.graphics.Bitmap;
+import android.view.math.Math3DHelper;
+
+/**
+ * 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(LayoutLog.TAG_INFO,  "Arithmetic error while drawing " +
+                            "spot shadow",
+                    null, mathError);
+        } catch (Exception ex) {
+            Bridge.getLog().warning(LayoutLog.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
index 2e4191b..fc02d18 100644
--- a/bridge/src/android/view/shadow/SpotShadowVertexCalculator.java
+++ b/bridge/src/android/view/shadow/SpotShadowVertexCalculator.java
@@ -16,6 +16,7 @@
 
 package android.view.shadow;
 
+import android.graphics.Rect;
 import android.view.math.Math3DHelper;
 
 /**
@@ -30,31 +31,40 @@
      * 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 points - how many light source points to generate
      * @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, int points, float x, float y, float height) {
-        float[] ret = new float[points * 3];
-        for (int i = 0; i < points; i++) {
-            double angle = 2 * i * Math.PI / points;
-            ret[i * 3] = (float) Math.sin(angle) * radius + x;
-            ret[i * 3 + 1] = (float) Math.cos(angle) * radius + y;
-            ret[i * 3 + 2] = (height);
-        }
+    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 rays - Number of rays to use for tracing
-     * @param layers - Number of layers for shadow rendering.
-     * @return size required for shadow vertices mData array based on # of rays and layers
+     * @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 getStripSize(int rays, int layers){
-        return  (2 + rays + ((layers) * 2 * (rays + 1)));
+    public static int[] getStripSizes(int polyLength){
+        return new int[] { ((polyLength + 4) / 8) * 16 + 2, 4};
     }
 
     /**
@@ -64,97 +74,122 @@
      * Precondition : Light height must be higher than any poly vertices
      *
      * @param lightPoly - Vertices of a light source.
-     * @param lightPolyLength - Size of the vertices (usually lightPoly.length/3 unless w is
-     * included)
      * @param poly - Vertices of opaque object casting shadow
      * @param polyLength - Size of the vertices
-     * @param rays - Number of rays to use for tracing. It determines accuracy of the outline
-     * (bounds) of the shadow
-     * @param layers - Number of layers for shadow. It determines intensity of pen-umbra
      * @param strength - Strength of the shadow overall [0-1]
-     * @param retstrips - Array mData to be filled in format : {x1, y1, z1, x2, y2, z2}
-     * @return 1 if successful, error code otherwise.
+     * @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 int calculateShadow(
+    public static void calculateShadow(
             float[] lightPoly,
-            int lightPolyLength,
             float[] poly,
             int polyLength,
-            int rays,
-            int layers,
             float strength,
-            float[] retstrips) {
-        float[] shadowRegion = new float[lightPolyLength * polyLength * 2];
-        float[] outline = new float[polyLength * 2];
-        float[] umbra = new float[polyLength * lightPolyLength * 2];
-        int umbraLength = 0;
+            float[][] retstrips) {
+        float[] outerStrip = retstrips[0];
 
-        int k = 0;
-        for (int j = 0; j < lightPolyLength; j++) {
-            int m = 0;
-            for (int i = 0; i < polyLength; i++) {
-                float t = lightPoly[j * 3 + 2] - poly[i * 3 + 2];
-                if (t == 0) {
-                    return 0;
-                }
-                t = lightPoly[j * 3 + 2] / t;
-                float x = lightPoly[j * 3] - t * (lightPoly[j * 3] - poly[i * 3]);
-                float y = lightPoly[j * 3 + 1] - t * (lightPoly[j * 3 + 1] - poly[i * 3 + 1]);
+        // 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;
 
-                shadowRegion[k * 2] = x;
-                shadowRegion[k * 2 + 1] = y;
-                outline[m * 2] = x;
-                outline[m * 2 + 1] = y;
+                float t = lz / (lz - poly[shift + 2]);
 
-                k++;
-                m++;
+                umbra[uShift + 0] = lx - t * (lx - poly[shift + 0]);
+                umbra[uShift + 1] = ly - t * (ly - poly[shift + 1]);
             }
 
-            if (umbraLength == 0) {
-                for (int i = 0; i < polyLength * 2; i++) {
-                    umbra[i] = outline[i];
-                }
-                umbraLength = polyLength;
-            } else {
-                umbraLength = Math3DHelper.intersection(outline, polyLength, umbra, umbraLength);
-                if (umbraLength == 0) {
-                    break;
-                }
-
-            }
-        }
-        int shadowRegionLength = k;
-
-        float[] penumbra = new float[k * 2];
-        int penumbraLength = Math3DHelper.hull(shadowRegion, shadowRegionLength, penumbra);
-        if (umbraLength < 3) {// no real umbra make a fake one
-            float[] p = new float[3];
-            Math3DHelper.centroid3d(lightPoly, lightPolyLength, p);
-            float[] centShadow = new float[polyLength * 2];
-            for (int i = 0; i < polyLength; i++) {
-                float t = p[2] - poly[i * 3 + 2];
-                if (t == 0) {
-                    return 0;
-                }
-                t = p[2] / t;
-                float x = p[0] - t * (p[0] - poly[i * 3]);
-                float y = p[1] - t * (p[1] - poly[i * 3 + 1]);
-
-                centShadow[i * 2] = x;
-                centShadow[i * 2 + 1] = y;
-            }
-            float[] c = new float[2];
-            Math3DHelper.centroid2d(centShadow, polyLength, c);
-            for (int i = 0; i < polyLength; i++) {
-                centShadow[i * 2] = (c[0] * 9 + centShadow[i * 2]) / 10;
-                centShadow[i * 2 + 1] = (c[1] * 9 + centShadow[i * 2 + 1]) / 10;
-            }
-            umbra = centShadow; // fake umbra
-            umbraLength = polyLength; // same size as the original polygon
+            idx -= rollBack;
         }
 
-        Math3DHelper.donutPie2(penumbra, penumbraLength, umbra, umbraLength, rays,
-                layers, strength, retstrips);
-        return 1;
+        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
index 8db8e1c..3c01171 100644
--- a/bridge/src/android/view/shadow/TriangleBuffer.java
+++ b/bridge/src/android/view/shadow/TriangleBuffer.java
@@ -18,11 +18,12 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
+import android.view.math.Math3DHelper;
 
 import java.util.Arrays;
 
-import static android.view.math.Math3DHelper.max;
-import static android.view.math.Math3DHelper.min;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
 
 /**
  * 2D Triangle buffer element that colours using z value. (z scale set).
@@ -35,10 +36,6 @@
     int mBorder;
     Bitmap mBitmap;
     int mData[];
-    private float mMinX;
-    private float mMaxX;
-    private float mMinY;
-    private float mMaxY;
 
     public void setSize(int width, int height, int border) {
         if (mWidth == width && mHeight == height) {
@@ -50,8 +47,6 @@
         mImgWidth = width;
         mImgHeight = height;
 
-        setScale(0, width, 0, height);
-
         mBitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
         mData = new int[width * height];
     }
@@ -77,12 +72,9 @@
             c =  scale*color[vIndex * 4 + 3];
             float fx1 = vx, fy1 = vy, fz1 = c;
 
-            triangleZBuffMin(mData, mImgWidth, mImgHeight, fx3, fy3, fz3, fx2, fy2,
-                    fz2, fx1, fy1, fz1);
             triangleZBuffMin(mData, mImgWidth, mImgHeight, fx1, fy1, fz1, fx2, fy2,
                     fz2, fx3, fy3, fz3);
         }
-        mBitmap.setPixels(mData, 0, mWidth, 0, 0, mWidth, mHeight);
     }
 
     public void drawTriangles(float[] strip,float scale) {
@@ -97,10 +89,10 @@
             triangleZBuffMin(mData, mImgWidth, mImgHeight, fx3, fy3, fz3, fx2, fy2,
                     fz2, fx1, fy1, fz1);
         }
-        mBitmap.setPixels(mData, 0, mWidth, 0, 0, mWidth, mHeight);
     }
 
-    public Bitmap getImage() {
+    public Bitmap createImage() {
+        mBitmap.setPixels(mData, 0, mWidth, 0, 0, mWidth, mHeight);
         return mBitmap;
     }
 
@@ -156,10 +148,10 @@
         int fdy23 = dy23 << 4;
         int fdy31 = dy31 << 4;
 
-        int minx = (min(x1, x2, x3) + 0xF) >> 4;
-        int maxx = (max(x1, x2, x3) + 0xF) >> 4;
-        int miny = (min(y1, y2, y3) + 0xF) >> 4;
-        int maxy = (max(y1, y2, y3) + 0xF) >> 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;
@@ -197,15 +189,26 @@
             int cx2 = cy2;
             int cx3 = cy3;
             float p = zoff + dy * y;
-            for (int x = minx; x < maxx; x++) {
-                if (cx1 > 0 && cx2 > 0 && cx3 > 0) {
-                    int point = x + off;
-                    float zval = p + dx * x;
-                    buff[point] = ((int) (zval * 255)) << 24;
-                }
-                cx1 -= fdy12;
-                cx2 -= fdy23;
-                cx3 -= fdy31;
+
+            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;
@@ -214,11 +217,46 @@
         }
     }
 
-    private void setScale(float minx, float maxx, float miny, float maxy) {
-        mMinX = minx;
-        mMaxX = maxx;
-        mMinY = miny;
-        mMaxY = maxy;
+    /**
+     * 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() {
diff --git a/bridge/src/com/android/layoutlib/bridge/Bridge.java b/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 4072bf3..88298d6 100644
--- a/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -16,9 +16,7 @@
 
 package com.android.layoutlib.bridge;
 
-import com.android.ide.common.rendering.api.Capability;
 import com.android.ide.common.rendering.api.DrawableParams;
-import com.android.ide.common.rendering.api.Features;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.RenderSession;
 import com.android.ide.common.rendering.api.ResourceNamespace;
@@ -37,6 +35,8 @@
 import com.android.tools.layoutlib.create.OverrideMethod;
 import com.android.util.Pair;
 
+import android.animation.PropertyValuesHolder;
+import android.animation.PropertyValuesHolder_Delegate;
 import android.content.res.BridgeAssetManager;
 import android.graphics.Bitmap;
 import android.graphics.FontFamily_Delegate;
@@ -126,17 +126,18 @@
      */
     private final static LayoutLog sDefaultLog = new LayoutLog() {
         @Override
-        public void error(String tag, String message, Object data) {
+        public void error(String tag, String message, Object viewCookie, Object data) {
             System.err.println(message);
         }
 
         @Override
-        public void error(String tag, String message, Throwable throwable, Object data) {
+        public void error(String tag, String message, Throwable throwable, Object viewCookie,
+                Object data) {
             System.err.println(message);
         }
 
         @Override
-        public void warning(String tag, String message, Object data) {
+        public void warning(String tag, String message, Object viewCookie, Object data) {
             System.out.println(message);
         }
     };
@@ -148,30 +149,10 @@
 
     public static boolean sIsTypefaceInitialized;
 
-    private static final int LAST_SUPPORTED_FEATURE = Features.THEME_PREVIEW_NAVIGATION_BAR;
-
-    @Override
-    public int getApiLevel() {
-        return com.android.ide.common.rendering.api.Bridge.API_CURRENT;
-    }
-
-    @SuppressWarnings("deprecation")
-    @Override
-    @Deprecated
-    public EnumSet<Capability> getCapabilities() {
-        // The Capability class is deprecated and frozen. All Capabilities enumerated there are
-        // supported by this version of LayoutLibrary. So, it's safe to use EnumSet.allOf()
-        return EnumSet.allOf(Capability.class);
-    }
-
-    @Override
-    public boolean supports(int feature) {
-        return feature <= LAST_SUPPORTED_FEATURE;
-    }
-
     @Override
     public boolean init(Map<String,String> platformProperties,
             File fontLocation,
+            String nativeLibPath,
             String icuDataPath,
             Map<String, Map<String, Integer>> enumValueMap,
             LayoutLog log) {
@@ -196,7 +177,7 @@
                 @Override
                 public void onInvokeV(String signature, boolean isNative, Object caller) {
                     sDefaultLog.error(null, "Missing Stub: " + signature +
-                            (isNative ? " (native)" : ""), null /*data*/);
+                            (isNative ? " (native)" : ""), null, null /*data*/);
 
                     if (debug.equalsIgnoreCase("throw")) {
                         // Throwing this exception doesn't seem that useful. It breaks
@@ -266,7 +247,7 @@
             if (log != null) {
                 log.error(LayoutLog.TAG_BROKEN,
                         "Failed to load com.android.internal.R from the layout library jar",
-                        throwable, null);
+                        throwable, null, null);
             }
             return false;
         }
@@ -443,7 +424,7 @@
     }
 
     @Override
-    public void clearCaches(Object projectKey) {
+    public void clearResourceCaches(Object projectKey) {
         if (projectKey != null) {
             sProjectBitmapCache.remove(projectKey);
             sProject9PatchCache.remove(projectKey);
@@ -451,6 +432,12 @@
     }
 
     @Override
+    public void clearAllCaches(Object projectKey) {
+        clearResourceCaches(projectKey);
+        PropertyValuesHolder_Delegate.clearCaches();
+    }
+
+    @Override
     public Result getViewParent(Object viewObject) {
         if (viewObject instanceof View) {
             return Status.SUCCESS.createResult(((View)viewObject).getParent());
@@ -545,7 +532,7 @@
     /**
      * Returns details of a framework resource from its integer value.
      *
-     * <p>TODO(namespaces): remove this and just do all id resolution through the callback.
+     * <p>TODO(b/156609434): remove this and just do all id resolution through the callback.
      */
     @Nullable
     public static ResourceReference resolveResourceId(int value) {
diff --git a/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
index 842d82a..442a77b 100644
--- a/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -58,11 +58,6 @@
     }
 
     @Override
-    public boolean isAlphaChannelImage() {
-        return mSession != null && mSession.isAlphaChannelImage();
-    }
-
-    @Override
     public List<ViewInfo> getRootViews() {
         return mSession != null ? mSession.getViewInfos() : Collections.emptyList();
     }
@@ -159,4 +154,12 @@
         }
         mLastResult = lastResult;
     }
+
+    @Override
+    public Object getValidationData() {
+        if (mSession != null) {
+            return mSession.getValidatorResult();
+        }
+        return null;
+    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
index d8bef78..ce613e0 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
@@ -18,16 +18,20 @@
 
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderResult;
+import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.IContentProvider;
 import android.content.OperationApplicationException;
+import android.content.pm.PackageManager;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.ICancellationSignal;
 import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 
 import java.io.FileNotFoundException;
@@ -40,29 +44,29 @@
  */
 public final class BridgeContentProvider implements IContentProvider {
     @Override
-    public ContentProviderResult[] applyBatch(String callingPackage, String authority,
-            ArrayList<ContentProviderOperation> arg0)
+    public ContentProviderResult[] applyBatch(String callingPackage, String callingFeatureId,
+            String authority, ArrayList<ContentProviderOperation> arg0)
             throws RemoteException, OperationApplicationException {
         // TODO Auto-generated method stub
         return null;
     }
 
     @Override
-    public int bulkInsert(String callingPackage, Uri arg0, ContentValues[] arg1)
-            throws RemoteException {
+    public int bulkInsert(String callingPackage, String callingFeatureId, Uri arg0,
+            ContentValues[] arg1) throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
     }
 
     @Override
-    public Bundle call(String callingPackage, String authority, String arg0, String arg1,
-            Bundle arg2) throws RemoteException {
+    public Bundle call(String callingPackage, String callingFeatureId, String authority,
+            String arg0, String arg1, Bundle arg2) throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
 
     @Override
-    public int delete(String callingPackage, Uri arg0, String arg1, String[] arg2)
+    public int delete(String callingPackage, String callingFeatureId, Uri arg0, Bundle arg1)
             throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
@@ -74,38 +78,53 @@
         return null;
     }
 
+    @SuppressWarnings("deprecation")
     @Override
-    public Uri insert(String callingPackage, Uri arg0, ContentValues arg1) throws RemoteException {
+    public void getTypeAsync(Uri uri, RemoteCallback remoteCallback) {
+        AsyncTask.SERIAL_EXECUTOR.execute(() -> {
+            try {
+                final Bundle bundle = new Bundle();
+                bundle.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri));
+                remoteCallback.sendResult(bundle);
+            } catch (RemoteException e) {
+              // Ignore
+            }
+        });
+    }
+
+    @Override
+    public Uri insert(String callingPackage, String callingFeatureId, Uri arg0, ContentValues arg1,
+            Bundle arg2) throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
 
     @Override
-    public AssetFileDescriptor openAssetFile(
-            String callingPackage, Uri arg0, String arg1, ICancellationSignal signal)
+    public AssetFileDescriptor openAssetFile(String callingPackage, String callingFeatureId,
+            Uri arg0, String arg1, ICancellationSignal signal)
             throws RemoteException, FileNotFoundException {
         // TODO Auto-generated method stub
         return null;
     }
 
     @Override
-    public ParcelFileDescriptor openFile(
-            String callingPackage, Uri arg0, String arg1, ICancellationSignal signal, IBinder token)
+    public ParcelFileDescriptor openFile(String callingPackage, String callingFeatureId, Uri arg0,
+            String arg1, ICancellationSignal signal, IBinder token)
             throws RemoteException, FileNotFoundException {
         // TODO Auto-generated method stub
         return null;
     }
 
     @Override
-    public Cursor query(String callingPackage, Uri arg0, String[] arg1,
+    public Cursor query(String callingPackage, String callingFeatureId, Uri arg0, String[] arg1,
             Bundle arg3, ICancellationSignal arg4) throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
 
     @Override
-    public int update(String callingPackage, Uri arg0, ContentValues arg1, String arg2,
-            String[] arg3) throws RemoteException {
+    public int update(String callingPackage, String callingFeatureId, Uri arg0, ContentValues arg1,
+            Bundle arg2) throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
     }
@@ -123,8 +142,9 @@
     }
 
     @Override
-    public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri arg0, String arg1,
-            Bundle arg2, ICancellationSignal signal) throws RemoteException, FileNotFoundException {
+    public AssetFileDescriptor openTypedAssetFile(String callingPackage, String callingFeatureId,
+            Uri arg0, String arg1, Bundle arg2, ICancellationSignal signal)
+            throws RemoteException, FileNotFoundException {
         // TODO Auto-generated method stub
         return null;
     }
@@ -137,18 +157,42 @@
 
 
     @Override
-    public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException {
+    public Uri canonicalize(String callingPkg, String callingFeatureId, Uri uri)
+            throws RemoteException {
+        return null;
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public void canonicalizeAsync(String callingPkg, String callingFeatureId, Uri uri,
+            RemoteCallback remoteCallback) {
+        AsyncTask.SERIAL_EXECUTOR.execute(() -> {
+            try {
+                final Bundle bundle = new Bundle();
+                bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
+                        canonicalize(callingPkg, callingFeatureId, uri));
+                remoteCallback.sendResult(bundle);
+            } catch (RemoteException e) {
+              // Ignore
+            }
+        });
+    }
+
+    @Override
+    public Uri uncanonicalize(String callingPkg, String callingFeatureId, Uri uri)
+            throws RemoteException {
         return null;
     }
 
     @Override
-    public Uri uncanonicalize(String callingPkg, Uri uri) throws RemoteException {
-        return null;
-    }
-
-    @Override
-    public boolean refresh(String callingPkg, Uri url, Bundle args,
-                    ICancellationSignal cancellationSignal) throws RemoteException {
+    public boolean refresh(String callingPkg, String callingFeatureId, Uri url, Bundle args,
+            ICancellationSignal cancellationSignal) throws RemoteException {
         return false;
     }
+
+    @Override
+    public int checkUriPermission(String callingPkg, String callingFeatureId, Uri uri, int uid,
+            int modeFlags) throws RemoteException {
+        return PackageManager.PERMISSION_DENIED;
+    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeContentResolver.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeContentResolver.java
index 8d259d7..80a50a7 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeContentResolver.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeContentResolver.java
@@ -90,6 +90,15 @@
      * Stub for the layoutlib bridge content resolver.
      */
     @Override
+    public void registerContentObserver(Uri uri, boolean notifyForDescendents,
+            ContentObserver observer, int userHandle) {
+        // pass
+    }
+
+    /**
+     * Stub for the layoutlib bridge content resolver.
+     */
+    @Override
     public void unregisterContentObserver(ContentObserver observer) {
         // pass
     }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 0d1a7d2..9a076f5 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -32,6 +32,7 @@
 import com.android.layoutlib.bridge.BridgeConstants;
 import com.android.layoutlib.bridge.android.view.WindowManagerImpl;
 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.util.Pair;
@@ -41,8 +42,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.SystemServiceRegistry_Accessor;
+import android.app.SystemServiceRegistry;
 import android.content.BroadcastReceiver;
+import android.content.ClipboardManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -65,7 +67,6 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.graphics.Bitmap;
-import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
 import android.net.Uri;
@@ -90,6 +91,9 @@
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
+import android.view.autofill.AutofillManager;
+import android.view.autofill.IAutoFillManager.Default;
+import android.view.inputmethod.InputMethodManager;
 import android.view.textservice.TextServicesManager;
 
 import java.io.File;
@@ -149,6 +153,8 @@
      */
     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;
@@ -158,6 +164,8 @@
     private final LayoutlibCallback mLayoutlibCallback;
     private final WindowManager mWindowManager;
     private final DisplayManager mDisplayManager;
+    private final AutofillManager mAutofillManager;
+    private final ClipboardManager mClipboardManager;
     private final HashMap<View, Integer> mScrollYPos = new HashMap<>();
     private final HashMap<View, Integer> mScrollXPos = new HashMap<>();
 
@@ -218,7 +226,9 @@
             @NonNull LayoutlibCallback layoutlibCallback,
             @NonNull Configuration config,
             int targetSdkVersion,
-            boolean hasRtlSupport) {
+            boolean hasRtlSupport,
+            boolean shadowsEnabled,
+            boolean highQualityShadows) {
         mProjectKey = projectKey;
         mMetrics = metrics;
         mLayoutlibCallback = layoutlibCallback;
@@ -239,8 +249,10 @@
             mApplicationInfo.flags = mApplicationInfo.flags | ApplicationInfo.FLAG_SUPPORTS_RTL;
         }
 
-        mWindowManager = new WindowManagerImpl(mMetrics);
+        mWindowManager = new WindowManagerImpl(this, mMetrics);
         mDisplayManager = new DisplayManager(this);
+        mAutofillManager = new AutofillManager(this, new Default());
+        mClipboardManager = new ClipboardManager(this, null);
 
         if (mLayoutlibCallback.isResourceNamespacingRequired()) {
             if (mLayoutlibCallback.hasAndroidXAppCompat()) {
@@ -251,6 +263,9 @@
         } else {
             mAppCompatNamespace = ResourceNamespace.RES_AUTO;
         }
+
+        mShadowsEnabled = shadowsEnabled;
+        mHighQualityShadows = highQualityShadows;
     }
 
     /**
@@ -401,12 +416,28 @@
         String stringValue = value.getValue();
         if (!stringValue.isEmpty()) {
             if (stringValue.charAt(0) == '#') {
-                outValue.type = TypedValue.TYPE_INT_COLOR_ARGB8;
-                outValue.data = Color.parseColor(value.getValue());
+                outValue.data = ResourceHelper.getColor(stringValue);
+                switch (stringValue.length()) {
+                    case 4:
+                        outValue.type = TypedValue.TYPE_INT_COLOR_RGB4;
+                        break;
+                    case 5:
+                        outValue.type = TypedValue.TYPE_INT_COLOR_ARGB4;
+                        break;
+                    case 7:
+                        outValue.type = TypedValue.TYPE_INT_COLOR_RGB8;
+                        break;
+                    default:
+                        outValue.type = TypedValue.TYPE_INT_COLOR_ARGB8;
+                }
             }
             else if (stringValue.charAt(0) == '@') {
                 outValue.type = TypedValue.TYPE_REFERENCE;
             }
+            else if ("true".equals(stringValue) || "false".equals(stringValue)) {
+                outValue.type = TypedValue.TYPE_INT_BOOLEAN;
+                outValue.data = "true".equals(stringValue) ? 1 : 0;
+            }
         }
 
         int a = getResourceId(value.asReference(), 0 /*defValue*/);
@@ -492,11 +523,11 @@
                     }
                 } else {
                     Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                            String.format("File %s is missing!", path), null);
+                            String.format("File %s is missing!", path), null, null);
                 }
             } catch (XmlPullParserException e) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                        "Failed to parse file " + path, e, null /*data*/);
+                        "Failed to parse file " + path, e, null, null /*data*/);
                 // we'll return null below.
             } finally {
                 mBridgeInflater.setResourceReference(null);
@@ -504,7 +535,7 @@
         } else {
             Bridge.getLog().error(LayoutLog.TAG_BROKEN,
                     String.format("Layout %s%s does not exist.", isPlatformLayout ? "android:" : "",
-                            layout.getName()), null);
+                            layout.getName()), null, null);
         }
 
         return Pair.of(null, Boolean.FALSE);
@@ -599,7 +630,8 @@
                 return mWindowManager;
 
             case POWER_SERVICE:
-                return new PowerManager(this, new BridgePowerManager(), new Handler());
+                return new PowerManager(this, new BridgePowerManager(), new BridgeThermalService(),
+                        new Handler());
 
             case DISPLAY_SERVICE:
                 return mDisplayManager;
@@ -607,8 +639,15 @@
             case ACCESSIBILITY_SERVICE:
                 return AccessibilityManager.getInstance(this);
 
-            case INPUT_METHOD_SERVICE:  // needed by SearchView
+            case INPUT_METHOD_SERVICE:  // needed by SearchView and Compose
+                return InputMethodManager.forContext(this);
+
             case AUTOFILL_MANAGER_SERVICE:
+                return mAutofillManager;
+
+            case CLIPBOARD_SERVICE:
+                return mClipboardManager;
+
             case AUDIO_SERVICE:
             case TEXT_CLASSIFICATION_SERVICE:
             case CONTENT_CAPTURE_MANAGER_SERVICE:
@@ -622,7 +661,7 @@
 
     @Override
     public String getSystemServiceName(Class<?> serviceClass) {
-        return SystemServiceRegistry_Accessor.getSystemServiceName(serviceClass);
+        return SystemServiceRegistry.getSystemServiceName(serviceClass);
     }
 
     /**
@@ -647,7 +686,7 @@
 
             if (style == null) {
                 Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
-                        "Failed to find style with " + resId, null);
+                        "Failed to find style with " + resId, null, null);
                 return null;
             }
         }
@@ -716,7 +755,7 @@
         } else if (set != null) {
             // really this should not be happening since its instantiated in Bridge
             Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                    "Parser is not a BridgeXmlBlockParser!", null);
+                    "Parser is not a BridgeXmlBlockParser!", null, null);
             return null;
         } else {
             // `set` is null, so there will be no values to resolve.
@@ -755,7 +794,8 @@
                 // This will happen if the user explicitly used a non existing int value for
                 // defStyleAttr or there's something wrong with the project structure/build.
                 Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
-                        "Failed to find the style corresponding to the id " + defStyleAttr, null);
+                        "Failed to find the style corresponding to the id " + defStyleAttr, null,
+                        null);
             } else {
                 // look for the style in the current theme, and its parent:
                 ResourceValue item = mRenderResources.findItemInTheme(defStyleAttribute);
@@ -798,21 +838,21 @@
                                     String.format(
                                             "Style with id 0x%x (resolved to '%s') does not exist.",
                                             defStyleRes, value.getName()),
-                                    null);
+                                    null, null);
                         }
                     } else {
                         Bridge.getLog().error(null,
                                 String.format(
                                         "Resource id 0x%x is not of type STYLE (instead %s)",
                                         defStyleRes, value.getResourceType().name()),
-                                null);
+                                null, null);
                     }
                 } else {
                     Bridge.getLog().error(null,
                             String.format(
                                     "Failed to find style with id 0x%x in current theme",
                                     defStyleRes),
-                            null);
+                            null, null);
                 }
             }
         }
@@ -914,7 +954,7 @@
                                 }
                                 Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR,
                                         String.format("Failed to find '%s' in current theme.", val),
-                                        val);
+                                        null, val);
                             }
                         }
                     }
@@ -1056,7 +1096,7 @@
      * Maps a given style to a numeric id.
      *
      * <p>For now Bridge handles numeric ids (both fixed and dynamic) for framework and the callback
-     * for non-framework. TODO(namespaces): teach the IDE about fixed framework ids and handle this
+     * for non-framework. TODO(b/156609434): teach the IDE about fixed framework ids and handle this
      * all in the callback.
      */
     public int getDynamicIdByStyle(StyleResourceValue resValue) {
@@ -1071,7 +1111,7 @@
      * Maps a numeric id back to {@link StyleResourceValue}.
      *
      * <p>For now framework numeric ids are handled by Bridge, so try there first and fall back to
-     * the callback, which manages ids for non-framework resources. TODO(namespaces): manage all
+     * the callback, which manages ids for non-framework resources. TODO(b/156609434): manage all
      * ids in the IDE.
      *
      * <p>Once we the resource for the given id, we ask the IDE to get the
@@ -1925,7 +1965,7 @@
 
     @Override
     public File getObbDir() {
-        Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, "OBB not supported", null);
+        Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, "OBB not supported", null, null);
         return null;
     }
 
@@ -2026,6 +2066,25 @@
         return true;
     }
 
+    @Override
+    public boolean isUiContext() {
+        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);
     }
@@ -2036,6 +2095,31 @@
         return (T) mUserData.get(key);
     }
 
+    /** Logs an error message to the error log of the host application. */
+    public void error(@NonNull String message, @NonNull String... details) {
+        mLayoutlibCallback.error(message, details);
+    }
+
+    /** Logs an error message to the error log of the host application. */
+    public void error(@NonNull String message, @Nullable Throwable t) {
+        mLayoutlibCallback.error(message, t);
+    }
+
+    /** Logs an error message to the error log of the host application. */
+    public void error(@NonNull Throwable t) {
+        mLayoutlibCallback.error(t);
+    }
+
+    /** Logs a warning to the log of the host application. */
+    public void warn(@NonNull String message, @Nullable Throwable t) {
+        mLayoutlibCallback.warn(message, t);
+    }
+
+    /** Logs a warning to the log of the host application. */
+    public void warn(@NonNull Throwable t) {
+        mLayoutlibCallback.warn(t);
+    }
+
     /**
      * No two Key instances are considered equal.
      *
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index 0b5f14c..584e8c2 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -95,6 +95,21 @@
     }
 
     @Override
+    public void setPowerBoost(int boost, int durationMs) {
+        // pass for now.
+    }
+
+    @Override
+    public void setPowerMode(int mode, boolean enabled) {
+        // pass for now.
+    }
+
+    @Override
+    public boolean setPowerModeChecked(int mode, boolean enabled) {
+        return false;
+    }
+
+    @Override
     public void crash(String arg0) throws RemoteException {
         // pass for now.
     }
@@ -110,6 +125,11 @@
     }
 
     @Override
+    public float getBrightnessConstraint(int constraint) {
+        return PowerManager.BRIGHTNESS_MAX;
+    }
+
+    @Override
     public void reboot(boolean confirm, String reason, boolean wait) {
         // pass for now.
     }
@@ -202,6 +222,26 @@
     }
 
     @Override
+    public boolean isAmbientDisplayAvailable() {
+        return false;
+    }
+
+    @Override
+    public void suppressAmbientDisplay(String token, boolean suppress) {
+        // pass for now
+    }
+
+    @Override
+    public boolean isAmbientDisplaySuppressedForToken(String token) {
+        return false;
+    }
+
+    @Override
+    public boolean isAmbientDisplaySuppressed() {
+        return false;
+    }
+
+    @Override
     public boolean forceSuspend() {
         return false;
     }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeSharedPreferences.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeSharedPreferences.java
index 132ff2f..2d84b25 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeSharedPreferences.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeSharedPreferences.java
@@ -23,7 +23,7 @@
 
 /**
  * An empty shared preferences implementation which doesn't store anything. It always returns
- * null, 0 or false.
+ * the default value, null or false.
  */
 public class BridgeSharedPreferences implements SharedPreferences {
     private Editor mEditor;
@@ -35,32 +35,32 @@
 
     @Override
     public String getString(String key, String defValue) {
-        return null;
+        return defValue;
     }
 
     @Override
     public Set<String> getStringSet(String key, Set<String> defValues) {
-        return null;
+        return defValues;
     }
 
     @Override
     public int getInt(String key, int defValue) {
-        return 0;
+        return defValue;
     }
 
     @Override
     public long getLong(String key, long defValue) {
-        return 0;
+        return defValue;
     }
 
     @Override
     public float getFloat(String key, float defValue) {
-        return 0;
+        return defValue;
     }
 
     @Override
     public boolean getBoolean(String key, boolean defValue) {
-        return false;
+        return defValue;
     }
 
     @Override
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeThermalService.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeThermalService.java
new file mode 100644
index 0000000..5fe116e
--- /dev/null
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeThermalService.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 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.layoutlib.bridge.android;
+
+import android.os.CoolingDevice;
+import android.os.IBinder;
+import android.os.IThermalEventListener;
+import android.os.IThermalStatusListener;
+import android.os.IThermalService;
+import android.os.Temperature;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fake implementation of IThermalService
+ */
+public class BridgeThermalService implements IThermalService {
+
+    @Override
+    public IBinder asBinder() {
+        return null;
+    }
+
+    @Override
+    public boolean registerThermalEventListener(IThermalEventListener listener) {
+        return false;
+    }
+
+    @Override
+    public boolean registerThermalEventListenerWithType(IThermalEventListener listener, int type) {
+        return false;
+    }
+
+    @Override
+    public boolean unregisterThermalEventListener(IThermalEventListener listener) {
+        return false;
+    }
+
+    @Override
+    public Temperature[] getCurrentTemperatures() {
+        return new Temperature[0];
+    }
+
+    @Override
+    public Temperature[] getCurrentTemperaturesWithType(int type) {
+        return new Temperature[0];
+    }
+
+    @Override
+    public boolean registerThermalStatusListener(IThermalStatusListener listener) {
+        return false;
+    }
+
+    @Override
+    public boolean unregisterThermalStatusListener(IThermalStatusListener listener) {
+        return false;
+    }
+
+    @Override
+    public int getCurrentThermalStatus() {
+        return 0;
+    }
+
+    @Override
+    public CoolingDevice[] getCurrentCoolingDevices() {
+        return new CoolingDevice[0];
+    }
+
+    @Override
+    public CoolingDevice[] getCurrentCoolingDevicesWithType(int type) {
+        return new CoolingDevice[0];
+    }
+
+    @Override
+    public float getThermalHeadroom(int forecastSeconds) {
+        return Float.NaN;
+    }
+}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java b/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
index a2f5976..4eaf352 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
@@ -87,6 +87,19 @@
     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 =
+            new Key<>("enableLayoutValidator", Boolean.class);
+
+    /**
+     * Enables image-related validation checks within layout validation.
+     * {@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);
+
     // Disallow instances.
     private RenderParamsFlags() {}
 }
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 0c2ef8b..5f785f5 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java
@@ -62,7 +62,7 @@
             invoke(getMethod(view.getClass(), "setTitle", CharSequence.class), view, title);
         } catch (ReflectionException e) {
             Bridge.getLog().warning(LayoutLog.TAG_INFO,
-                    "Error occurred while trying to set title.", e);
+                    "Error occurred while trying to set title.", null, e);
         }
     }
 }
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 1124a21..c41bcae 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java
@@ -60,7 +60,7 @@
                     gravity);
         } catch (ReflectionException e) {
             Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to open navigation drawer",
-                    getCause(e), null);
+                    getCause(e), null, null);
         }
     }
 }
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 da2d3ee..c77cb57 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java
@@ -60,7 +60,7 @@
 
         if (fragmentManager == null) {
             Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                    "Unable to find FragmentManager.", null);
+                    "Unable to find FragmentManager.", null, null);
             return;
         }
 
@@ -71,7 +71,7 @@
         } catch (ReflectionException e) {
             Throwable cause = getCause(e);
             Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                    "Error occurred while trying to setup FragmentTabHost.", cause, null);
+                    "Error occurred while trying to setup FragmentTabHost.", cause, null, null);
         }
     }
 }
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 894937c..6fc8009 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
@@ -73,7 +73,7 @@
         } catch (ReflectionException e) {
             Throwable cause = getCause(e);
             Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                    "Error occurred while trying to setup RecyclerView.", cause, null);
+                    "Error occurred while trying to setup RecyclerView.", cause, null, null);
         }
     }
 
diff --git a/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java b/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
index bf2b527..3cd95be 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
@@ -15,21 +15,35 @@
  */
 package com.android.layoutlib.bridge.android.view;
 
+import android.app.ResourcesManager;
+import android.content.Context;
+import android.graphics.Insets;
+import android.graphics.Point;
+import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.RemoteException;
 import android.util.DisplayMetrics;
+import android.util.Size;
 import android.view.Display;
 import android.view.Display.Mode;
 import android.view.DisplayAdjustments;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
+import android.view.InsetsState;
 import android.view.View;
+import android.view.WindowInsets;
 import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.WindowMetrics;
 
 public class WindowManagerImpl implements WindowManager {
 
+    private final Context mContext;
     private final DisplayMetrics mMetrics;
     private final Display mDisplay;
 
-    public WindowManagerImpl(DisplayMetrics metrics) {
+    public WindowManagerImpl(Context context, DisplayMetrics metrics) {
+        mContext = context;
         mMetrics = metrics;
 
         DisplayInfo info = new DisplayInfo();
@@ -38,6 +52,7 @@
         info.supportedModes = new Mode[] {
                 new Mode(0, mMetrics.widthPixels, mMetrics.heightPixels, 60f)
         };
+        info.logicalDensityDpi = mMetrics.densityDpi;
         mDisplay = new Display(null, Display.DEFAULT_DISPLAY, info,
                 DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
     }
@@ -93,4 +108,47 @@
     public void setShouldShowIme(int displayId, boolean shouldShow) {
         // pass
     }
+
+    @Override
+    public WindowMetrics getCurrentWindowMetrics() {
+        final Rect bound = getCurrentBounds(mContext);
+
+        return new WindowMetrics(bound, computeWindowInsets());
+    }
+
+    private static Rect getCurrentBounds(Context context) {
+        synchronized (ResourcesManager.getInstance()) {
+            return context.getResources().getConfiguration().windowConfiguration.getBounds();
+        }
+    }
+
+    @Override
+    public WindowMetrics getMaximumWindowMetrics() {
+        return new WindowMetrics(getMaximumBounds(), computeWindowInsets());
+    }
+
+    private Rect getMaximumBounds() {
+        final Point displaySize = new Point();
+        mDisplay.getRealSize(displaySize);
+        return new Rect(0, 0, displaySize.x, displaySize.y);
+    }
+
+    private WindowInsets computeWindowInsets() {
+        try {
+            final Rect systemWindowInsets = new Rect();
+            final Rect stableInsets = new Rect();
+            final DisplayCutout.ParcelableWrapper displayCutout =
+                    new DisplayCutout.ParcelableWrapper();
+            final InsetsState insetsState = new InsetsState();
+            WindowManagerGlobal.getWindowManagerService().getWindowInsets(
+                    new WindowManager.LayoutParams(), mContext.getDisplayId(), systemWindowInsets,
+                    stableInsets, displayCutout, insetsState);
+            return new WindowInsets.Builder()
+                    .setSystemWindowInsets(Insets.of(systemWindowInsets))
+                    .setStableInsets(Insets.of(stableInsets))
+                    .setDisplayCutout(displayCutout.get()).build();
+        } catch (RemoteException ignore) {
+        }
+        return null;
+    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java b/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
index ae218ee..ab88f15 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
@@ -27,6 +27,7 @@
 import com.android.layoutlib.bridge.impl.ResourceHelper;
 import com.android.resources.ResourceType;
 
+import android.R.id;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -36,6 +37,7 @@
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.View;
+import android.view.Window;
 import android.widget.FrameLayout;
 
 import java.lang.reflect.Field;
@@ -67,8 +69,20 @@
         int contentRootId = context.getResourceId(resource, 0);
         View contentView = getDecorContent().findViewById(contentRootId);
 
+        // We need to change the id of the content view, but before it needs to have
+        // been assigned inside the action bar, which happens when calling setWindowCallback.
+        ResourceReference parentResource = context.createAppCompatResourceReference(
+                ResourceType.ID, "decor_content_parent");
+        int parentId = context.getResourceId(parentResource, 0);
+        View parentView = getDecorContent().findViewById(parentId);
+        if (parentView != null) {
+            invoke(getMethod(parentView.getClass(), "setWindowCallback",
+                    Window.Callback.class), parentView, (Object) null);
+        }
+
         if (contentView != null) {
             assert contentView instanceof FrameLayout;
+            contentView.setId(id.content);
             setContentRoot((FrameLayout) contentView);
         } else {
             // Something went wrong. Create a new FrameLayout in the enclosing layout.
@@ -77,6 +91,7 @@
             if (mEnclosingLayout != null) {
                 mEnclosingLayout.addView(contentRoot);
             }
+            contentRoot.setId(id.content);
             setContentRoot(contentRoot);
         }
         try {
@@ -101,9 +116,10 @@
                     mWindowDecorActionBar == null ? null : mWindowDecorActionBar.getClass();
             inflateMenus();
             setupActionBar();
+            getContentRoot().setId(id.content);
         } catch (Exception e) {
             Bridge.getLog().warning(LayoutLog.TAG_BROKEN,
-                    "Failed to load AppCompat ActionBar with unknown error.", e);
+                    "Failed to load AppCompat ActionBar with unknown error.", null, e);
         }
     }
 
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/bridge/src/com/android/layoutlib/bridge/bars/Config.java
index 641ca4e..7813844 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, O)) {
-            return "8:00";
+        if (isGreaterOrEqual(platformVersion, Q)) {
+            return "10:00";
         }
         if (platformVersion < GINGERBREAD) {
             return "2:20";
@@ -119,9 +119,21 @@
         if (platformVersion < N) {
             return "6:00";
         }
-        if (platformVersion < O) {
+        if (platformVersion < N_MR1) {
             return "7:00";
         }
+        if (platformVersion < O) {
+            return "7:10";
+        }
+        if (platformVersion < O_MR1) {
+            return "8:00";
+        }
+        if (platformVersion < P) {
+            return "8:10";
+        }
+        if (platformVersion < Q) {
+            return "9: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 332a861..3a5f633 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -259,7 +259,7 @@
                     Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
                             "Theme attribute @android:" + attr +
                                     " does not reference a color, instead is '" +
-                                    resource.getValue() + "'.", resource);
+                                    resource.getValue() + "'.", null, resource);
                 }
             }
         }
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index c653805..f58443a 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -91,14 +91,13 @@
 
         if (icons.size() != 2 || clockView == null) {
             Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to initialize statusbar", null,
-                    null);
+                    null, null);
             return;
         }
 
         // Cannot access the inside items through id because no R.id values have been
         // created for them.
         // We do know the order though.
-        // 0 is the spacer
         loadIcon(icons.get(0), "stat_sys_wifi_signal_4_fully."
                         + Config.getWifiIconType(simulatedPlatformVersion), density);
         loadIcon(icons.get(1), "stat_sys_battery_100.png", density);
@@ -128,10 +127,10 @@
                         Drawable.createFromXml(mContext.getResources(), parser));
             } catch (XmlPullParserException e) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to draw wifi icon", e,
-                        null);
+                        null, null);
             } catch (IOException e) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to draw wifi icon", e,
-                        null);
+                        null, null);
             }
         }
 
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
index 2ad7037..959ba77 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
@@ -16,6 +16,11 @@
 
 package com.android.layoutlib.bridge.impl;
 
+import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
+import static java.awt.image.BufferedImage.TYPE_INT_RGB;
+import static java.lang.Math.min;
+import static java.lang.Math.max;
+
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 
@@ -61,6 +66,7 @@
  * ({@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;
@@ -302,8 +308,8 @@
                     baseLayer.getImage().getWidth(),
                     baseLayer.getImage().getHeight(),
                     (mFlags & Canvas.HAS_ALPHA_LAYER_SAVE_FLAG) != 0 ?
-                            BufferedImage.TYPE_INT_ARGB :
-                                BufferedImage.TYPE_INT_RGB);
+                            TYPE_INT_ARGB :
+                                TYPE_INT_RGB);
 
             // create a graphics for it so that drawing can be done.
             Graphics2D layerGraphics = layerImage.createGraphics();
@@ -330,7 +336,7 @@
                 int h = mLayerBounds.height();
                 for (int i = 0 ; i < mLayers.size() - 1 ; i++) {
                     Layer layer = mLayers.get(i);
-                    BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
+                    BufferedImage image = new BufferedImage(w, h, TYPE_INT_ARGB);
                     Graphics2D graphics = image.createGraphics();
                     graphics.drawImage(layer.getImage(),
                             0, 0, w, h,
@@ -502,8 +508,6 @@
             area = Region_Delegate.combineShapes(getClip(), shape, regionOp);
         }
 
-        assert area != null;
-
         if (mLayers.size() > 0) {
             if (area != null) {
                 for (Layer layer : mLayers) {
@@ -600,6 +604,38 @@
         }
     }
 
+    /**
+     * 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();
@@ -618,22 +654,31 @@
 
             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());
             }
 
-            // b/63692596:
-            // Don't use the size of clip bound because it may be smaller than original size.
-            // Which makes Vector Drawable pixelized.
-            int width = layer.getImage().getWidth();
-            int height = layer.getImage().getHeight();
-
             // Create a temporary image to which the color filter will be applied.
-            BufferedImage image = new BufferedImage(width, height,
-                    BufferedImage.TYPE_INT_ARGB);
+            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(
@@ -643,23 +688,23 @@
             // get a Graphics2D object configured with the drawing parameters, but no shader.
             Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint,
                     true /*compositeOnly*/, forceMode);
-            configuredGraphics.setTransform(new AffineTransform());
+            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(originalGraphics.getTransform());
+                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(new AffineTransform());
-                filter.applyFilter(imageGraphics, width, height);
+                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, 0, 0, null);
+                configuredGraphics.drawImage(image, imgRect.x, imgRect.y, null);
                 layer.change();
             } finally {
                 // dispose Graphics2D objects
@@ -872,7 +917,7 @@
             bounds.x += latestTransform.getTranslateX() - originalTransform.getTranslateX();
             bounds.y += latestTransform.getTranslateY() - originalTransform.getTranslateY();
         } catch (NoninvertibleTransformException e) {
-            Bridge.getLog().warning(null, "Non invertible transformation", null);
+            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 534bc5a..45accb9 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
@@ -36,6 +36,7 @@
 import com.android.resources.ResourceType;
 import com.android.resources.ScreenOrientation;
 
+import android.R.id;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Color;
@@ -224,6 +225,7 @@
             params.addRule(RelativeLayout.BELOW, below);
         }
         contentRoot.setLayoutParams(params);
+        contentRoot.setId(id.content);
         return contentRoot;
     }
 
@@ -423,7 +425,7 @@
         }
 
         private void findBackground() {
-            if (!mParams.isBgColorOverridden()) {
+            if (!mParams.isTransparentBackground()) {
                 mWindowBackground = mResources.findItemInTheme(
                         BridgeContext.createFrameworkAttrReference(ATTR_WINDOW_BACKGROUND));
                 mWindowBackground = mResources.resolveResValue(mWindowBackground);
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java b/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
index 70e2eb1..dcbd272 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
@@ -48,7 +48,7 @@
             return PorterDuff.intToMode(porterDuffMode);
         }
         Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                String.format("Unknown PorterDuff.Mode: %1$d", porterDuffMode), null);
+                String.format("Unknown PorterDuff.Mode: %1$d", porterDuffMode), null, null);
         assert false;
         return Mode.SRC_OVER;
     }
@@ -99,7 +99,7 @@
             default:
                 Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
                         String.format("Unsupported PorterDuff Mode: %1$s", mode.name()),
-                        null, null /*data*/);
+                        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 51dcae9..b24c383 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -23,6 +23,7 @@
 import com.android.ide.common.rendering.api.Result;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.RenderParamsFlags;
 import com.android.resources.Density;
 import com.android.resources.ScreenOrientation;
 import com.android.resources.ScreenRound;
@@ -30,6 +31,7 @@
 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;
@@ -127,7 +129,9 @@
         // build the context
         mContext = new BridgeContext(mParams.getProjectKey(), metrics, resources,
                 mParams.getAssets(), mParams.getLayoutlibCallback(), getConfiguration(mParams),
-                mParams.getTargetSdkVersion(), mParams.isRtlSupported());
+                mParams.getTargetSdkVersion(), mParams.isRtlSupported(),
+                Boolean.TRUE.equals(mParams.getFlag(RenderParamsFlags.FLAG_ENABLE_SHADOW)),
+                Boolean.TRUE.equals(mParams.getFlag(RenderParamsFlags.FLAG_RENDER_HIGH_QUALITY_SHADOW)));
 
         setUp();
 
@@ -404,6 +408,8 @@
         String locale = params.getLocale();
         if (locale != null && !locale.isEmpty()) config.locale = new Locale(locale);
 
+        config.fontScale = params.getFontScale();
+
         // TODO: fill in more config info.
 
         return config;
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
index 35ec77b..dc05951 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
@@ -162,12 +162,18 @@
 
         // Draw.
         content.draw(canvas);
+
+        // Detach root from window after draw.
+        AttachInfo_Accessor.detachFromWindow(content);
+
         return image;
     }
 
     @NonNull
     protected BufferedImage getImage(int w, int h) {
-        BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
+        BufferedImage image = new BufferedImage(w > 0 ? w : 1,
+                h > 0 ? h : 1,
+                BufferedImage.TYPE_INT_ARGB);
         Graphics2D gc = image.createGraphics();
         gc.setComposite(AlphaComposite.Src);
 
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 4f47773..98299c3 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -27,6 +27,7 @@
 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.SessionParams.RenderingMode.SizeAction;
 import com.android.ide.common.rendering.api.ViewInfo;
 import com.android.ide.common.rendering.api.ViewType;
 import com.android.internal.view.menu.ActionMenuItemView;
@@ -46,6 +47,10 @@
 import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
 import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
 import com.android.tools.layoutlib.java.System_Delegate;
+import com.android.tools.idea.validator.ValidatorResult;
+import com.android.tools.idea.validator.LayoutValidator;
+import com.android.tools.idea.validator.ValidatorResult;
+import com.android.tools.idea.validator.ValidatorResult.Builder;
 import com.android.util.Pair;
 
 import android.annotation.NonNull;
@@ -126,6 +131,7 @@
     private List<ViewInfo> mSystemViewInfoList;
     private Layout.Builder mLayoutBuilder;
     private boolean mNewRenderSize;
+    @Nullable private ValidatorResult mValidatorResult = null;
 
     private static final class PostInflateException extends Exception {
         private static final long serialVersionUID = 1L;
@@ -236,19 +242,19 @@
             // now measure the content only using UNSPECIFIED (where applicable, based on
             // the rendering mode). This will give us the size the content needs.
             @SuppressWarnings("deprecation")
-            Pair<Integer, Integer> result = measureView(
+            Pair<Integer, Integer> neededMeasure = measureView(
                     mContentRoot, mContentRoot.getChildAt(0),
                     mMeasuredScreenWidth, widthMeasureSpecMode,
                     mMeasuredScreenHeight, heightMeasureSpecMode);
+            int neededWidth = neededMeasure.getFirst();
+            int neededHeight = neededMeasure.getSecond();
 
             // If measuredView is not null, exactMeasure nor result will be null.
-            assert exactMeasure != null;
-            assert result != null;
+            assert (exactMeasure != null && neededMeasure != null) || measuredView == null;
 
             // now look at the difference and add what is needed.
-            if (renderingMode.isHorizExpand()) {
+            if (renderingMode.getHorizAction() == SizeAction.EXPAND) {
                 int measuredWidth = exactMeasure.getFirst();
-                int neededWidth = result.getFirst();
                 if (neededWidth > measuredWidth) {
                     mMeasuredScreenWidth += neededWidth - measuredWidth;
                 }
@@ -257,11 +263,12 @@
                     // expand to match.
                     mMeasuredScreenWidth = measuredWidth;
                 }
+            } else if (renderingMode.getHorizAction() == SizeAction.SHRINK) {
+                mMeasuredScreenWidth = neededWidth;
             }
 
-            if (renderingMode.isVertExpand()) {
+            if (renderingMode.getVertAction() == SizeAction.EXPAND) {
                 int measuredHeight = exactMeasure.getSecond();
-                int neededHeight = result.getSecond();
                 if (neededHeight > measuredHeight) {
                     mMeasuredScreenHeight += neededHeight - measuredHeight;
                 }
@@ -270,6 +277,8 @@
                     // expand to match.
                     mMeasuredScreenHeight = measuredHeight;
                 }
+            } else if (renderingMode.getVertAction() == SizeAction.SHRINK) {
+                mMeasuredScreenHeight = neededHeight;
             }
         }
     }
@@ -296,21 +305,17 @@
                 if (!params.isRtlSupported()) {
                     Bridge.getLog().warning(LayoutLog.TAG_RTL_NOT_ENABLED,
                             "You are using a right-to-left " +
-                                    "(RTL) locale but RTL is not enabled", null);
+                                    "(RTL) locale but RTL is not enabled", null, null);
                 } else if (params.getSimulatedPlatformVersion() !=0 &&
                         params.getSimulatedPlatformVersion() < 17) {
                     // This will render ok because we are using the latest layoutlib but at least
                     // warn the user that this might fail in a real device.
                     Bridge.getLog().warning(LayoutLog.TAG_RTL_NOT_SUPPORTED, "You are using a " +
                             "right-to-left " +
-                            "(RTL) locale but RTL is not supported for API level < 17", null);
+                            "(RTL) locale but RTL is not supported for API level < 17", null, null);
                 }
             }
 
-            // Sets the project callback (custom view loader) to the fragment delegate so that
-            // it can instantiate the custom Fragment.
-            Fragment_Delegate.setLayoutlibCallback(params.getLayoutlibCallback());
-
             String rootTag = params.getFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG);
             boolean isPreference = "PreferenceScreen".equals(rootTag) ||
                     SupportPreferencesUtil.isSupportRootTag(rootTag);
@@ -318,10 +323,10 @@
             if (isPreference) {
                 // First try to use the support library inflater. If something fails, fallback
                 // to the system preference inflater.
-                view = SupportPreferencesUtil.inflatePreference(getContext(), mBlockParser,
+                view = SupportPreferencesUtil.inflatePreference(context, mBlockParser,
                         mContentRoot);
                 if (view == null) {
-                    view = Preference_Delegate.inflatePreference(getContext(), mBlockParser,
+                    view = Preference_Delegate.inflatePreference(context, mBlockParser,
                             mContentRoot);
                 }
             } else {
@@ -331,8 +336,6 @@
             // done with the parser, pop it.
             context.popParser();
 
-            Fragment_Delegate.setLayoutlibCallback(null);
-
             // set the AttachInfo on the root view.
             AttachInfo_Accessor.setAttachInfo(mViewRoot);
 
@@ -571,6 +574,24 @@
                     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));
+
+                if (enableLayoutValidation && !getViewInfos().isEmpty()) {
+                    BufferedImage imageToPass =
+                            enableLayoutValidationImageCheck ? getImage() : null;
+                    ValidatorResult validatorResult =
+                            LayoutValidator.validate(((View) getViewInfos().get(0).getViewObject()), imageToPass);
+                    setValidatorResult(validatorResult);
+                }
+            } catch (Throwable e) {
+                ValidatorResult.Builder builder = new Builder();
+                builder.mMetric.mErrorMessage = e.getMessage();
+                setValidatorResult(builder.build());
+            }
+
             // success!
             return renderResult;
         } catch (Throwable e) {
@@ -1132,6 +1153,15 @@
         return getContext().getDefaultNamespacedStyles();
     }
 
+    @Nullable
+    public ValidatorResult getValidatorResult() {
+        return mValidatorResult;
+    }
+
+    public void setValidatorResult(ValidatorResult result) {
+        mValidatorResult = result;
+    }
+
     public void setScene(RenderSession session) {
         mScene = session;
     }
@@ -1141,33 +1171,37 @@
     }
 
     public void dispose() {
-        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;
-        }
-        AttachInfo_Accessor.detachFromWindow(mViewRoot);
-        if (mCanvas != null) {
-            mCanvas.release();
-            mCanvas = null;
-        }
-        if (mViewInfoList != null) {
-            mViewInfoList.clear();
-        }
-        if (mSystemViewInfoList != null) {
-            mSystemViewInfoList.clear();
-        }
-        mImage = null;
-        mViewRoot = null;
-        mContentRoot = null;
-        NinePatch_Delegate.clearCache();
+        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;
+            }
+            AttachInfo_Accessor.detachFromWindow(mViewRoot);
+            if (mCanvas != null) {
+                mCanvas.release();
+                mCanvas = null;
+            }
+            if (mViewInfoList != null) {
+                mViewInfoList.clear();
+            }
+            if (mSystemViewInfoList != null) {
+                mSystemViewInfoList.clear();
+            }
+            mImage = null;
+            mViewRoot = null;
+            mContentRoot = null;
+            NinePatch_Delegate.clearCache();
 
-        if (createdLooper) {
-            Choreographer_Delegate.dispose();
-            Bridge.cleanupThread();
+            if (createdLooper) {
+                Choreographer_Delegate.dispose();
+                Bridge.cleanupThread();
+            }
+        } catch (Throwable t) {
+            getContext().error("Error while disposing a RenderSession", t);
         }
     }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index d3b934f..8e2ecd6 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -203,13 +203,13 @@
             }
         } catch (XmlPullParserException e) {
             Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                    "Failed to configure parser for " + value, e, null /*data*/);
+                    "Failed to configure parser for " + value, e, null,null /*data*/);
             // we'll return null below.
         } catch (Exception e) {
             // this is an error and not warning since the file existence is
             // checked before attempting to parse it.
             Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
-                    "Failed to parse file " + value, e, null /*data*/);
+                    "Failed to parse file " + value, e, null, null /*data*/);
 
             return null;
         }
@@ -333,7 +333,7 @@
             } catch (IOException e) {
                 // failed to read the file, we'll return null below.
                 Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
-                        "Failed to load " + stringValue, e, null /*data*/);
+                        "Failed to load " + stringValue, e, null, null /*data*/);
             }
 
             return null;
@@ -349,7 +349,8 @@
                         context.putUserData(KEY_GET_DRAWABLE, visitedValues);
                     }
                     if (!visitedValues.add(value)) {
-                        Bridge.getLog().error(null, "Cyclic dependency in " + stringValue, null);
+                        Bridge.getLog().error(null, "Cyclic dependency in " + stringValue, null,
+                                null);
                         return null;
                     }
 
@@ -364,7 +365,7 @@
                 // this is an error and not warning since the file existence is checked before
                 // attempting to parse it.
                 Bridge.getLog().error(null, "Failed to parse file " + stringValue, e,
-                        null /*data*/);
+                        null, null /*data*/);
             }
 
             return null;
@@ -393,7 +394,7 @@
                 } catch (IOException e) {
                     // we'll return null below
                     Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
-                            "Failed to load " + stringValue, e, null /*data*/);
+                            "Failed to load " + stringValue, e, null, null /*data*/);
                 }
             }
         }
@@ -636,7 +637,7 @@
                                 String.format(
                                         "Dimension \"%1$s\" in attribute \"%2$s\" is missing unit!",
                                         value, attribute),
-                                null);
+                                null, null);
                     }
                     return true;
                 }
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 f830b07..76c5942 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
@@ -95,7 +95,7 @@
                             if (value.getClass() != ViewAttribute.TEXT.getAttributeClass()) {
                                 Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format(
                                         "Wrong Adapter Item value class for TEXT. Expected String, got %s",
-                                        value.getClass().getName()), null);
+                                        value.getClass().getName()), null, null);
                             } else {
                                 tv.setText((String) value);
                             }
@@ -115,7 +115,7 @@
                             if (value.getClass() != ViewAttribute.IS_CHECKED.getAttributeClass()) {
                                 Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format(
                                         "Wrong Adapter Item value class for IS_CHECKED. Expected Boolean, got %s",
-                                        value.getClass().getName()), null);
+                                        value.getClass().getName()), null, null);
                             } else {
                                 cb.setChecked((Boolean) value);
                             }
@@ -135,7 +135,7 @@
                             if (value.getClass() != ViewAttribute.SRC.getAttributeClass()) {
                                 Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format(
                                         "Wrong Adapter Item value class for SRC. Expected Boolean, got %s",
-                                        value.getClass().getName()), null);
+                                        value.getClass().getName()), null, null);
                             } else {
                                 // FIXME
                             }
diff --git a/bridge/src/com/android/layoutlib/bridge/libcore/util/Cleaner.java b/bridge/src/com/android/layoutlib/bridge/libcore/util/Cleaner.java
new file mode 100644
index 0000000..7c95e64
--- /dev/null
+++ b/bridge/src/com/android/layoutlib/bridge/libcore/util/Cleaner.java
@@ -0,0 +1,52 @@
+/*
+ * 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.libcore.util;
+
+import java.lang.ref.Cleaner.Cleanable;
+
+public class Cleaner {
+    private final Cleanable mCleanable;
+
+    private Cleaner(Cleanable cleanable) {
+        mCleanable = cleanable;
+    }
+
+    /**
+     * Creates a new cleaner.
+     *
+     * @param  ob the referent object to be cleaned
+     * @param  thunk
+     *         The cleanup code to be run when the cleaner is invoked.  The
+     *         cleanup code is run directly from the reference-handler thread,
+     *         so it should be as simple and straightforward as possible.
+     *
+     * @return  The new cleaner
+     */
+    public static Cleaner create(Object ob, Runnable thunk) {
+        if (thunk == null)
+            return null;
+        java.lang.ref.Cleaner cleaner = java.lang.ref.Cleaner.create();
+        return new Cleaner(cleaner.register(ob, thunk));
+    }
+
+    /**
+     * Runs this cleaner, if it has not been run before.
+     */
+    public void clean() {
+        mCleanable.clean();
+    }
+}
diff --git a/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java b/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java
index 96a6e34..d84b411 100644
--- a/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java
+++ b/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java
@@ -52,7 +52,7 @@
                 try {
                     XmlPullParser parser = ParserFactory.create(stream, layoutName);
 
-                    // TODO(namespaces): does the namespace matter here?
+                    // TODO(b/156609434): does the namespace matter here?
                     return new BridgeXmlBlockParser(parser, context, PRIVATE_LAYOUTLIB_NAMESPACE);
                 } catch (XmlPullParserException e) {
                     // Should not happen as the resource is bundled with the jar, and  ParserFactory should
diff --git a/bridge/src/libcore/icu/ICU_Delegate.java b/bridge/src/libcore/icu/ICU_Delegate.java
index fb547df..708cf62 100644
--- a/bridge/src/libcore/icu/ICU_Delegate.java
+++ b/bridge/src/libcore/icu/ICU_Delegate.java
@@ -16,13 +16,10 @@
 
 package libcore.icu;
 
-import android.icu.util.TimeZone;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.icu.text.DateTimePatternGenerator;
-import android.icu.util.Currency;
 import android.icu.util.ULocale;
-import android.icu.util.VersionInfo;
 
 import java.util.Locale;
 
@@ -34,19 +31,6 @@
  *
  */
 public class ICU_Delegate {
-
-    // --- Java delegates
-
-    @LayoutlibDelegate
-    /*package*/ static String toLowerCase(String s, String localeName) {
-        return s.toLowerCase();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static String toUpperCase(String s, String localeName) {
-        return s.toUpperCase();
-    }
-
     // --- Native methods accessing ICU's database.
 
     @LayoutlibDelegate
@@ -56,102 +40,16 @@
     }
 
     @LayoutlibDelegate
-    @SuppressWarnings("deprecation")
-    /*package*/ static String getCldrVersion() {
-        return VersionInfo.ICU_DATA_VERSION.toString();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static String getIcuVersion() {
-        return VersionInfo.ICU_VERSION.toString();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static String getUnicodeVersion() {
-        return VersionInfo.UNICODE_7_0.toString();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static String[] getAvailableBreakIteratorLocalesNative() {
-        return new String[0];
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static String[] getAvailableCalendarLocalesNative() {
-        return new String[0];
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static String[] getAvailableCollatorLocalesNative() {
-        return new String[0];
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static String[] getAvailableDateFormatLocalesNative() {
-        return new String[0];
-    }
-
-    @LayoutlibDelegate
     /*package*/ static String[] getAvailableLocalesNative() {
         return new String[0];
     }
 
     @LayoutlibDelegate
-    /*package*/ static String[] getAvailableNumberFormatLocalesNative() {
-        return new String[0];
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static String[] getAvailableCurrencyCodes() {
-        return new String[0];
-    }
-
-    @LayoutlibDelegate
     /*package*/ static String getCurrencyCode(String locale) {
         return "";
     }
 
     @LayoutlibDelegate
-    /*package*/ static String getCurrencyDisplayName(String locale, String currencyCode) {
-        return "";
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int getCurrencyFractionDigits(String currencyCode) {
-        return 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int getCurrencyNumericCode(String currencyCode) {
-        return Currency.getInstance(currencyCode).getNumericCode();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static String getCurrencySymbol(String locale, String currencyCode) {
-        return "";
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static String getDisplayCountryNative(String countryCode, String locale) {
-        return "";
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static String getDisplayLanguageNative(String languageCode, String locale) {
-        return "";
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static String getDisplayVariantNative(String variantCode, String locale) {
-        return "";
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static String getDisplayScriptNative(String variantCode, String locale) {
-        return "";
-    }
-
-    @LayoutlibDelegate
     /*package*/ static String getISO3Country(String locale) {
         return "";
     }
@@ -162,11 +60,6 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static String addLikelySubtags(String locale) {
-        return "";
-    }
-
-    @LayoutlibDelegate
     /*package*/ static String getScript(String locale) {
         return "";
     }
@@ -190,9 +83,4 @@
     /*package*/ static String getDefaultLocale() {
         return ICU.getDefaultLocale();
     }
-
-    @LayoutlibDelegate
-    /*package*/ static String getTZDataVersion() {
-        return TimeZone.getTZDataVersion();
-    }
 }
diff --git a/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java b/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
index c739c65..141c728 100644
--- a/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
+++ b/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
@@ -19,10 +19,6 @@
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
-import libcore.util.NativeAllocationRegistry.CleanerRunner;
-import libcore.util.NativeAllocationRegistry.CleanerThunk;
-import sun.misc.Cleaner;
-
 /**
  * Delegate implementing the native methods of {@link NativeAllocationRegistry}
  *
@@ -67,29 +63,7 @@
         // This allows the DelegateManager to dispose objects without waiting
         // for an explicit call when the referent does not exist anymore.
         sManager.markAsNativeAllocation(referent, nativePtr);
-        if (referent == null) {
-            throw new IllegalArgumentException("referent is null");
-        }
-        if (nativePtr == 0) {
-            throw new IllegalArgumentException("nativePtr is null");
-        }
-
-        CleanerThunk thunk;
-        CleanerRunner result;
-        try {
-            thunk = registry.new CleanerThunk();
-            Cleaner cleaner = Cleaner.create(referent, thunk);
-            result = new CleanerRunner(cleaner);
-            registerNativeAllocation(registry.size);
-        } catch (VirtualMachineError vme /* probably OutOfMemoryError */) {
-            applyFreeFunction(registry.freeFunction, nativePtr);
-            throw vme;
-        } // Other exceptions are impossible.
-        // Enable the cleaner only after we can no longer throw anything, including OOME.
-        thunk.setNativePtr(nativePtr);
-        // Needs to call Reference.reachabilityFence(referent) to ensure that cleaner doesn't
-        // get invoked before we enable it. Unfortunately impossible in OpenJDK 8.
-        return result;
+        return registry.registerNativeAllocation_Original(referent, nativePtr);
     }
 
     @LayoutlibDelegate
diff --git a/bridge/tests/Android.bp b/bridge/tests/Android.bp
index 443d4d3..5c51c68 100644
--- a/bridge/tests/Android.bp
+++ b/bridge/tests/Android.bp
@@ -32,8 +32,12 @@
         "guava",
         "mockito",
         "objenesis",
+        "hamcrest",
+        "trove-prebuilt",
     ],
 
+    required: ["layoutlib"],
+
     // Copy the jar to DIST_DIR for sdk builds
     dist: {
         targets: [
diff --git a/bridge/tests/res/testApp/MyApplication/golden/a11y_test1.png b/bridge/tests/res/testApp/MyApplication/golden/a11y_test1.png
new file mode 100644
index 0000000..067a193
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/a11y_test1.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 1750fa8..2d55e8f 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 7014ddb..eccd6ea 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 3e837da..b98b38b 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 0c09075..44d2161 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 f3fb015..3361cc9 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 0638f32..8d64d47 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 8869159..7559d54 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 e33f92d..072df30 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 915868c..348a0f4 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 28000f1..e4be842 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 f69b128..3a00361 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 a053e8e..f6ff623 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/canvas.png b/bridge/tests/res/testApp/MyApplication/golden/canvas.png
index e3098ab..a267b07 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 16a457d..2d90bc3 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 27bc3d8..5627108 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/expand_horz_layout.png b/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png
index 781d617..0ae96a5 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 5b64d33..2f0b937 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 a8a4c90..b3db624 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 2be5248..e79c860 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 f9e728a..7129944 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 4d5a38c..0e5336f 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 794b546..c5e6e40 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 de3abe6..137eda8 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/large_shadows_test_high_quality.png b/bridge/tests/res/testApp/MyApplication/golden/large_shadows_test_high_quality.png
index 0e2848e..17bb713 100644
--- 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
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 5800b24..bfe7ce0 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 45a63aa..b3ac436 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/normal_layout.png b/bridge/tests/res/testApp/MyApplication/golden/normal_layout.png
new file mode 100644
index 0000000..f1d406e
--- /dev/null
+++ 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 ca51e21..79aa3a8 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 780f8e5..bac8f8e 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/rtl_ltr.png b/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.png
index b200928..1690785 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 cf1a769..27227ea 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_high_quality.png b/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test_high_quality.png
new file mode 100644
index 0000000..716cd08
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test_high_quality.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
index d219a1b..a4ff637 100644
--- 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
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 6331256..21f5ee1 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
index 1639a1a..1e10963 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality.png
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
index c5ba2c9..25ba535 100644
--- 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
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
index c21852c..0492491 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_no_shadow.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_no_shadow.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
new file mode 100644
index 0000000..fe6fc16
--- /dev/null
+++ 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 8ef9d55..01adbd7 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 0ff09d7..4f62a5d 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 614c000..aee806f 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
new file mode 100644
index 0000000..e33047d
--- /dev/null
+++ 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 413fd73..c031e41 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/typed_arrays.png b/bridge/tests/res/testApp/MyApplication/golden/typed_arrays.png
index 372eccd..5e13aac 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/vecitor_drawable_with_tint_in_image_view.png b/bridge/tests/res/testApp/MyApplication/golden/vecitor_drawable_with_tint_in_image_view.png
deleted file mode 100644
index a67ef73..0000000
--- a/bridge/tests/res/testApp/MyApplication/golden/vecitor_drawable_with_tint_in_image_view.png
+++ /dev/null
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 a81fa50..88f72c6 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 56c35f9..5e27b09 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 ea3d2d9..89da005 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
index ebc8a11..6422056 100644
--- 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
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 ad4fed8..a3dd935 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 7e6814c..dc8bd06 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 8866a3d..9e13c62 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 dbfd73a..b9a5001 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 00f0246..7e32c13 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/src/main/res/drawable/eye_chart.png b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/eye_chart.png
new file mode 100644
index 0000000..d195080
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/eye_chart.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/eye_chart_low_contrast.jpg b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/eye_chart_low_contrast.jpg
new file mode 100644
index 0000000..f578c26
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/eye_chart_low_contrast.jpg
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test1.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test1.xml
new file mode 100644
index 0000000..3af420c
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test1.xml
@@ -0,0 +1,45 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <android.widget.TextView
+        android:layout_width="100dp"
+        android:layout_height="100dp"
+        android:background="#000000"
+        android:gravity="center"
+        android:text="Hello!"
+        android:textColor="#000000" />
+
+
+    <Button
+        android:layout_width="10dp"
+        android:layout_height="10dp"
+        android:text="Hello World!!!"
+    />
+    <View
+        android:layout_width="100dp"
+        android:layout_height="100dp"
+        android:background="#000000"
+        android:importantForAccessibility="yes"
+        android:focusable="true"
+        android:clickable="true"
+    />
+</LinearLayout>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_dup_clickable_bounds.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_dup_clickable_bounds.xml
new file mode 100644
index 0000000..cbc04ac
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_dup_clickable_bounds.xml
@@ -0,0 +1,46 @@
+<?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.
+  -->
+
+<!-- Accessibility test with decoy clickable item -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:id="@+id/clickable_wrapper_for_button"
+        android:orientation="horizontal"
+        android:clickable="true">
+
+        <Button
+            android:text="Button in clickable parent"
+            android:id="@+id/button_in_clickable_parent"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:minHeight="48dp"/>
+    </LinearLayout>
+
+    <Button
+        android:text="Button on its own"
+        android:id="@+id/button_on_its_own"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minHeight="48dp"/>
+
+</LinearLayout>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_duplicate_speakable.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_duplicate_speakable.xml
new file mode 100644
index 0000000..676a2d4
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_duplicate_speakable.xml
@@ -0,0 +1,49 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/layout_across_top"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+    <TextView
+        android:id="@+id/first_news_text"
+        android:text="News"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="16dp"
+        android:textSize="32dp"/>
+    <Button
+        android:id="@+id/first_print_button"
+        android:text="Print"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+    <TextView
+        android:id="@+id/first_news_text1"
+        android:text="News"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="16dp"
+        android:textSize="32dp"/>
+    <Button
+        android:id="@+id/first_print_button2"
+        android:text="Print"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+</LinearLayout>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_image_contrast.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_image_contrast.xml
new file mode 100644
index 0000000..0f20c19
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_image_contrast.xml
@@ -0,0 +1,41 @@
+<?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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:gravity="top|center_horizontal"
+              android:orientation="vertical">
+
+    <ImageView
+        android:layout_width="97dp"
+        android:layout_height="wrap_content"
+        android:layout_margin="8dp"
+        android:adjustViewBounds="true"
+        android:src="@drawable/eye_chart"
+        android:contentDescription="Eye Chart"
+        android:clickable="true" />
+
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="8dp"
+        android:layout_marginTop="0dp"
+        android:src="@drawable/eye_chart_low_contrast"
+        android:contentDescription="Eye Chart with Low Contrast"
+        android:clickable="true" />
+</LinearLayout>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_redundant_desc.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_redundant_desc.xml
new file mode 100644
index 0000000..ff99a35
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_redundant_desc.xml
@@ -0,0 +1,52 @@
+<?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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+            <Button
+                android:id="@+id/button_with_label_containing_button"
+                android:text="OK"
+                android:contentDescription="OK button"
+                android:layout_width="wrap_content"
+                android:layout_height="48dp" />
+
+            <TextView
+                android:id="@+id/oath"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:padding="16dp"
+                android:text="I hereby declare, on oath, that I absolutely and entirely renounce and abjure all allegiance
+    and fidelity to any foreign prince, potentate, state, or sovereignty of whom or which I have
+    heretofore been a subject or citizen; that I will support and defend the Constitution and
+    laws of the United States of America against all enemies, foreign and domestic; that I will
+    bear true faith and allegiance to the same; that I will bear arms on behalf of the United
+    States when required by the law; that I will perform noncombatant service in the
+    Armed Forces of the United States when required by the law; that I will perform work of
+    national importance under civilian direction when required by the law; and that I take this
+    obligation freely without any mental reservation or purpose of evasion; so help me God."/>
+
+            <Button
+                android:text="Good Button"
+                android:id="@+id/bottom_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_margin="8dp" />
+
+</LinearLayout>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_speakable_text_present.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_speakable_text_present.xml
new file mode 100644
index 0000000..64c2873
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_speakable_text_present.xml
@@ -0,0 +1,37 @@
+<?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.
+  -->
+
+<!-- Accessibility test with label -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <Button
+        android:id="@+id/button_labeled_by_textview"
+        android:layout_width="48dp"
+        android:layout_height="48dp" />
+
+    <TextView
+        android:id="@+id/textview_label"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:labelFor="@id/button_labeled_by_textview"
+        android:minHeight="48dp"
+        android:text="Howdy"/>
+
+</LinearLayout>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_speakable_text_present2.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_speakable_text_present2.xml
new file mode 100644
index 0000000..c261979
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_speakable_text_present2.xml
@@ -0,0 +1,30 @@
+<?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.
+  -->
+
+<!-- Accessibility test with nothing important error msg -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <Button
+        android:id="@+id/unlabeled_button"
+        android:importantForAccessibility="no"
+        android:layout_width="48dp"
+        android:layout_height="48dp" />
+
+</LinearLayout>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_speakable_text_present3.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_speakable_text_present3.xml
new file mode 100644
index 0000000..6f9741b
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_speakable_text_present3.xml
@@ -0,0 +1,29 @@
+<?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.
+  -->
+
+<!-- Accessibility test with nothing important error msg -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <Button
+        android:id="@+id/unlabeled_button2"
+        android:layout_width="48dp"
+        android:layout_height="48dp" />
+
+</LinearLayout>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_text_contrast.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_text_contrast.xml
new file mode 100644
index 0000000..4d3ac6f
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_text_contrast.xml
@@ -0,0 +1,60 @@
+<?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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:gravity="top|center_horizontal"
+              android:orientation="vertical">
+    <!-- Low contrast, with slightly transparent text -->
+    <Button
+        android:id="@+id/low_contrast_button1"
+        android:text="Bad Button"
+        android:layout_width="wrap_content"
+        android:layout_height="48dp"
+        android:layout_margin="8dp"
+        android:layout_marginLeft="0dp"
+        android:background="@android:color/holo_green_dark"
+        android:textColor="#fe0099cc" />
+    <!-- ATF bypasses transparent views / colors unless image is available. -->
+    <Button
+        android:id="@+id/low_contrast_button2"
+        android:layout_width="wrap_content"
+        android:layout_height="48dp"
+        android:background="@android:color/holo_green_dark"
+        android:text="Button B"
+        android:textColor="@android:color/holo_blue_dark"/>
+    <EditText
+        android:id="@+id/low_contrast_edit_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:ems="10"
+        android:inputType="textPersonName"
+        android:minHeight="48dp"
+        android:text="Edit B"
+        android:background="@android:color/holo_green_dark"
+        android:textColor="@android:color/holo_blue_dark"
+    />
+    <CheckBox
+        android:id="@+id/low_contrast_check_box"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@android:color/holo_green_dark"
+        android:minHeight="48dp"
+        android:text="CheckBox B"
+        android:textColor="@android:color/holo_blue_dark"/>
+</LinearLayout>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_touch_target_size.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_touch_target_size.xml
new file mode 100644
index 0000000..58885ac
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_touch_target_size.xml
@@ -0,0 +1,58 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <Button
+        android:id="@+id/ok_top_left_corner_button1"
+        android:text="E"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+    />
+
+    <Button
+        android:id="@+id/thin_top_button2"
+        android:text="F"
+        android:layout_width="32dp"
+        android:layout_height="48dp"
+        />
+
+    <Button
+        android:id="@+id/ok_top_button3"
+        android:text="G"
+        android:layout_width="48dp"
+        android:layout_height="32dp"
+        />
+
+    <Button
+        android:id="@+id/short_top_button4"
+        android:text="H"
+        android:layout_width="48dp"
+        android:layout_height="31dp"
+        />
+
+    <Button
+        android:id="@+id/short_top_right_corner_button5"
+        android:text="I"
+        android:layout_width="32dp"
+        android:layout_height="31dp"
+        />
+
+</LinearLayout>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadows_scrollview.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadows_scrollview.xml
new file mode 100644
index 0000000..1127570
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadows_scrollview.xml
@@ -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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" >
+
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginStart="50dp"
+        android:layout_marginTop="200dp"
+        android:layout_marginEnd="50dp"
+        android:layout_marginBottom="200dp"
+        android:background="#FFFFFF"
+        android:scrollY="300px"
+        android:elevation="20dp"
+        android:rotation="30">
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+            <Button
+                android:id="@+id/button1"
+                android:layout_width="60dp"
+                android:layout_height="150dp"
+                android:layout_marginStart="30dp"/>
+            <Button
+                android:id="@+id/button2"
+                android:layout_width="30dp"
+                android:layout_height="150dp"
+                android:layout_marginStart="60dp"/>
+        </LinearLayout>
+    </ScrollView>
+</LinearLayout>
\ No newline at end of file
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 f598fda..9e84a53 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
@@ -3,6 +3,8 @@
     <!-- Base application theme. -->
     <style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar">
         <item name="myattr">@integer/ten</item>
+        <item name="android:colorError">#f00</item>
+        <item name="android:colorActivatedHighlight">#c0f0</item>
     </style>
 
     <style name="ThemableWidgetStyle">
diff --git a/bridge/tests/run_tests.sh b/bridge/tests/run_tests.sh
index 4d9cde2..3c68921 100755
--- a/bridge/tests/run_tests.sh
+++ b/bridge/tests/run_tests.sh
@@ -1,17 +1,36 @@
 #!/bin/bash
 
-SCRIPT_DIR="$(dirname $0)"
-DIST_DIR="$1"
+readonly OUT_DIR="$1"
+readonly DIST_DIR="$2"
+readonly BUILD_NUMBER="$3"
 
-STUDIO_JDK=${SCRIPT_DIR}"/../../../../prebuilts/studio/jdk/linux"
+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/linux-x86"
 MISC_COMMON=${SCRIPT_DIR}"/../../../../prebuilts/misc/common"
-M2_REPO=${SCRIPT_DIR}"/../../../../prebuilts/tools/common/m2/repository"
-JAVA_LIBRARIES=${SCRIPT_DIR}"/../../../../out/host/common/obj/JAVA_LIBRARIES"
+OUT_INTERMEDIATES=${SCRIPT_DIR}"/../../../../out/soong/.intermediates"
 
+# Run layoutlib tests
 ${STUDIO_JDK}/bin/java -ea \
     -Dtest_res.dir=${SCRIPT_DIR}/res \
-    -Dtest_failure.dir=${DIST_DIR}/layoutlib_failures \
-    -cp ${M2_REPO}/junit/junit/4.12/junit-4.12.jar:${M2_REPO}/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar:${MISC_COMMON}/tools-common/tools-common-prebuilt.jar:${MISC_COMMON}/sdk-common/sdk-common.jar:${MISC_COMMON}/layoutlib_api/layoutlib_api-prebuilt.jar:${MISC_COMMON}/kxml2/kxml2-2.3.0.jar:${M2_REPO}/com/google/guava/guava/22.0/guava-22.0.jar:${JAVA_LIBRARIES}/layoutlib-tests_intermediates/javalib.jar:${JAVA_LIBRARIES}/layoutlib_intermediates/javalib.jar:${JAVA_LIBRARIES}/mockito-host_intermediates/javalib.jar:${JAVA_LIBRARIES}/objenesis-host_intermediates/javalib.jar \
+    -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 \
     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
+
+exit ${test_exit_code}
diff --git a/bridge/tests/src/android/app/SystemServiceRegistry_AccessorTest.java b/bridge/tests/src/android/app/SystemServiceRegistry_AccessorTest.java
deleted file mode 100644
index 4f1c16c..0000000
--- a/bridge/tests/src/android/app/SystemServiceRegistry_AccessorTest.java
+++ /dev/null
@@ -1,39 +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.app;
-
-import org.junit.Test;
-
-import android.content.Context;
-import android.hardware.input.InputManager;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
-
-import static org.junit.Assert.*;
-
-public class SystemServiceRegistry_AccessorTest {
-    @Test
-    public void testRegistry() {
-        // Just check a few services to make sure we don't break the accessor
-        assertEquals(Context.ACCESSIBILITY_SERVICE,
-                SystemServiceRegistry_Accessor.getSystemServiceName(AccessibilityManager.class));
-        assertEquals(Context.INPUT_SERVICE,
-                SystemServiceRegistry_Accessor.getSystemServiceName(InputManager.class));
-        assertEquals(Context.WINDOW_SERVICE,
-                SystemServiceRegistry_Accessor.getSystemServiceName(WindowManager.class));
-    }
-}
\ 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
index e8f0f17..9761097 100644
--- a/bridge/tests/src/android/util/imagepool/ImagePoolImplTest.java
+++ b/bridge/tests/src/android/util/imagepool/ImagePoolImplTest.java
@@ -16,6 +16,7 @@
 
 package android.util.imagepool;
 
+import org.junit.Ignore;
 import org.junit.Test;
 
 import android.util.imagepool.ImagePool.Image;
@@ -223,6 +224,7 @@
         assertEquals(bufferedImg1, bufferedImage3);
     }
 
+    @Ignore("b/132614809")
     @Test
     public void testPoolDispose() throws InterruptedException {
         int width = 700;
diff --git a/bridge/tests/src/com/android/ide/common/resources/deprecated/FrameworkResources.java b/bridge/tests/src/com/android/ide/common/resources/deprecated/FrameworkResources.java
new file mode 100644
index 0000000..2408fa6
--- /dev/null
+++ b/bridge/tests/src/com/android/ide/common/resources/deprecated/FrameworkResources.java
@@ -0,0 +1,199 @@
+/*
+ * 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.ide.common.resources.deprecated;
+
+
+import com.android.SdkConstants;
+import com.android.io.IAbstractFile;
+import com.android.io.IAbstractFolder;
+import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
+import com.android.utils.ILogger;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+
+import com.google.common.base.Charsets;
+
+/**
+ * @deprecated This class is part of an obsolete resource repository system that is no longer used
+ *     in production code. The class is preserved temporarily for LayoutLib tests.
+ */
+@Deprecated
+public class FrameworkResources extends ResourceRepository {
+
+    /**
+     * Map of {@link ResourceType} to list of items. It is guaranteed to contain a list for all
+     * possible values of ResourceType.
+     */
+    private final Map<ResourceType, List<ResourceItem>> mPublicResourceMap =
+        new EnumMap<>(ResourceType.class);
+
+    public FrameworkResources(@NotNull IAbstractFolder resFolder) {
+        super(resFolder, true /*isFrameworkRepository*/);
+    }
+
+    @Override
+    @NotNull
+    protected ResourceItem createResourceItem(@NotNull String name) {
+        return new ResourceItem(name);
+    }
+
+    /**
+     * Reads the public.xml file in data/res/values/ for a given resource folder and builds up
+     * a map of public resources.
+     *
+     * This map is a subset of the full resource map that only contains framework resources
+     * that are public.
+     *
+     * @param logger a logger to report issues to
+     */
+    public void loadPublicResources(@Nullable ILogger logger) {
+        IAbstractFolder valueFolder = getResFolder().getFolder(SdkConstants.FD_RES_VALUES);
+        if (!valueFolder.exists()) {
+            return;
+        }
+
+        IAbstractFile publicXmlFile = valueFolder.getFile("public.xml"); //$NON-NLS-1$
+        if (publicXmlFile.exists()) {
+            try (Reader reader = new BufferedReader(
+                    new InputStreamReader(publicXmlFile.getContents(), Charsets.UTF_8))) {
+                KXmlParser parser = new KXmlParser();
+                parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
+                parser.setInput(reader);
+
+                ResourceType lastType = null;
+                String lastTypeName = "";
+                while (true) {
+                    int event = parser.next();
+                    if (event == XmlPullParser.START_TAG) {
+                        // As of API 15 there are a number of "java-symbol" entries here
+                        if (!parser.getName().equals("public")) { //$NON-NLS-1$
+                            continue;
+                        }
+
+                        String name = null;
+                        String typeName = null;
+                        for (int i = 0, n = parser.getAttributeCount(); i < n; i++) {
+                            String attribute = parser.getAttributeName(i);
+
+                            if (attribute.equals("name")) { //$NON-NLS-1$
+                                name = parser.getAttributeValue(i);
+                                if (typeName != null) {
+                                    // Skip id attribute processing
+                                    break;
+                                }
+                            } else if (attribute.equals("type")) { //$NON-NLS-1$
+                                typeName = parser.getAttributeValue(i);
+                            }
+                        }
+
+                        if (name != null && typeName != null) {
+                            ResourceType type;
+                            if (typeName.equals(lastTypeName)) {
+                                type = lastType;
+                            } else {
+                                type = ResourceType.fromXmlValue(typeName);
+                                lastType = type;
+                                lastTypeName = typeName;
+                            }
+                            if (type != null) {
+                                ResourceItem match = null;
+                                Map<String, ResourceItem> map = mResourceMap.get(type);
+                                if (map != null) {
+                                    match = map.get(name);
+                                }
+
+                                if (match != null) {
+                                    List<ResourceItem> publicList = mPublicResourceMap.get(type);
+                                    if (publicList == null) {
+                                        // Pick initial size for the list to hold the public
+                                        // resources. We could just use map.size() here,
+                                        // but they're usually much bigger; for example,
+                                        // in one platform version, there are 1500 drawables
+                                        // and 1200 strings but only 175 and 25 public ones
+                                        // respectively.
+                                        int size;
+                                        switch (type) {
+                                            case STYLE:
+                                                size = 500;
+                                                break;
+                                            case ATTR:
+                                                size = 1050;
+                                                break;
+                                            case DRAWABLE:
+                                                size = 200;
+                                                break;
+                                            case ID:
+                                                size = 50;
+                                                break;
+                                            case LAYOUT:
+                                            case COLOR:
+                                            case STRING:
+                                            case ANIM:
+                                            case INTERPOLATOR:
+                                                size = 30;
+                                                break;
+                                            default:
+                                                size = 10;
+                                                break;
+                                        }
+                                        publicList = new ArrayList<>(size);
+                                        mPublicResourceMap.put(type, publicList);
+                                    }
+
+                                    publicList.add(match);
+                                }
+                            }
+                        }
+                    } else if (event == XmlPullParser.END_DOCUMENT) {
+                        break;
+                    }
+                }
+            } catch (Exception e) {
+                if (logger != null) {
+                    logger.error(e, "Can't read and parse public attribute list");
+                }
+            }
+        }
+
+        // put unmodifiable list for all res type in the public resource map
+        // this will simplify access
+        for (ResourceType type : ResourceType.values()) {
+            List<ResourceItem> list = mPublicResourceMap.get(type);
+            if (list == null) {
+                list = Collections.emptyList();
+            } else {
+                list = Collections.unmodifiableList(list);
+            }
+
+            // put the new list in the map
+            mPublicResourceMap.put(type, list);
+        }
+    }
+}
+
diff --git a/bridge/tests/src/com/android/ide/common/resources/deprecated/IdGeneratingResourceFile.java b/bridge/tests/src/com/android/ide/common/resources/deprecated/IdGeneratingResourceFile.java
new file mode 100644
index 0000000..90d6d64
--- /dev/null
+++ b/bridge/tests/src/com/android/ide/common/resources/deprecated/IdGeneratingResourceFile.java
@@ -0,0 +1,215 @@
+/*
+ * 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.ide.common.resources.deprecated;
+
+import com.android.ide.common.rendering.api.DensityBasedResourceValueImpl;
+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.ResourceValueImpl;
+import com.android.ide.common.resources.ResourceValueMap;
+import com.android.ide.common.resources.configuration.DensityQualifier;
+import com.android.ide.common.resources.configuration.ResourceQualifier;
+import com.android.ide.common.resources.deprecated.ValueResourceParser.IValueResourceRepository;
+import com.android.io.IAbstractFile;
+import com.android.io.StreamException;
+import com.android.resources.ResourceType;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @deprecated This class is part of an obsolete resource repository system that is no longer used
+ *     in production code. The class is preserved temporarily for LayoutLib tests.
+ */
+@Deprecated
+public final class IdGeneratingResourceFile extends ResourceFile
+        implements IValueResourceRepository {
+
+    private final ResourceValueMap mIdResources = ResourceValueMap.create();
+
+    private final Collection<ResourceType> mResourceTypeList;
+
+    private final String mFileName;
+
+    private final ResourceType mFileType;
+
+    private final ResourceValue mFileValue;
+
+    public IdGeneratingResourceFile(IAbstractFile file, ResourceFolder folder, ResourceType type) {
+        super(file, folder);
+
+        mFileType = type;
+
+        // Set up our resource types
+        mResourceTypeList = EnumSet.of(mFileType, ResourceType.ID);
+
+        // compute the resource name
+        mFileName = getFileName();
+
+        // Get the resource value of this file as a whole layout
+        mFileValue = getFileValue(file, folder);
+    }
+
+    @Override
+    protected void load(ScanningContext context) {
+        // Parse the file and look for @+id/ entries
+        parseFileForIds();
+
+        // create the resource items in the repository
+        updateResourceItems(context);
+    }
+
+    @Override
+    protected void update(ScanningContext context) {
+        // Copy the previous list of ID names
+        Set<String> oldIdNames = new HashSet<>(mIdResources.keySet());
+
+        // reset current content.
+        mIdResources.clear();
+
+        // need to parse the file and find the IDs.
+        if (!parseFileForIds()) {
+            context.requestFullAapt();
+            // Continue through to updating the resource item here since it
+            // will make for example layout rendering more accurate until
+            // aapt is re-run
+        }
+
+        // We only need to update the repository if our IDs have changed
+        Set<String> keySet = mIdResources.keySet();
+        assert keySet != oldIdNames;
+        if (!oldIdNames.equals(keySet)) {
+            updateResourceItems(context);
+        }
+    }
+
+    @Override
+    public ResourceValue getValue(ResourceType type, String name) {
+        // Check to see if they're asking for one of the right types:
+        if (type != mFileType && type != ResourceType.ID) {
+            return null;
+        }
+
+        // If they're looking for a resource of this type with this name give them the whole file
+        if (type == mFileType && name.equals(mFileName)) {
+            return mFileValue;
+        } else {
+            // Otherwise try to return them an ID
+            // the map will return null if it's not found
+            return mIdResources.get(name);
+        }
+    }
+
+    /**
+     * Looks through the file represented for Ids and adds them to
+     * our id repository
+     *
+     * @return true if parsing succeeds and false if it fails
+     */
+    private boolean parseFileForIds() {
+        IdResourceParser parser = new IdResourceParser(this, isFramework());
+        try {
+            IAbstractFile file = getFile();
+            return parser.parse(file.getContents());
+        } catch (IOException | StreamException ignore) {}
+
+        return false;
+    }
+
+    /**
+     * Add the resources represented by this file to the repository
+     */
+    private void updateResourceItems(ScanningContext context) {
+        ResourceRepository repository = getRepository();
+
+        // remove this file from all existing ResourceItem.
+        repository.removeFile(mResourceTypeList, this);
+
+        // First add this as a layout file
+        ResourceItem item = repository.getResourceItem(mFileType, mFileName);
+        item.add(this);
+
+        // Now iterate through our IDs and add
+        for (String idName : mIdResources.keySet()) {
+            item = repository.getResourceItem(ResourceType.ID, idName);
+            // add this file to the list of files generating ID resources.
+            item.add(this);
+        }
+
+        //  Ask the repository for an ID refresh
+        context.requestFullAapt();
+    }
+
+    /**
+     * Returns the resource value associated with this whole file as a layout resource
+     * @param file the file handler that represents this file
+     * @param folder the folder this file is under
+     * @return a resource value associated with this layout
+     */
+    private ResourceValue getFileValue(IAbstractFile file, ResourceFolder folder) {
+        // test if there's a density qualifier associated with the resource
+        DensityQualifier qualifier = folder.getConfiguration().getDensityQualifier();
+
+        ResourceValue value;
+        if (!ResourceQualifier.isValid(qualifier)) {
+            value =
+                    new ResourceValueImpl(
+                            new ResourceReference(
+                                    ResourceNamespace.fromBoolean(isFramework()),
+                                    mFileType,
+                                    mFileName),
+                            file.getOsLocation());
+        } else {
+            value =
+                    new DensityBasedResourceValueImpl(
+                            new ResourceReference(
+                                    ResourceNamespace.fromBoolean(isFramework()),
+                                    mFileType,
+                                    mFileName),
+                            file.getOsLocation(),
+                            qualifier.getValue());
+        }
+        return value;
+    }
+
+
+    /**
+     * Returns the name of this resource.
+     */
+    private String getFileName() {
+        // get the name from the filename.
+        String name = getFile().getName();
+
+        int pos = name.indexOf('.');
+        if (pos != -1) {
+            name = name.substring(0, pos);
+        }
+
+        return name;
+    }
+
+    @Override
+    public void addResourceValue(ResourceValue value) {
+        // Just overwrite collisions. We're only interested in the unique
+        // IDs declared
+        mIdResources.put(value.getName(), value);
+    }
+}
diff --git a/bridge/tests/src/com/android/ide/common/resources/deprecated/IdResourceParser.java b/bridge/tests/src/com/android/ide/common/resources/deprecated/IdResourceParser.java
new file mode 100644
index 0000000..090217a
--- /dev/null
+++ b/bridge/tests/src/com/android/ide/common/resources/deprecated/IdResourceParser.java
@@ -0,0 +1,122 @@
+/*
+ * 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.ide.common.resources.deprecated;
+
+import com.android.SdkConstants;
+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.ResourceValueImpl;
+import com.android.ide.common.resources.deprecated.ValueResourceParser.IValueResourceRepository;
+import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.google.common.io.Closeables;
+
+/**
+ * @deprecated This class is part of an obsolete resource repository system that is no longer used
+ *     in production code. The class is preserved temporarily for LayoutLib tests.
+ */
+@Deprecated
+public class IdResourceParser {
+    private final IValueResourceRepository mRepository;
+    private final boolean mIsFramework;
+
+    /**
+     * Creates a new {@link com.android.ide.common.resources.deprecated.IdResourceParser}
+     *
+     * @param repository value repository for registering resource declaration
+     * @param isFramework true if scanning a framework resource
+     */
+    public IdResourceParser(
+            @NotNull ValueResourceParser.IValueResourceRepository repository,
+            boolean isFramework) {
+        mRepository = repository;
+        mIsFramework = isFramework;
+    }
+
+    /**
+     * Parse the given input and register ids with the given
+     * {@link IValueResourceRepository}.
+     *
+     * @param input the input stream of the XML to be parsed (will be closed by this method)
+     * @return true if parsing succeeds and false if it fails
+     * @throws IOException if reading the contents fails
+     */
+    public boolean parse(InputStream input)
+            throws IOException {
+        KXmlParser parser = new KXmlParser();
+        try {
+            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+
+            if (input instanceof FileInputStream) {
+                input = new BufferedInputStream(input);
+            }
+            parser.setInput(input, SdkConstants.UTF_8);
+
+            return parse(parser);
+        } catch (XmlPullParserException | RuntimeException e) {
+            return false;
+        } finally {
+            try {
+                Closeables.close(input, true /* swallowIOException */);
+            } catch (IOException e) {
+                // cannot happen
+            }
+        }
+    }
+
+    private boolean parse(KXmlParser parser)
+            throws XmlPullParserException, IOException {
+        while (true) {
+            int event = parser.next();
+            if (event == XmlPullParser.START_TAG) {
+                for (int i = 0, n = parser.getAttributeCount(); i < n; i++) {
+                    String attribute = parser.getAttributeName(i);
+                    String value = parser.getAttributeValue(i);
+                    assert value != null : attribute;
+
+                    if (value.startsWith("@+")) {       //$NON-NLS-1$
+                        // Strip out the @+id/ or @+android:id/ section
+                        String id = value.substring(value.indexOf('/') + 1);
+                        ResourceValue newId =
+                                new ResourceValueImpl(
+                                        new ResourceReference(
+                                                ResourceNamespace.fromBoolean(mIsFramework),
+                                                ResourceType.ID,
+                                                id),
+                                        null);
+                        mRepository.addResourceValue(newId);
+                    }
+                }
+            } else if (event == XmlPullParser.END_DOCUMENT) {
+                break;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/bridge/tests/src/com/android/ide/common/resources/deprecated/MultiResourceFile.java b/bridge/tests/src/com/android/ide/common/resources/deprecated/MultiResourceFile.java
new file mode 100644
index 0000000..3a51134
--- /dev/null
+++ b/bridge/tests/src/com/android/ide/common/resources/deprecated/MultiResourceFile.java
@@ -0,0 +1,190 @@
+/*
+ * 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.ide.common.resources.deprecated;
+
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.ResourceValueImpl;
+import com.android.ide.common.resources.ResourceValueMap;
+import com.android.ide.common.resources.deprecated.ValueResourceParser.IValueResourceRepository;
+import com.android.io.IAbstractFile;
+import com.android.io.StreamException;
+import com.android.resources.ResourceType;
+import com.android.utils.XmlUtils;
+
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.Map;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+/**
+ * @deprecated This class is part of an obsolete resource repository system that is no longer used
+ *     in production code. The class is preserved temporarily for LayoutLib tests.
+ */
+@Deprecated
+public final class MultiResourceFile extends ResourceFile implements IValueResourceRepository {
+
+    private static final SAXParserFactory sParserFactory = XmlUtils.configureSaxFactory(
+            SAXParserFactory.newInstance(), false, false);
+
+    private final Map<ResourceType, ResourceValueMap> mResourceItems =
+        new EnumMap<>(ResourceType.class);
+
+    private Collection<ResourceType> mResourceTypeList = null;
+
+    public MultiResourceFile(IAbstractFile file, ResourceFolder folder) {
+        super(file, folder);
+    }
+
+    // Boolean flag to track whether a named element has been added or removed, thus requiring
+    // a new ID table to be generated
+    private boolean mNeedIdRefresh;
+
+    @Override
+    protected void load(ScanningContext context) {
+        // need to parse the file and find the content.
+        parseFile();
+
+        // create new ResourceItems for the new content.
+        mResourceTypeList = Collections.unmodifiableCollection(mResourceItems.keySet());
+
+        // We need an ID generation step
+        mNeedIdRefresh = true;
+
+        // create/update the resource items.
+        updateResourceItems(context);
+    }
+
+    @Override
+    protected void update(ScanningContext context) {
+        // Reset the ID generation flag
+        mNeedIdRefresh = false;
+
+        // Copy the previous version of our list of ResourceItems and types
+        Map<ResourceType, ResourceValueMap> oldResourceItems
+                        = new EnumMap<>(mResourceItems);
+
+        // reset current content.
+        mResourceItems.clear();
+
+        // need to parse the file and find the content.
+        parseFile();
+
+        // create new ResourceItems for the new content.
+        mResourceTypeList = Collections.unmodifiableCollection(mResourceItems.keySet());
+
+        // Check to see if any names have changed. If so, mark the flag so updateResourceItems
+        // can notify the ResourceRepository that an ID refresh is needed
+        if (oldResourceItems.keySet().equals(mResourceItems.keySet())) {
+            for (ResourceType type : mResourceTypeList) {
+                // We just need to check the names of the items.
+                // If there are new or removed names then we'll have to regenerate IDs
+                if (!mResourceItems.get(type).keySet().equals(oldResourceItems.get(type).keySet())) {
+                    mNeedIdRefresh = true;
+                }
+            }
+        } else {
+            // If our type list is different, obviously the names will be different
+            mNeedIdRefresh = true;
+        }
+        // create/update the resource items.
+        updateResourceItems(context);
+    }
+
+    private void updateResourceItems(ScanningContext context) {
+        ResourceRepository repository = getRepository();
+
+        // remove this file from all existing ResourceItem.
+        repository.removeFile(mResourceTypeList, this);
+
+        for (ResourceType type : mResourceTypeList) {
+            ResourceValueMap list = mResourceItems.get(type);
+
+            if (list != null) {
+                Collection<ResourceValue> values = list.values();
+                for (ResourceValue res : values) {
+                    ResourceItem item = repository.getResourceItem(type, res.getName());
+
+                    // add this file to the list of files generating this resource item.
+                    item.add(this);
+                }
+            }
+        }
+
+        // If we need an ID refresh, ask the repository for that now
+        if (mNeedIdRefresh) {
+            context.requestFullAapt();
+        }
+    }
+
+    /**
+     * Parses the file and creates a list of {@link ResourceType}.
+     */
+    private void parseFile() {
+        try {
+            SAXParser parser = XmlUtils.createSaxParser(sParserFactory);
+            parser.parse(getFile().getContents(), new ValueResourceParser(this, isFramework(), null));
+        } catch (ParserConfigurationException | IOException | StreamException | SAXException ignore) {
+        }
+    }
+
+    /**
+     * Adds a resource item to the list
+     * @param value The value of the resource.
+     */
+    @Override
+    public void addResourceValue(ResourceValue value) {
+        ResourceType resType = value.getResourceType();
+
+        ResourceValueMap list = mResourceItems.get(resType);
+
+        // if the list does not exist, create it.
+        if (list == null) {
+            list = ResourceValueMap.create();
+            mResourceItems.put(resType, list);
+        } else {
+            // look for a possible value already existing.
+            ResourceValue oldValue = list.get(value.getName());
+
+            if (oldValue instanceof ResourceValueImpl) {
+                ((ResourceValueImpl) oldValue).replaceWith(value);
+                return;
+            }
+        }
+
+        // empty list or no match found? add the given resource
+        list.put(value.getName(), value);
+    }
+
+    @Override
+    public ResourceValue getValue(ResourceType type, String name) {
+        // get the list for the given type
+        ResourceValueMap list = mResourceItems.get(type);
+
+        if (list != null) {
+            return list.get(name);
+        }
+
+        return null;
+    }
+}
diff --git a/create/tests/com/android/tools/layoutlib/create/dataclass/StubClass.java b/bridge/tests/src/com/android/ide/common/resources/deprecated/ResourceDeltaKind.java
similarity index 61%
copy from create/tests/com/android/tools/layoutlib/create/dataclass/StubClass.java
copy to bridge/tests/src/com/android/ide/common/resources/deprecated/ResourceDeltaKind.java
index 3ae8e47..67c48f3 100644
--- a/create/tests/com/android/tools/layoutlib/create/dataclass/StubClass.java
+++ b/bridge/tests/src/com/android/ide/common/resources/deprecated/ResourceDeltaKind.java
@@ -1,5 +1,5 @@
 /*
- * 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");
  * you may not use this file except in compliance with the License.
@@ -14,17 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.tools.layoutlib.create.dataclass;
-
-import com.android.tools.layoutlib.create.StubMethodAdapterTest;
+package com.android.ide.common.resources.deprecated;
 
 /**
- * Used by {@link StubMethodAdapterTest}
+ * @deprecated This class is part of an obsolete resource repository system that is no longer used
+ *     in production code. The class is preserved temporarily for LayoutLib tests.
  */
-@SuppressWarnings("unused")
-public class StubClass {
-
-    public boolean returnTrue() {
-        return true;
-    }
+@Deprecated
+public enum ResourceDeltaKind {
+    CHANGED, ADDED, REMOVED
 }
diff --git a/bridge/tests/src/com/android/ide/common/resources/deprecated/ResourceFile.java b/bridge/tests/src/com/android/ide/common/resources/deprecated/ResourceFile.java
new file mode 100644
index 0000000..c7eaa81
--- /dev/null
+++ b/bridge/tests/src/com/android/ide/common/resources/deprecated/ResourceFile.java
@@ -0,0 +1,79 @@
+/*
+ * 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.ide.common.resources.deprecated;
+
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.resources.configuration.Configurable;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.io.IAbstractFile;
+import com.android.resources.ResourceType;
+
+/**
+ * @deprecated This class is part of an obsolete resource repository system that is no longer used
+ *     in production code. The class is preserved temporarily for LayoutLib tests.
+ */
+@Deprecated
+public abstract class ResourceFile implements Configurable {
+
+    private final IAbstractFile mFile;
+    private final ResourceFolder mFolder;
+
+    protected ResourceFile(IAbstractFile file, ResourceFolder folder) {
+        mFile = file;
+        mFolder = folder;
+    }
+
+    protected abstract void load(ScanningContext context);
+    protected abstract void update(ScanningContext context);
+
+    @Override
+    public FolderConfiguration getConfiguration() {
+        return mFolder.getConfiguration();
+    }
+
+    /**
+     * Returns the IFile associated with the ResourceFile.
+     */
+    public final IAbstractFile getFile() {
+        return mFile;
+    }
+
+    public final ResourceRepository getRepository() {
+        return mFolder.getRepository();
+    }
+
+    /**
+     * Returns whether the resource is a framework resource.
+     */
+    public final boolean isFramework() {
+        return mFolder.getRepository().isFrameworkRepository();
+    }
+
+    /**
+     * Returns the value of a resource generated by this file by {@link ResourceType} and name.
+     * <p>If no resource match, <code>null</code> is returned.
+     * @param type the type of the resource.
+     * @param name the name of the resource.
+     */
+    public abstract ResourceValue getValue(ResourceType type, String name);
+
+    @Override
+    public String toString() {
+        return mFile.toString();
+    }
+}
+
diff --git a/bridge/tests/src/com/android/ide/common/resources/deprecated/ResourceFolder.java b/bridge/tests/src/com/android/ide/common/resources/deprecated/ResourceFolder.java
new file mode 100644
index 0000000..fe708f3
--- /dev/null
+++ b/bridge/tests/src/com/android/ide/common/resources/deprecated/ResourceFolder.java
@@ -0,0 +1,151 @@
+/*
+ * 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.ide.common.resources.deprecated;
+
+import com.android.SdkConstants;
+import com.android.ide.common.resources.configuration.Configurable;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.io.IAbstractFile;
+import com.android.io.IAbstractFolder;
+import com.android.resources.FolderTypeRelationship;
+import com.android.resources.ResourceFolderType;
+import com.android.resources.ResourceType;
+import com.android.utils.SdkUtils;
+
+import java.util.List;
+
+/**
+ * @deprecated This class is part of an obsolete resource repository system that is no longer used
+ *     in production code. The class is preserved temporarily for LayoutLib tests.
+ */
+@Deprecated
+public final class ResourceFolder implements Configurable {
+    private final ResourceFolderType mType;
+    final FolderConfiguration mConfiguration;
+    IAbstractFolder mFolder;
+    private final ResourceRepository mRepository;
+
+    /**
+     * Creates a new {@link com.android.ide.common.resources.deprecated.ResourceFolder}
+     * @param type The type of the folder
+     * @param config The configuration of the folder
+     * @param folder The associated {@link IAbstractFolder} object.
+     * @param repository The associated {@link ResourceRepository}
+     */
+    protected ResourceFolder(ResourceFolderType type, FolderConfiguration config,
+            IAbstractFolder folder, ResourceRepository repository) {
+        mType = type;
+        mConfiguration = config;
+        mFolder = folder;
+        mRepository = repository;
+    }
+
+    /**
+     * Processes a file and adds it to its parent folder resource.
+     *
+     * @param file the underlying resource file.
+     * @param kind the file change kind.
+     * @param context a context object with state for the current update, such
+     *            as a place to stash errors encountered
+     * @return the {@link ResourceFile} that was created.
+     */
+    public ResourceFile processFile(IAbstractFile file, ResourceDeltaKind kind,
+            ScanningContext context) {
+        // look for this file if it's already been created
+        ResourceFile resFile = getFile(file, context);
+
+        if (resFile == null) {
+            if (kind != ResourceDeltaKind.REMOVED) {
+                // create a ResourceFile for it.
+
+                resFile = createResourceFile(file);
+                resFile.load(context);
+            }
+        } else {
+            if (kind != ResourceDeltaKind.REMOVED) {
+                resFile.update(context);
+            }
+        }
+
+        return resFile;
+    }
+
+    private ResourceFile createResourceFile(IAbstractFile file) {
+        // check if that's a single or multi resource type folder. We have a special case
+        // for ID generating resource types (layout/menu, and XML drawables, etc.).
+        // MultiResourceFile handles the case when several resource types come from a single file
+        // (values files).
+
+        ResourceFile resFile;
+        if (mType != ResourceFolderType.VALUES) {
+            if (FolderTypeRelationship.isIdGeneratingFolderType(mType) &&
+                SdkUtils.endsWithIgnoreCase(file.getName(), SdkConstants.DOT_XML)) {
+                List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(mType);
+                ResourceType primaryType = types.get(0);
+                resFile = new IdGeneratingResourceFile(file, this, primaryType);
+            } else {
+                resFile = new SingleResourceFile(file, this);
+            }
+        } else {
+            resFile = new MultiResourceFile(file, this);
+        }
+        return resFile;
+    }
+
+    /**
+     * Returns the {@link ResourceFolderType} of this object.
+     */
+    public ResourceFolderType getType() {
+        return mType;
+    }
+
+    public ResourceRepository getRepository() {
+        return mRepository;
+    }
+
+    @Override
+    public FolderConfiguration getConfiguration() {
+        return mConfiguration;
+    }
+
+    /**
+     * Returns the {@link ResourceFile} matching a {@link IAbstractFile} object.
+     *
+     * @param file The {@link IAbstractFile} object.
+     * @param context a context object with state for the current update, such
+     *            as a place to stash errors encountered
+     * @return the {@link ResourceFile} or null if no match was found.
+     */
+    private ResourceFile getFile(IAbstractFile file, ScanningContext context) {
+        assert mFolder.equals(file.getParentFolder());
+
+        // If the file actually exists, the resource folder  may not have been
+        // scanned yet; add it lazily
+        if (file.exists()) {
+            ResourceFile resFile = createResourceFile(file);
+            resFile.load(context);
+            return resFile;
+        }
+
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        return mFolder.toString();
+    }
+}
diff --git a/bridge/tests/src/com/android/ide/common/resources/deprecated/ResourceItem.java b/bridge/tests/src/com/android/ide/common/resources/deprecated/ResourceItem.java
new file mode 100644
index 0000000..8aaefde
--- /dev/null
+++ b/bridge/tests/src/com/android/ide/common/resources/deprecated/ResourceItem.java
@@ -0,0 +1,107 @@
+/*
+ * 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.ide.common.resources.deprecated;
+
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.resources.ResourceType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @deprecated This class is part of an obsolete resource repository system that is no longer used
+ *     in production code. The class is preserved temporarily for LayoutLib tests.
+ */
+@Deprecated
+public class ResourceItem implements Comparable<ResourceItem> {
+    private final String mName;
+
+    /**
+     * List of files generating this ResourceItem.
+     */
+    private final List<ResourceFile> mFiles = new ArrayList<>();
+
+    /**
+     * Constructs a new ResourceItem.
+     * @param name the name of the resource as it appears in the XML and R.java files.
+     */
+    public ResourceItem(String name) {
+        mName = name;
+    }
+
+    /**
+     * Returns the name of the resource.
+     */
+    public final String getName() {
+        return mName;
+    }
+
+    /**
+     * Compares the {@link com.android.ide.common.resources.deprecated.ResourceItem} to another.
+     * @param other the ResourceItem to be compared to.
+     */
+    @Override
+    public int compareTo(com.android.ide.common.resources.deprecated.ResourceItem other) {
+        return mName.compareTo(other.mName);
+    }
+
+    /**
+     * Returns a {@link ResourceValue} for this item based on the given configuration.
+     * If the ResourceItem has several source files, one will be selected based on the config.
+     * @param type the type of the resource. This is necessary because ResourceItem doesn't embed
+     *     its type, but ResourceValue does.
+     * @param referenceConfig the config of the resource item.
+     * @return a ResourceValue or null if none match the config.
+     */
+    public ResourceValue getResourceValue(ResourceType type, FolderConfiguration referenceConfig) {
+        // look for the best match for the given configuration
+        // the match has to be of type ResourceFile since that's what the input list contains
+        ResourceFile match = referenceConfig.findMatchingConfigurable(mFiles);
+
+        if (match != null) {
+            // get the value of this configured resource.
+            return match.getValue(type, mName);
+        }
+
+        return null;
+    }
+
+    /**
+     * Adds a new source file.
+     * @param file the source file.
+     */
+    protected void add(ResourceFile file) {
+        mFiles.add(file);
+    }
+
+    /**
+     * Removes a file from the list of source files.
+     * @param file the file to remove
+     */
+    protected void removeFile(ResourceFile file) {
+        mFiles.remove(file);
+    }
+
+    /**
+     * Returns {@code true} if the item has no source file.
+     * @return true if the item has no source file.
+     */
+    protected boolean hasNoSourceFile() {
+        return mFiles.isEmpty();
+    }
+}
diff --git a/bridge/tests/src/com/android/ide/common/resources/deprecated/ResourceRepository.java b/bridge/tests/src/com/android/ide/common/resources/deprecated/ResourceRepository.java
new file mode 100644
index 0000000..d8204d5
--- /dev/null
+++ b/bridge/tests/src/com/android/ide/common/resources/deprecated/ResourceRepository.java
@@ -0,0 +1,394 @@
+/*
+ * 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.ide.common.resources.deprecated;
+
+import com.android.SdkConstants;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.resources.ResourceValueMap;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.io.IAbstractFile;
+import com.android.io.IAbstractFolder;
+import com.android.io.IAbstractResource;
+import com.android.resources.ResourceFolderType;
+import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @deprecated This class is part of an obsolete resource repository system that is no longer used
+ *     in production code. The class is preserved temporarily for LayoutLib tests.
+ */
+@Deprecated
+public abstract class ResourceRepository {
+    private final IAbstractFolder mResourceFolder;
+
+    private Map<ResourceFolderType, List<ResourceFolder>> mFolderMap =
+            new EnumMap<>(ResourceFolderType.class);
+
+    protected Map<ResourceType, Map<String, ResourceItem>> mResourceMap =
+            new EnumMap<>(ResourceType.class);
+
+    private final boolean mFrameworkRepository;
+    private boolean mCleared = true;
+    private boolean mInitializing;
+
+    /**
+     * Makes a resource repository.
+     *
+     * @param resFolder the resource folder of the repository.
+     * @param isFrameworkRepository whether the repository is for framework resources.
+     */
+    protected ResourceRepository(@NotNull IAbstractFolder resFolder,
+            boolean isFrameworkRepository) {
+        mResourceFolder = resFolder;
+        mFrameworkRepository = isFrameworkRepository;
+    }
+
+    public IAbstractFolder getResFolder() {
+        return mResourceFolder;
+    }
+
+    public boolean isFrameworkRepository() {
+        return mFrameworkRepository;
+    }
+
+    private synchronized void clear() {
+        mCleared = true;
+        mFolderMap = new EnumMap<>(ResourceFolderType.class);
+        mResourceMap = new EnumMap<>(ResourceType.class);
+    }
+
+    /**
+     * Ensures that the repository has been initialized again after a call to
+     * {@link com.android.ide.common.resources.deprecated.ResourceRepository#clear()}.
+     *
+     * @return true if the repository was just re-initialized.
+     */
+    private synchronized boolean ensureInitialized() {
+        if (mCleared && !mInitializing) {
+            ScanningContext context = new ScanningContext();
+            mInitializing = true;
+
+            IAbstractResource[] resources = mResourceFolder.listMembers();
+
+            for (IAbstractResource res : resources) {
+                if (res instanceof IAbstractFolder) {
+                    IAbstractFolder folder = (IAbstractFolder)res;
+                    ResourceFolder resFolder = processFolder(folder);
+
+                    if (resFolder != null) {
+                        // now we process the content of the folder
+                        IAbstractResource[] files = folder.listMembers();
+
+                        for (IAbstractResource fileRes : files) {
+                            if (fileRes instanceof IAbstractFile) {
+                                IAbstractFile file = (IAbstractFile)fileRes;
+
+                                resFolder.processFile(file, ResourceDeltaKind.ADDED, context);
+                            }
+                        }
+                    }
+                }
+            }
+
+            mInitializing = false;
+            mCleared = false;
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Adds a Folder Configuration to the project.
+     *
+     * @param type The resource type.
+     * @param config The resource configuration.
+     * @param folder The workspace folder object.
+     * @return the {@link ResourceFolder} object associated to this folder.
+     */
+    private ResourceFolder add(
+            @NotNull ResourceFolderType type,
+            @NotNull FolderConfiguration config,
+            @NotNull IAbstractFolder folder) {
+        // get the list for the resource type
+        List<ResourceFolder> list = mFolderMap.get(type);
+
+        if (list == null) {
+            list = new ArrayList<>();
+
+            ResourceFolder cf = new ResourceFolder(type, config, folder, this);
+            list.add(cf);
+
+            mFolderMap.put(type, list);
+
+            return cf;
+        }
+
+        // look for an already existing folder configuration.
+        for (ResourceFolder cFolder : list) {
+            if (cFolder.mConfiguration.equals(config)) {
+                // config already exist. Nothing to be done really, besides making sure
+                // the IAbstractFolder object is up to date.
+                cFolder.mFolder = folder;
+                return cFolder;
+            }
+        }
+
+        // If we arrive here, this means we didn't find a matching configuration.
+        // So we add one.
+        ResourceFolder cf = new ResourceFolder(type, config, folder, this);
+        list.add(cf);
+
+        return cf;
+    }
+
+    /**
+     * Returns a {@link ResourceItem} matching the given {@link ResourceType} and name. If none
+     * exist, it creates one.
+     *
+     * @param type the resource type
+     * @param name the name of the resource.
+     * @return A resource item matching the type and name.
+     */
+    @NotNull
+    public ResourceItem getResourceItem(@NotNull ResourceType type, @NotNull String name) {
+        ensureInitialized();
+
+        // looking for an existing ResourceItem with this type and name
+        ResourceItem item = findDeclaredResourceItem(type, name);
+
+        // create one if there isn't one already, or if the existing one is inlined, since
+        // clearly we need a non inlined one (the inline one is removed too)
+        if (item == null) {
+            item = createResourceItem(name);
+
+            Map<String, ResourceItem> map = mResourceMap.get(type);
+
+            if (map == null) {
+                if (isFrameworkRepository()) {
+                    // Pick initial size for the maps. Also change the load factor to 1.0
+                    // to avoid rehashing the whole table when we (as expected) get near
+                    // the known rough size of each resource type map.
+                    int size;
+                    switch (type) {
+                        // Based on counts in API 16. Going back to API 10, the counts
+                        // are roughly 25-50% smaller (e.g. compared to the top 5 types below
+                        // the fractions are 1107 vs 1734, 831 vs 1508, 895 vs 1255,
+                        // 733 vs 1064 and 171 vs 783.
+                        case PUBLIC:           size = 1734; break;
+                        case DRAWABLE:         size = 1508; break;
+                        case STRING:           size = 1255; break;
+                        case ATTR:             size = 1064; break;
+                        case STYLE:             size = 783; break;
+                        case ID:                size = 347; break;
+                        case STYLEABLE:
+                            size = 210;
+                            break;
+                        case LAYOUT:            size = 187; break;
+                        case COLOR:             size = 120; break;
+                        case ANIM:               size = 95; break;
+                        case DIMEN:              size = 81; break;
+                        case BOOL:               size = 54; break;
+                        case INTEGER:            size = 52; break;
+                        case ARRAY:              size = 51; break;
+                        case PLURALS:            size = 20; break;
+                        case XML:                size = 14; break;
+                        case INTERPOLATOR :      size = 13; break;
+                        case ANIMATOR:            size = 8; break;
+                        case RAW:                 size = 4; break;
+                        case MENU:                size = 2; break;
+                        case MIPMAP:              size = 2; break;
+                        case FRACTION:            size = 1; break;
+                        default:
+                            size = 2;
+                    }
+                    map = new HashMap<>(size, 1.0f);
+                } else {
+                    map = new HashMap<>();
+                }
+                mResourceMap.put(type, map);
+            }
+
+            map.put(item.getName(), item);
+        }
+
+        return item;
+    }
+
+    /**
+     * Creates a resource item with the given name.
+     * @param name the name of the resource
+     * @return a new ResourceItem (or child class) instance.
+     */
+    @NotNull
+    protected abstract ResourceItem createResourceItem(@NotNull String name);
+
+    /**
+     * Processes a folder and adds it to the list of existing folders.
+     * @param folder the folder to process
+     * @return the ResourceFolder created from this folder, or null if the process failed.
+     */
+    @Nullable
+    private ResourceFolder processFolder(@NotNull IAbstractFolder folder) {
+        ensureInitialized();
+
+        // split the name of the folder in segments.
+        String[] folderSegments = folder.getName().split(SdkConstants.RES_QUALIFIER_SEP);
+
+        // get the enum for the resource type.
+        ResourceFolderType type = ResourceFolderType.getTypeByName(folderSegments[0]);
+
+        if (type != null) {
+            // get the folder configuration.
+            FolderConfiguration config = FolderConfiguration.getConfig(folderSegments);
+
+            if (config != null) {
+                return add(type, config, folder);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the resources values matching a given {@link FolderConfiguration}.
+     *
+     * @param referenceConfig the configuration that each value must match.
+     * @return a map with guaranteed to contain an entry for each {@link ResourceType}
+     */
+    @NotNull
+    public Map<ResourceType, ResourceValueMap> getConfiguredResources(
+            @NotNull FolderConfiguration referenceConfig) {
+        ensureInitialized();
+
+        return doGetConfiguredResources(referenceConfig);
+    }
+
+    /**
+     * Returns the resources values matching a given {@link FolderConfiguration} for the current
+     * project.
+     *
+     * @param referenceConfig the configuration that each value must match.
+     * @return a map with guaranteed to contain an entry for each {@link ResourceType}
+     */
+    @NotNull
+    private Map<ResourceType, ResourceValueMap> doGetConfiguredResources(
+            @NotNull FolderConfiguration referenceConfig) {
+        ensureInitialized();
+
+        Map<ResourceType, ResourceValueMap> map =
+            new EnumMap<>(ResourceType.class);
+
+        for (ResourceType key : ResourceType.values()) {
+            // get the local results and put them in the map
+            map.put(key, getConfiguredResource(key, referenceConfig));
+        }
+
+        return map;
+    }
+
+   /**
+     * Loads the resources.
+     */
+    public void loadResources() {
+        clear();
+        ensureInitialized();
+    }
+
+    protected void removeFile(@NotNull Collection<ResourceType> types,
+            @NotNull ResourceFile file) {
+        ensureInitialized();
+
+        for (ResourceType type : types) {
+            removeFile(type, file);
+        }
+    }
+
+    private void removeFile(@NotNull ResourceType type, @NotNull ResourceFile file) {
+        Map<String, ResourceItem> map = mResourceMap.get(type);
+        if (map != null) {
+            Collection<ResourceItem> values = map.values();
+            List<ResourceItem> toDelete = null;
+            for (ResourceItem item : values) {
+                item.removeFile(file);
+                if (item.hasNoSourceFile()) {
+                    if (toDelete == null) {
+                        toDelete = new ArrayList<>(values.size());
+                    }
+                    toDelete.add(item);
+                }
+            }
+            if (toDelete != null) {
+                for (ResourceItem item : toDelete) {
+                    map.remove(item.getName());
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns a map of (resource name, resource value) for the given {@link ResourceType}.
+     * <p>The values returned are taken from the resource files best matching a given
+     * {@link FolderConfiguration}.
+     *
+     * @param type the type of the resources.
+     * @param referenceConfig the configuration to best match.
+     */
+    @NotNull
+    private ResourceValueMap getConfiguredResource(@NotNull ResourceType type,
+            @NotNull FolderConfiguration referenceConfig) {
+        // get the resource item for the given type
+        Map<String, ResourceItem> items = mResourceMap.get(type);
+        if (items == null) {
+            return ResourceValueMap.create();
+        }
+
+        // create the map
+        ResourceValueMap map = ResourceValueMap.createWithExpectedSize(items.size());
+
+        for (ResourceItem item : items.values()) {
+            ResourceValue value = item.getResourceValue(type, referenceConfig);
+            if (value != null) {
+                map.put(item.getName(), value);
+            }
+        }
+
+        return map;
+    }
+
+    /**
+     * Looks up an existing {@link ResourceItem} by {@link ResourceType} and name.
+     *
+     * @param type the resource type.
+     * @param name the resource name.
+     * @return the existing ResourceItem or null if no match was found.
+     */
+    @Nullable
+    private ResourceItem findDeclaredResourceItem(
+            @NotNull ResourceType type, @NotNull String name) {
+        Map<String, ResourceItem> map = mResourceMap.get(type);
+        return map != null ? map.get(name) : null;
+    }
+}
+
diff --git a/bridge/tests/src/com/android/ide/common/resources/deprecated/ScanningContext.java b/bridge/tests/src/com/android/ide/common/resources/deprecated/ScanningContext.java
new file mode 100644
index 0000000..dadf18d
--- /dev/null
+++ b/bridge/tests/src/com/android/ide/common/resources/deprecated/ScanningContext.java
@@ -0,0 +1,44 @@
+/*
+ * 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.ide.common.resources.deprecated;
+
+/**
+ * @deprecated This class is part of an obsolete resource repository system that is no longer used
+ *     in production code. The class is preserved temporarily for LayoutLib tests.
+ */
+@Deprecated
+public class ScanningContext {
+    private boolean mNeedsFullAapt;
+
+    /**
+     * Marks that a full aapt compilation of the resources is necessary because it has
+     * detected a change that cannot be incrementally handled.
+     */
+    protected void requestFullAapt() {
+        mNeedsFullAapt = true;
+    }
+
+    /**
+     * Returns whether this repository has been marked as "dirty"; if one or
+     * more of the constituent files have declared that the resource item names
+     * that they provide have changed.
+     *
+     * @return true if a full aapt compilation is required
+     */
+    public boolean needsFullAapt() {
+        return mNeedsFullAapt;
+    }
+}
diff --git a/bridge/tests/src/com/android/ide/common/resources/deprecated/SingleResourceFile.java b/bridge/tests/src/com/android/ide/common/resources/deprecated/SingleResourceFile.java
new file mode 100644
index 0000000..e7a3f90
--- /dev/null
+++ b/bridge/tests/src/com/android/ide/common/resources/deprecated/SingleResourceFile.java
@@ -0,0 +1,150 @@
+/*
+ * 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.ide.common.resources.deprecated;
+
+import com.android.ide.common.rendering.api.DensityBasedResourceValueImpl;
+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.ResourceValueImpl;
+import com.android.ide.common.resources.configuration.DensityQualifier;
+import com.android.ide.common.resources.configuration.ResourceQualifier;
+import com.android.io.IAbstractFile;
+import com.android.resources.FolderTypeRelationship;
+import com.android.resources.ResourceType;
+import com.android.utils.SdkUtils;
+
+import java.util.List;
+
+
+import static com.android.SdkConstants.DOT_XML;
+
+/**
+ * @deprecated This class is part of an obsolete resource repository system that is no longer used
+ *     in production code. The class is preserved temporarily for LayoutLib tests.
+ */
+@Deprecated
+public class SingleResourceFile extends ResourceFile {
+    private final String mResourceName;
+    private final ResourceType mType;
+    private final ResourceValue mValue;
+
+    public SingleResourceFile(IAbstractFile file, ResourceFolder folder) {
+        super(file, folder);
+
+        // we need to infer the type of the resource from the folder type.
+        // This is easy since this is a single Resource file.
+        List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(folder.getType());
+        mType = types.get(0);
+
+        // compute the resource name
+        mResourceName = getResourceName();
+
+        // test if there's a density qualifier associated with the resource
+        DensityQualifier qualifier = folder.getConfiguration().getDensityQualifier();
+
+        if (!ResourceQualifier.isValid(qualifier)) {
+            mValue =
+                    new ResourceValueImpl(
+                            new ResourceReference(
+                                    ResourceNamespace.fromBoolean(isFramework()),
+                                    mType,
+                                    getResourceName()),
+                            file.getOsLocation());
+        } else {
+            mValue =
+                    new DensityBasedResourceValueImpl(
+                            new ResourceReference(
+                                    ResourceNamespace.fromBoolean(isFramework()),
+                                    mType,
+                                    getResourceName()),
+                            file.getOsLocation(),
+                            qualifier.getValue());
+        }
+    }
+
+    @Override
+    protected void load(ScanningContext context) {
+        // get a resource item matching the given type and name
+        ResourceItem item = getRepository().getResourceItem(mType, mResourceName);
+
+        // add this file to the list of files generating this resource item.
+        item.add(this);
+
+        // Ask for an ID refresh since we're adding an item that will generate an ID
+        context.requestFullAapt();
+    }
+
+    @Override
+    protected void update(ScanningContext context) {
+        // when this happens, nothing needs to be done since the file only generates
+        // a single resources that doesn't actually change (its content is the file path)
+
+        // However, we should check for newly introduced errors
+        // Parse the file and look for @+id/ entries
+        validateAttributes(context);
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see com.android.ide.eclipse.editors.resources.manager.ResourceFile#getValue(com.android.ide.eclipse.common.resources.ResourceType, java.lang.String)
+     *
+     * This particular implementation does not care about the type or name since a
+     * SingleResourceFile represents a file generating only one resource.
+     * The value returned is the full absolute path of the file in OS form.
+     */
+    @Override
+    public ResourceValue getValue(ResourceType type, String name) {
+        return mValue;
+    }
+
+    /**
+     * Returns the name of the resources.
+     */
+    private String getResourceName() {
+        // get the name from the filename.
+        String name = getFile().getName();
+
+        int pos = name.indexOf('.');
+        if (pos != -1) {
+            name = name.substring(0, pos);
+        }
+
+        return name;
+    }
+
+    /**
+     * Validates the associated resource file to make sure the attribute references are valid
+     *
+     * @return true if parsing succeeds and false if it fails
+     */
+    private boolean validateAttributes(ScanningContext context) {
+        // We only need to check if it's a non-framework file (and an XML file; skip .png's)
+        if (!isFramework() && SdkUtils.endsWith(getFile().getName(), DOT_XML)) {
+            ValidatingResourceParser parser = new ValidatingResourceParser(context, false);
+            try {
+                IAbstractFile file = getFile();
+                return parser.parse(file.getContents());
+            } catch (Exception e) {
+                context.needsFullAapt();
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/bridge/tests/src/com/android/ide/common/resources/deprecated/ValidatingResourceParser.java b/bridge/tests/src/com/android/ide/common/resources/deprecated/ValidatingResourceParser.java
new file mode 100644
index 0000000..58af88d
--- /dev/null
+++ b/bridge/tests/src/com/android/ide/common/resources/deprecated/ValidatingResourceParser.java
@@ -0,0 +1,121 @@
+/*
+ * 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.ide.common.resources.deprecated;
+
+import com.android.SdkConstants;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.google.common.io.Closeables;
+
+/**
+ * @deprecated This class is part of an obsolete resource repository system that is no longer used
+ *     in production code. The class is preserved temporarily for LayoutLib tests.
+ */
+@Deprecated
+public class ValidatingResourceParser {
+    private final boolean mIsFramework;
+    private final ScanningContext mContext;
+
+    /**
+     * Creates a new {@link com.android.ide.common.resources.deprecated.ValidatingResourceParser}
+     *
+     * @param context a context object with state for the current update, such
+     *            as a place to stash errors encountered
+     * @param isFramework true if scanning a framework resource
+     */
+    public ValidatingResourceParser(
+            @NotNull ScanningContext context,
+            boolean isFramework) {
+        mContext = context;
+        mIsFramework = isFramework;
+    }
+
+    /**
+     * Parse the given input and return false if it contains errors, <b>or</b> if
+     * the context is already tagged as needing a full aapt run.
+     *
+     * @param input the input stream of the XML to be parsed (will be closed by this method)
+     * @return true if parsing succeeds and false if it fails
+     * @throws IOException if reading the contents fails
+     */
+    public boolean parse(InputStream input)
+            throws IOException {
+        // No need to validate framework files
+        if (mIsFramework) {
+            try {
+                Closeables.close(input, true /* swallowIOException */);
+            } catch (IOException e) {
+                // cannot happen
+            }
+            return true;
+        }
+        if (mContext.needsFullAapt()) {
+            try {
+                Closeables.close(input, true /* swallowIOException */);
+            } catch (IOException ignore) {
+                // cannot happen
+            }
+            return false;
+        }
+
+        KXmlParser parser = new KXmlParser();
+        try {
+            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+
+            if (input instanceof FileInputStream) {
+                input = new BufferedInputStream(input);
+            }
+            parser.setInput(input, SdkConstants.UTF_8);
+
+            return parse(parser);
+        } catch (XmlPullParserException | RuntimeException e) {
+            return false;
+        } finally {
+            try {
+                Closeables.close(input, true /* swallowIOException */);
+            } catch (IOException e) {
+                // cannot happen
+            }
+        }
+    }
+
+    private boolean parse(KXmlParser parser)
+            throws XmlPullParserException, IOException {
+        while (true) {
+            int event = parser.next();
+            if (event == XmlPullParser.START_TAG) {
+                for (int i = 0, n = parser.getAttributeCount(); i < n; i++) {
+                    String attribute = parser.getAttributeName(i);
+                    String value = parser.getAttributeValue(i);
+                    assert value != null : attribute;
+                }
+            } else if (event == XmlPullParser.END_DOCUMENT) {
+                break;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/bridge/tests/src/com/android/ide/common/resources/deprecated/ValueResourceParser.java b/bridge/tests/src/com/android/ide/common/resources/deprecated/ValueResourceParser.java
new file mode 100644
index 0000000..53bb5c0
--- /dev/null
+++ b/bridge/tests/src/com/android/ide/common/resources/deprecated/ValueResourceParser.java
@@ -0,0 +1,227 @@
+/*
+ * 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.ide.common.resources.deprecated;
+
+import com.android.ide.common.rendering.api.ArrayResourceValueImpl;
+import com.android.ide.common.rendering.api.AttrResourceValueImpl;
+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.ResourceValueImpl;
+import com.android.ide.common.rendering.api.StyleItemResourceValue;
+import com.android.ide.common.rendering.api.StyleItemResourceValueImpl;
+import com.android.ide.common.rendering.api.StyleResourceValueImpl;
+import com.android.ide.common.rendering.api.StyleableResourceValueImpl;
+import com.android.ide.common.resources.ValueXmlHelper;
+import com.android.resources.ResourceType;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import com.google.common.base.Strings;
+
+import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX;
+import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX_LEN;
+import static com.android.SdkConstants.ATTR_NAME;
+import static com.android.SdkConstants.ATTR_PARENT;
+import static com.android.SdkConstants.ATTR_VALUE;
+import static com.android.SdkConstants.TAG_RESOURCES;
+
+/**
+ * @deprecated This class is part of an obsolete resource repository system that is no longer used
+ *     in production code. The class is preserved temporarily for LayoutLib tests.
+ */
+@Deprecated
+public final class ValueResourceParser extends DefaultHandler {
+
+    private static final ResourceReference TMP_REF =
+            new ResourceReference(ResourceNamespace.RES_AUTO, ResourceType.STRING, "_tmp");
+
+    public interface IValueResourceRepository {
+        void addResourceValue(ResourceValue value);
+    }
+
+    private boolean inResources;
+    private int mDepth;
+    private ResourceValueImpl mCurrentValue;
+    private ArrayResourceValueImpl mArrayResourceValue;
+    private StyleResourceValueImpl mCurrentStyle;
+    private StyleableResourceValueImpl mCurrentDeclareStyleable;
+    private AttrResourceValueImpl mCurrentAttr;
+    private final IValueResourceRepository mRepository;
+    private final boolean mIsFramework;
+    private final String mLibraryName;
+
+    public ValueResourceParser(IValueResourceRepository repository, boolean isFramework, String libraryName) {
+        mRepository = repository;
+        mIsFramework = isFramework;
+        mLibraryName = libraryName;
+    }
+
+    @Override
+    public void endElement(String uri, String localName, String qName) throws SAXException {
+        if (mCurrentValue != null) {
+            String value = mCurrentValue.getValue();
+            value = value == null ? "" : ValueXmlHelper.unescapeResourceString(value, false, true);
+            mCurrentValue.setValue(value);
+        }
+
+        if (inResources && qName.equals(TAG_RESOURCES)) {
+            inResources = false;
+        } else if (mDepth == 2) {
+            mCurrentValue = null;
+            mCurrentStyle = null;
+            mCurrentDeclareStyleable = null;
+            mCurrentAttr = null;
+            mArrayResourceValue = null;
+        } else if (mDepth == 3) {
+            if (mArrayResourceValue != null && mCurrentValue != null) {
+                mArrayResourceValue.addElement(mCurrentValue.getValue());
+            }
+            mCurrentValue = null;
+            //noinspection VariableNotUsedInsideIf
+            if (mCurrentDeclareStyleable != null) {
+                mCurrentAttr = null;
+            }
+        }
+
+        mDepth--;
+        super.endElement(uri, localName, qName);
+    }
+
+    @Override
+    public void startElement(String uri, String localName, String qName, Attributes attributes)
+            throws SAXException {
+        try {
+            ResourceNamespace namespace = ResourceNamespace.fromBoolean(mIsFramework);
+            mDepth++;
+            if (!inResources && mDepth == 1) {
+                if (qName.equals(TAG_RESOURCES)) {
+                    inResources = true;
+                }
+            } else if (mDepth == 2 && inResources) {
+                ResourceType type =
+                        ResourceType.fromXmlTag(
+                                new Object(), (t) -> qName, (t, name) -> attributes.getValue(name));
+
+                if (type != null) {
+                    // get the resource name
+                    String name = attributes.getValue(ATTR_NAME);
+                    if (name != null) {
+                        switch (type) {
+                            case STYLE:
+                                String parent = attributes.getValue(ATTR_PARENT);
+                                mCurrentStyle =
+                                        new StyleResourceValueImpl(
+                                                namespace, name, parent, mLibraryName);
+                                mRepository.addResourceValue(mCurrentStyle);
+                                break;
+                            case STYLEABLE:
+                                mCurrentDeclareStyleable =
+                                        new StyleableResourceValueImpl(
+                                                namespace, name, null, mLibraryName);
+                                mRepository.addResourceValue(mCurrentDeclareStyleable);
+                                break;
+                            case ATTR:
+                                mCurrentAttr =
+                                        new AttrResourceValueImpl(namespace, name, mLibraryName);
+                                mRepository.addResourceValue(mCurrentAttr);
+                                break;
+                            case ARRAY:
+                                mArrayResourceValue =
+                                        new ArrayResourceValueImpl(namespace, name, mLibraryName);
+                                mRepository.addResourceValue(mArrayResourceValue);
+                                break;
+                            default:
+                                mCurrentValue =
+                                        new ResourceValueImpl(
+                                                namespace, type, name, null, mLibraryName);
+                                mRepository.addResourceValue(mCurrentValue);
+                                break;
+                        }
+                    }
+                }
+            } else if (mDepth == 3) {
+                // get the resource name
+                String name = attributes.getValue(ATTR_NAME);
+                if (!Strings.isNullOrEmpty(name)) {
+                    if (mCurrentStyle != null) {
+                        mCurrentValue =
+                                new StyleItemResourceValueImpl(
+                                        mCurrentStyle.getNamespace(), name, null, mLibraryName);
+                        mCurrentStyle.addItem((StyleItemResourceValue) mCurrentValue);
+                    } else if (mCurrentDeclareStyleable != null) {
+                        if (name.startsWith(ANDROID_NS_NAME_PREFIX)) {
+                            name = name.substring(ANDROID_NS_NAME_PREFIX_LEN);
+                        }
+
+                        mCurrentAttr = new AttrResourceValueImpl(namespace, name, mLibraryName);
+                        mCurrentDeclareStyleable.addValue(mCurrentAttr);
+
+                        // also add it to the repository.
+                        mRepository.addResourceValue(mCurrentAttr);
+
+                    } else if (mCurrentAttr != null) {
+                        // get the enum/flag value
+                        String value = attributes.getValue(ATTR_VALUE);
+
+                        try {
+                            // Integer.decode/parseInt can't deal with hex value > 0x7FFFFFFF so we
+                            // use Long.decode instead.
+                            mCurrentAttr.addValue(name, Long.decode(value).intValue(), null);
+                        } catch (NumberFormatException e) {
+                            // pass, we'll just ignore this value
+                        }
+                    }
+                } else //noinspection VariableNotUsedInsideIf
+                    if (mArrayResourceValue != null) {
+                    // Create a temporary resource value to hold the item's value. The value is
+                    // not added to the repository, since it's just a holder. The value will be set
+                    // in the `characters` method and then added to mArrayResourceValue in `endElement`.
+                    mCurrentValue = new ResourceValueImpl(TMP_REF, null);
+                    }
+            } else if (mDepth == 4 && mCurrentAttr != null) {
+                // get the enum/flag name
+                String name = attributes.getValue(ATTR_NAME);
+                String value = attributes.getValue(ATTR_VALUE);
+
+                try {
+                    // Integer.decode/parseInt can't deal with hex value > 0x7FFFFFFF so we
+                    // use Long.decode instead.
+                    mCurrentAttr.addValue(name, Long.decode(value).intValue(), null);
+                } catch (NumberFormatException e) {
+                    // pass, we'll just ignore this value
+                }
+            }
+        } finally {
+            super.startElement(uri, localName, qName, attributes);
+        }
+    }
+
+    @Override
+    public void characters(char[] ch, int start, int length) {
+        if (mCurrentValue != null) {
+            String value = mCurrentValue.getValue();
+            if (value == null) {
+                mCurrentValue.setValue(new String(ch, start, length));
+            } else {
+                mCurrentValue.setValue(value + new String(ch, start, length));
+            }
+        }
+    }
+}
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 96146dc..d877110 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java
@@ -56,7 +56,7 @@
         Configuration configuration = RenderAction.getConfiguration(params);
         BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
                 params.getAssets(), params.getLayoutlibCallback(), configuration,
-                params.getTargetSdkVersion(), params.isRtlSupported());
+                params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
 
         context.initResources();
         BridgeContext oldContext = RenderActionTestUtil.setBridgeContext(context);
@@ -103,7 +103,7 @@
         Configuration configuration = RenderAction.getConfiguration(params);
         BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
                 params.getAssets(), params.getLayoutlibCallback(), configuration,
-                params.getTargetSdkVersion(), params.isRtlSupported());
+                params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
 
         context.initResources();
         BridgeContext oldContext = RenderActionTestUtil.setBridgeContext(context);
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/HighQualityShadowsRenderTests.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/HighQualityShadowsRenderTests.java
new file mode 100644
index 0000000..3582860
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/HighQualityShadowsRenderTests.java
@@ -0,0 +1,121 @@
+/*
+ * 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.layoutlib.bridge.intensive;
+
+import com.android.ide.common.rendering.api.SessionParams;
+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.After;
+import org.junit.Test;
+
+public class HighQualityShadowsRenderTests extends RenderTestBase {
+    @After
+    public void afterTestCase() {
+        com.android.layoutlib.bridge.test.widgets.HookWidget.reset();
+    }
+
+    @Test
+    public void testHighQualityRectangleShadow() 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()
+                .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."));
+    }
+
+    @Test
+    public void testRoundedEdgeRectangle() throws Exception {
+        LayoutPullParser parser = createParserFromPath("shadows_rounded_edge_test.xml");
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(ConfigGenerator.NEXUS_5)
+                .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."));
+    }
+
+    @Test
+    public void testLargeView() throws Exception {
+        LayoutPullParser parser = createParserFromPath("large_view_shadows_test.xml");
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(ConfigGenerator.NEXUS_5)
+                .setCallback(layoutLibCallback)
+                .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."));
+    }
+
+    @Test
+    public void testShadowSizes() throws Exception {
+        LayoutPullParser parser = createParserFromPath("shadow_sizes_test.xml");
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(ConfigGenerator.NEXUS_5)
+                .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."));
+    }
+
+    @Test
+    public void testWidgetWithScroll() throws Exception {
+        LayoutPullParser parser = createParserFromPath("shadows_scrollview.xml");
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .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."));
+    }
+}
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 e2d5e6a..e43531a 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -22,16 +22,19 @@
 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 org.junit.runner.RunWith;
 import org.junit.runners.Suite;
 import org.junit.runners.Suite.SuiteClasses;
 
-import android.app.SystemServiceRegistry_AccessorTest;
 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
@@ -42,8 +45,9 @@
         BridgeXmlBlockParserTest.class, BridgeXmlPullAttributesTest.class,
         Matrix_DelegateTest.class, TestDelegates.class,
         BridgeRenderSessionTest.class, ResourceHelperTest.class, BridgeContextTest.class,
-        SystemServiceRegistry_AccessorTest.class, Resources_DelegateTest.class,
-        Color_DelegateTest.class,
+        Resources_DelegateTest.class, Color_DelegateTest.class, ImagePoolHelperTest.class,
+        ImagePoolImplTest.class, HighQualityShadowsRenderTests.class,
+        LayoutValidatorTests.class, AccessibilityValidatorTests.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 36c09ad..a22c3bc 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
@@ -89,6 +89,16 @@
  */
 public class RenderTestBase {
 
+    /**
+     * Listener for render process.
+     */
+    public interface RenderSessionListener {
+
+        /**
+         * Called before session is disposed after rendering.
+         */
+        void beforeDisposed(RenderSession session);
+    }
     private static final String PLATFORM_DIR_PROPERTY = "platform.dir";
     private static final String RESOURCE_DIR_PROPERTY = "test_res.dir";
 
@@ -308,11 +318,14 @@
                 };
         sProjectResources.loadResources();
 
-        File fontLocation = new File(data_dir, "fonts");
+        // 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 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,
+        sBridge.init(ConfigGenerator.loadProperties(buildProp), fontLocation, null, null,
                 ConfigGenerator.getEnumMap(attrs), getLayoutLog());
         Bridge.getLock().lock();
         try {
@@ -340,6 +353,14 @@
     protected static RenderResult render(com.android.ide.common.rendering.api.Bridge bridge,
             SessionParams params,
             long frameTimeNanos) {
+        return render(bridge, params, frameTimeNanos, null);
+    }
+
+    @NonNull
+    protected static RenderResult render(com.android.ide.common.rendering.api.Bridge bridge,
+            SessionParams params,
+            long frameTimeNanos,
+            @Nullable RenderSessionListener listener) {
         // TODO: Set up action bar handler properly to test menu rendering.
         // Create session params.
         System_Delegate.setBootTimeNanos(TimeUnit.MILLISECONDS.toNanos(871732800000L));
@@ -363,6 +384,9 @@
                             session.getResult().getErrorMessage());
                 }
             }
+            if (listener != null) {
+                listener.beforeDisposed(session);
+            }
 
             return RenderResult.getFromSession(session);
         } finally {
@@ -413,7 +437,7 @@
         if (sLayoutLibLog == null) {
             sLayoutLibLog = new LayoutLog() {
                 @Override
-                public void warning(String tag, String message, Object data) {
+                public void warning(String tag, String message, Object cookie, Object data) {
                     System.out.println("Warning " + tag + ": " + message);
                     failWithMsg(message);
                 }
@@ -430,13 +454,14 @@
                 }
 
                 @Override
-                public void error(String tag, String message, Object data) {
+                public void error(String tag, String message, Object cookie, Object data) {
                     System.out.println("Error " + tag + ": " + message);
                     failWithMsg(message);
                 }
 
                 @Override
-                public void error(String tag, String message, Throwable throwable, Object data) {
+                public void error(String tag, String message, Throwable throwable, Object cookie,
+                        Object data) {
                     System.out.println("Error " + tag + ": " + message);
                     if (throwable != null) {
                         throwable.printStackTrace();
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 2c91ade..303c247 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
@@ -43,6 +43,7 @@
 import org.kxml2.io.KXmlParser;
 import org.xmlpull.v1.XmlPullParser;
 
+import android.R.attr;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.AssetManager;
@@ -156,11 +157,11 @@
         LayoutLibTestCallback layoutLibCallback =
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
-        layoutLibCallback.setUseShadow(false);
         SessionParams params = getSessionParamsBuilder()
                 .setParser(parser)
                 .setConfigGenerator(ConfigGenerator.NEXUS_5)
                 .setCallback(layoutLibCallback)
+                .disableShadows()
                 .build();
 
         renderAndVerify(params, "allwidgets.png");
@@ -184,11 +185,11 @@
         LayoutLibTestCallback layoutLibCallback =
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
-        layoutLibCallback.setUseShadow(false);
         SessionParams params = getSessionParamsBuilder()
                 .setParser(parser)
                 .setConfigGenerator(ConfigGenerator.NEXUS_7_2012)
                 .setCallback(layoutLibCallback)
+                .disableShadows()
                 .build();
         renderAndVerify(params, "allwidgets_tab.png");
 
@@ -331,6 +332,50 @@
         renderAndVerify(params, "expand_horz_layout.png");
     }
 
+    @Test
+    public void testShrink() throws ClassNotFoundException, FileNotFoundException {
+        // Create the layout pull parser.
+        LayoutPullParser parser = createParserFromPath("expand_vert_layout.xml");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        // Normal mode
+        ConfigGenerator customConfigGenerator = new ConfigGenerator()
+                .setScreenWidth(600)
+                .setScreenHeight(3000)
+                .setDensity(Density.XHIGH)
+                .setNavigation(Navigation.NONAV);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(customConfigGenerator)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.NORMAL)
+                .disableDecoration()
+                .build();
+
+        renderAndVerify(params, "normal_layout.png");
+
+        // Shrink mode
+        customConfigGenerator = new ConfigGenerator()
+                .setScreenWidth(600)
+                .setScreenHeight(3000)
+                .setDensity(Density.XHIGH)
+                .setNavigation(Navigation.NONAV);
+        parser = createParserFromPath("expand_vert_layout.xml");
+        params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(customConfigGenerator)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.SHRINK)
+                .disableDecoration()
+                .build();
+
+        renderAndVerify(params, "shrunk_layout.png");
+    }
+
     /** Test indeterminate_progressbar.xml */
     @Test
     public void testVectorAnimation() throws ClassNotFoundException {
@@ -758,7 +803,7 @@
         Configuration configuration = RenderAction.getConfiguration(params);
         BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
                 params.getAssets(), params.getLayoutlibCallback(), configuration,
-                params.getTargetSdkVersion(), params.isRtlSupported());
+                params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
         Resources resources = Resources_Delegate.initSystem(context, assetManager, metrics,
                 configuration, params.getLayoutlibCallback());
         // Test
@@ -800,7 +845,7 @@
         Configuration configuration = RenderAction.getConfiguration(params);
         BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
                 params.getAssets(), params.getLayoutlibCallback(), configuration,
-                params.getTargetSdkVersion(), params.isRtlSupported());
+                params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
         Resources resources = Resources_Delegate.initSystem(context, assetManager, metrics,
                 configuration, params.getLayoutlibCallback());
 
@@ -849,6 +894,7 @@
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
 
+        layoutLibCallback.setAdaptiveIconMaskPath("M50,0L100,0 100,100 0,100 0,0z");
         SessionParams params = getSessionParamsBuilder()
                 .setParser(parser)
                 .setCallback(layoutLibCallback)
@@ -892,7 +938,7 @@
     }
 
     @Test
-    public void testColorTypedValue() throws Exception {
+    public void testTypedValue() throws Exception {
         // Setup
         // Create the layout pull parser for our resources (empty.xml can not be part of the test
         // app as it won't compile).
@@ -912,12 +958,27 @@
         BridgeContext mContext =
                 new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
                         params.getAssets(), params.getLayoutlibCallback(), configuration,
-                        params.getTargetSdkVersion(), params.isRtlSupported());
+                        params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
 
         TypedValue outValue = new TypedValue();
         mContext.resolveThemeAttribute(android.R.attr.colorPrimary, outValue, true);
         assertEquals(TypedValue.TYPE_INT_COLOR_ARGB8, outValue.type);
         assertNotEquals(0, outValue.data);
+
+        outValue = new TypedValue();
+        mContext.resolveThemeAttribute(android.R.attr.colorError, outValue, true);
+        assertEquals(TypedValue.TYPE_INT_COLOR_RGB4, outValue.type);
+        assertEquals(-65536, outValue.data);
+
+        outValue = new TypedValue();
+        mContext.resolveThemeAttribute(attr.colorActivatedHighlight, outValue, true);
+        assertEquals(TypedValue.TYPE_INT_COLOR_ARGB4, outValue.type);
+        assertEquals(-872349952, outValue.data);
+
+        outValue = new TypedValue();
+        mContext.resolveThemeAttribute(android.R.attr.isLightTheme, outValue, true);
+        assertEquals(TypedValue.TYPE_INT_BOOLEAN, outValue.type);
+        assertEquals(1, outValue.data);
         assertTrue(sRenderMessages.isEmpty());
     }
 
@@ -975,7 +1036,7 @@
         BridgeContext mContext =
                 new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
                         params.getAssets(), params.getLayoutlibCallback(), configuration,
-                        params.getTargetSdkVersion(), params.isRtlSupported());
+                        params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
         mContext.initResources();
         BridgeContext oldContext = RenderActionTestUtil.setBridgeContext(mContext);
 
@@ -1032,12 +1093,12 @@
         LayoutLibTestCallback layoutLibCallback =
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
-        layoutLibCallback.setUseShadow(false);
         SessionParams params = getSessionParamsBuilder()
                 .setParser(parser)
                 .setConfigGenerator(ConfigGenerator.NEXUS_5)
                 .setCallback(layoutLibCallback)
                 .disableDecoration()
+                .disableShadows()
                 .build();
 
         renderAndVerify(params, "shadows_test_no_shadow.png");
@@ -1051,12 +1112,12 @@
         LayoutLibTestCallback layoutLibCallback =
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
-        layoutLibCallback.setShadowQuality(false);
         SessionParams params = getSessionParamsBuilder()
                 .setParser(parser)
                 .setConfigGenerator(ConfigGenerator.NEXUS_5)
                 .setCallback(layoutLibCallback)
                 .disableDecoration()
+                .disableHighQualityShadows()
                 .build();
 
         renderAndVerify(params, "shadows_test.png");
@@ -1065,80 +1126,6 @@
     }
 
     @Test
-    public void testHighQualityRectangleShadow() throws Exception {
-        LayoutPullParser parser = createParserFromPath("shadows_test.xml");
-        LayoutLibTestCallback layoutLibCallback =
-                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
-        layoutLibCallback.initResources();
-        layoutLibCallback.setShadowQuality(true);
-        SessionParams params = getSessionParamsBuilder()
-                .setParser(parser)
-                .setConfigGenerator(ConfigGenerator.NEXUS_5)
-                .setCallback(layoutLibCallback)
-                .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."));
-    }
-
-    @Test
-    public void testHighQualityRoundedEdgeRectangleShadow() throws Exception {
-        LayoutPullParser parser = createParserFromPath("shadows_rounded_edge_test.xml");
-        LayoutLibTestCallback layoutLibCallback =
-                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
-        layoutLibCallback.initResources();
-        layoutLibCallback.setShadowQuality(true);
-        SessionParams params = getSessionParamsBuilder()
-                .setParser(parser)
-                .setConfigGenerator(ConfigGenerator.NEXUS_5)
-                .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."));
-    }
-
-    @Test
-    public void testHighQualityLargeViewShadow() throws Exception {
-        LayoutPullParser parser = createParserFromPath("large_view_shadows_test.xml");
-        LayoutLibTestCallback layoutLibCallback =
-                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
-        layoutLibCallback.initResources();
-        layoutLibCallback.setShadowQuality(true);
-        SessionParams params = getSessionParamsBuilder()
-                .setParser(parser)
-                .setConfigGenerator(ConfigGenerator.NEXUS_5)
-                .setCallback(layoutLibCallback)
-                .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."));
-    }
-
-    @Test
-    public void testHighQualityShadowSizes() throws Exception {
-        LayoutPullParser parser = createParserFromPath("shadow_sizes_test.xml");
-        LayoutLibTestCallback layoutLibCallback =
-                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
-        layoutLibCallback.initResources();
-        layoutLibCallback.setShadowQuality(true);
-        SessionParams params = getSessionParamsBuilder()
-                .setParser(parser)
-                .setConfigGenerator(ConfigGenerator.NEXUS_5)
-                .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."));
-    }
-
-    @Test
     public void testResourcesGetIdentifier() throws Exception {
         // Setup
         // Create the layout pull parser for our resources (empty.xml can not be part of the test
@@ -1158,7 +1145,7 @@
         Configuration configuration = RenderAction.getConfiguration(params);
         BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
                 params.getAssets(), params.getLayoutlibCallback(), configuration,
-                params.getTargetSdkVersion(), params.isRtlSupported());
+                params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
         Resources resources = Resources_Delegate.initSystem(context, assetManager, metrics,
                 configuration, params.getLayoutlibCallback());
         Integer id =
@@ -1534,7 +1521,6 @@
         LayoutLibTestCallback layoutLibCallback =
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
-        layoutLibCallback.setUseShadow(false);
 
         LayoutPullParser parser = LayoutPullParser.createFromString(
                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
@@ -1561,6 +1547,7 @@
                         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");
@@ -1693,4 +1680,100 @@
                 TimeUnit.SECONDS.toNanos(2));
     }
 
+    @Test
+    public void testHighQualityShadowWidgetWithScroll() throws Exception {
+        LayoutPullParser parser = createParserFromPath("shadows_scrollview.xml");
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .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."));
+    }
+
+    @Test
+    public void testContentId() throws ClassNotFoundException {
+        final String layout =
+                "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "              android:layout_width=\"match_parent\"\n" +
+                        "              android:layout_height=\"match_parent\">\n" + "\n" +
+                        "    <com.android.layoutlib.bridge.test.widgets.ContentWidget\n" +
+                        "        android:layout_width=\"match_parent\"\n" +
+                        "        android:layout_height=\"wrap_content\"/>\n" +
+                        "</FrameLayout>";
+
+        {
+            // 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)
+                    .build();
+
+            RenderResult result = render(sBridge, params, TimeUnit.SECONDS.toNanos(2));
+            BufferedImage image = result.getImage();
+            assertNotNull(image);
+        }
+
+        {
+            // 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)
+                    .disableDecoration()
+                    .build();
+
+            RenderResult result = render(sBridge, params, TimeUnit.SECONDS.toNanos(2));
+            BufferedImage image = result.getImage();
+            assertNotNull(image);
+        }
+    }
+
+    /**
+     * Tests that the TextClock widget renders without error
+     * <p/>
+     * http://b/150151293
+     */
+    @Test
+    public void testTextClock() throws ClassNotFoundException {
+        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" +
+                        "    <TextClock\n" +
+                        "             android:layout_height=\"wrap_content\"\n" +
+                        "             android:layout_width=\"wrap_content\"\n" +
+                        "             android:text=\"12:34\"" +
+                        "             android:textSize=\"18sp\" />\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, "textclock.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 f2d62d4..a6389ac 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
@@ -108,13 +108,6 @@
         return viewConstructor.newInstance(constructorArgs);
     }
 
-    @NonNull
-    @Override
-    public String getNamespace() {
-        return String.format(SdkConstants.NS_CUSTOM_RESOURCES_S,
-                PACKAGE_NAME);
-    }
-
     @Override
     public ResourceReference resolveResourceId(int id) {
         return mProjectResources.get(id);
@@ -155,11 +148,6 @@
     }
 
     @Override
-    public boolean supports(int ideFeature) {
-        return false;
-    }
-
-    @Override
     @Nullable
     public XmlPullParser createXmlParserForPsiFile(@NonNull String fileName) {
         return createXmlParserForFile(fileName);
@@ -196,32 +184,15 @@
         if (key.equals(RenderParamsFlags.FLAG_KEY_ADAPTIVE_ICON_MASK_PATH)) {
             return (T) mAdaptiveIconMaskPath;
         }
-        if (key.equals(RenderParamsFlags.FLAG_RENDER_HIGH_QUALITY_SHADOW)) {
-            return (T) new Boolean(mHighShadowQuality);
-        }
-        if (key.equals(RenderParamsFlags.FLAG_ENABLE_SHADOW)) {
-            return (T) new Boolean(mSetUseShadow);
-        }
         return null;
     }
 
+    @Override
+    public Class<?> findClass(String name) throws ClassNotFoundException {
+        return mModuleClassLoader.loadClass(name);
+    }
+
     public void setAdaptiveIconMaskPath(String adaptiveIconMaskPath) {
         mAdaptiveIconMaskPath = adaptiveIconMaskPath;
     }
-
-    /**
-     * Enables shadow from rendering. Shadow rendering is enabled by default.
-     * @param useShadow true to enable shadow. False to disable.
-     */
-    public void setUseShadow(boolean useShadow) {
-        mSetUseShadow = useShadow;
-    }
-
-    /**
-     * Sets high quality shadow rendering. Turned off by default.
-     * @param useHighQuality true to use high quality shadow. False otherwise.
-     */
-    public void setShadowQuality(boolean useHighQuality) {
-        mHighShadowQuality = useHighQuality;
-    }
 }
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 321bb77..baadc62 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
@@ -28,6 +28,7 @@
 import com.android.ide.common.resources.ResourceResolver;
 import com.android.ide.common.resources.configuration.FolderConfiguration;
 import com.android.ide.common.resources.deprecated.ResourceRepository;
+import com.android.layoutlib.bridge.android.RenderParamsFlags;
 import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
 import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
 import com.android.resources.ResourceType;
@@ -60,6 +61,10 @@
     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;
 
     @NonNull
     public SessionParamsBuilder setParser(@NonNull LayoutPullParser layoutParser) {
@@ -161,6 +166,30 @@
     }
 
     @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;
+    }
+
+    @NonNull
+    public SessionParamsBuilder enableLayoutValidationImageCheck() {
+        this.enableLayoutValidatorImageCheck = true;
+        return this;
+    }
+
+    @NonNull
     public SessionParams build() {
         assert mFrameworkResources != null;
         assert mProjectResources != null;
@@ -181,6 +210,12 @@
         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,
+                enableLayoutValidatorImageCheck);
         if (mImageFactory != null) {
             params.setImageFactory(mImageFactory);
         }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/ContentWidget.java b/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/ContentWidget.java
new file mode 100644
index 0000000..a8b6f1c
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/ContentWidget.java
@@ -0,0 +1,57 @@
+/*
+ * 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.test.widgets;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class ContentWidget extends View {
+    public ContentWidget(Context context) {
+        super(context);
+    }
+
+    public ContentWidget(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public ContentWidget(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public ContentWidget(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        View parent = this;
+        while (parent != null) {
+            if (parent.getId() == android.R.id.content && parent instanceof ViewGroup) {
+                return;
+            }
+            if (parent.getParent() instanceof ViewGroup) {
+                parent = (ViewGroup) parent.getParent();
+            } else {
+                break;
+            }
+        }
+        throw new RuntimeException("No view with id \"android.R.id.content\" in the hierarchy");
+    }
+}
diff --git a/bridge/tests/src/com/android/tools/idea/validator/LayoutValidatorTests.java b/bridge/tests/src/com/android/tools/idea/validator/LayoutValidatorTests.java
new file mode 100644
index 0000000..ec91e17
--- /dev/null
+++ b/bridge/tests/src/com/android/tools/idea/validator/LayoutValidatorTests.java
@@ -0,0 +1,182 @@
+/*
+ * 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.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.tools.idea.validator.ValidatorData.Issue;
+import com.android.tools.idea.validator.ValidatorData.Level;
+import com.android.tools.idea.validator.ValidatorData.Type;
+
+import org.junit.Test;
+
+import android.view.View;
+
+import java.util.EnumSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckPreset;
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class LayoutValidatorTests extends RenderTestBase {
+
+    @Test
+    public void testRenderAndVerify() throws Exception {
+        LayoutPullParser parser = createParserFromPath("a11y_test1.xml");
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(ConfigGenerator.NEXUS_5)
+                .setCallback(layoutLibCallback)
+                .disableDecoration()
+                .enableLayoutValidation()
+                .build();
+
+        renderAndVerify(params, "a11y_test1.png");
+    }
+
+    @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());
+            for (Issue issue : result.getIssues()) {
+                assertEquals(Type.ACCESSIBILITY, issue.mType);
+                assertEquals(Level.ERROR, issue.mLevel);
+            }
+
+            Issue first = result.getIssues().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);
+
+            Issue second = result.getIssues().get(1);
+            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);
+
+            Issue third = result.getIssues().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.",
+                         third.mMsg);
+            assertEquals("https://support.google.com/accessibility/android/answer/7158390",
+                         third.mHelpfulUrl);
+            assertEquals("TextContrastCheck", third.mSourceClass);
+        });
+    }
+
+    @Test
+    public void testValidationPolicyType() throws Exception {
+        try {
+            ValidatorData.Policy newPolicy = new ValidatorData.Policy(
+                    EnumSet.of(Type.RENDER),
+                    EnumSet.of(Level.ERROR, Level.WARNING));
+            LayoutValidator.updatePolicy(newPolicy);
+
+            render(sBridge, generateParams(), -1, session -> {
+                ValidatorResult result = LayoutValidator.validate(
+                        ((View) session.getRootViews().get(0).getViewObject()), null);
+                assertTrue(result.getIssues().isEmpty());
+            });
+        } finally {
+            LayoutValidator.updatePolicy(LayoutValidator.DEFAULT_POLICY);
+        }
+    }
+
+    @Test
+    public void testValidationPolicyLevel() throws Exception {
+        try {
+            ValidatorData.Policy newPolicy = new ValidatorData.Policy(
+                    EnumSet.of(Type.ACCESSIBILITY, Type.RENDER),
+                    EnumSet.of(Level.VERBOSE));
+            LayoutValidator.updatePolicy(newPolicy);
+
+            render(sBridge, generateParams(), -1, session -> {
+                ValidatorResult result = LayoutValidator.validate(
+                        ((View) session.getRootViews().get(0).getViewObject()), null);
+                assertEquals(27, result.getIssues().size());
+                result.getIssues().forEach(issue ->assertEquals(Level.VERBOSE, issue.mLevel));
+            });
+        } finally {
+            LayoutValidator.updatePolicy(LayoutValidator.DEFAULT_POLICY);
+        }
+    }
+
+    @Test
+    public void testValidationPolicyChecks() throws Exception {
+        Set<AccessibilityHierarchyCheck> allChecks =
+                AccessibilityCheckPreset.getAccessibilityHierarchyChecksForPreset(
+                        AccessibilityCheckPreset.LATEST);
+        Set<AccessibilityHierarchyCheck> filtered =allChecks
+                .stream()
+                .filter(it -> it.getClass().getSimpleName().equals("TextContrastCheck"))
+                .collect(Collectors.toSet());
+        try {
+            ValidatorData.Policy newPolicy = new ValidatorData.Policy(
+                    EnumSet.of(Type.ACCESSIBILITY, Type.RENDER),
+                    EnumSet.of(Level.ERROR));
+            newPolicy.mChecks.addAll(filtered);
+            LayoutValidator.updatePolicy(newPolicy);
+
+            render(sBridge, generateParams(), -1, session -> {
+                ValidatorResult result = LayoutValidator.validate(
+                        ((View) session.getRootViews().get(0).getViewObject()), null);
+                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 " +
+                                "of #000000 and background color of #000000. Consider increasing this item's" +
+                                " text contrast ratio to 4.50 or greater.",
+                        textCheck.mMsg);
+                assertEquals("https://support.google.com/accessibility/android/answer/7158390",
+                        textCheck.mHelpfulUrl);
+                assertEquals("TextContrastCheck", textCheck.mSourceClass);
+            });
+        } finally {
+            LayoutValidator.updatePolicy(LayoutValidator.DEFAULT_POLICY);
+        }
+    }
+
+    private SessionParams generateParams() throws Exception {
+        LayoutPullParser parser = createParserFromPath("a11y_test1.xml");
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        return getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(ConfigGenerator.NEXUS_5)
+                .setCallback(layoutLibCallback)
+                .disableDecoration()
+                .enableLayoutValidation()
+                .build();
+    }
+}
diff --git a/bridge/tests/src/com/android/tools/idea/validator/accessibility/AccessibilityValidatorTests.java b/bridge/tests/src/com/android/tools/idea/validator/accessibility/AccessibilityValidatorTests.java
new file mode 100644
index 0000000..16ad4a2
--- /dev/null
+++ b/bridge/tests/src/com/android/tools/idea/validator/accessibility/AccessibilityValidatorTests.java
@@ -0,0 +1,338 @@
+/*
+ * 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.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.Test;
+
+import java.util.EnumSet;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Sanity check for a11y checks. For now it lacks checking the following:
+ * - ClassNameCheck
+ * - ClickableSpanCheck
+ * - EditableContentDescCheck
+ * - LinkPurposeUnclearCheck
+ * As these require more complex UI for testing.
+ *
+ * It's also missing:
+ * - TraversalOrderCheck
+ * Because in Layoutlib test env, traversalBefore/after attributes seems to be lost. Tested on
+ * studio and it seems to work ok.
+ */
+public class AccessibilityValidatorTests extends RenderTestBase {
+
+    @Test
+    public void testDuplicateClickableBoundsCheck() throws Exception {
+        render("a11y_test_dup_clickable_bounds.xml", session -> {
+            ValidatorResult result = getRenderResult(session);
+            List<Issue> dupBounds = filter(result.getIssues(), "DuplicateClickableBoundsCheck");
+
+            ExpectedLevels expectedLevels = new ExpectedLevels();
+            expectedLevels.expectedErrors = 1;
+            expectedLevels.check(dupBounds);
+        });
+    }
+
+    @Test
+    public void testDuplicateSpeakableTextsCheck() throws Exception {
+        render("a11y_test_duplicate_speakable.xml", session -> {
+            ValidatorResult result = getRenderResult(session);
+            List<Issue> duplicateSpeakableTexts = filter(result.getIssues(),
+                    "DuplicateSpeakableTextCheck");
+
+            ExpectedLevels expectedLevels = new ExpectedLevels();
+            expectedLevels.expectedInfos = 1;
+            expectedLevels.expectedWarnings = 1;
+            expectedLevels.check(duplicateSpeakableTexts);
+        });
+    }
+
+    @Test
+    public void testRedundantDescriptionCheck() throws Exception {
+        render("a11y_test_redundant_desc.xml", session -> {
+            ValidatorResult result = getRenderResult(session);
+            List<Issue> redundant = filter(result.getIssues(), "RedundantDescriptionCheck");
+
+            ExpectedLevels expectedLevels = new ExpectedLevels();
+            expectedLevels.expectedVerboses = 3;
+            expectedLevels.expectedWarnings = 1;
+            expectedLevels.check(redundant);
+        });
+    }
+
+    @Test
+    public void testLabelFor() throws Exception {
+        render("a11y_test_speakable_text_present.xml", session -> {
+            ValidatorResult result = getRenderResult(session);
+            List<Issue> speakableCheck = filter(result.getIssues(), "SpeakableTextPresentCheck");
+
+            // Post-JB MR2 support labelFor, so SpeakableTextPresentCheck does not need to find any
+            // speakable text. Expected 1 verbose result saying something along the line of
+            // didn't run or not important for a11y.
+            ExpectedLevels expectedLevels = new ExpectedLevels();
+            expectedLevels.expectedVerboses = 1;
+            expectedLevels.check(speakableCheck);
+        });
+    }
+
+    @Test
+    public void testImportantForAccessibility() throws Exception {
+        render("a11y_test_speakable_text_present2.xml", session -> {
+            ValidatorResult result = getRenderResult(session);
+            List<Issue> speakableCheck = filter(result.getIssues(), "SpeakableTextPresentCheck");
+
+            // Post-JB MR2 support importantForAccessibility, so SpeakableTextPresentCheck
+            // does not need to find any speakable text. Expected 2 verbose results.
+            ExpectedLevels expectedLevels = new ExpectedLevels();
+            expectedLevels.expectedVerboses = 2;
+            expectedLevels.check(speakableCheck);
+        });
+    }
+
+    @Test
+    public void testSpeakableTextPresentCheck() throws Exception {
+        render("a11y_test_speakable_text_present3.xml", session -> {
+            ValidatorResult result = getRenderResult(session);
+            List<Issue> speakableCheck = filter(result.getIssues(), "SpeakableTextPresentCheck");
+
+            ExpectedLevels expectedLevels = new ExpectedLevels();
+            expectedLevels.expectedVerboses = 1;
+            expectedLevels.expectedErrors = 1;
+            expectedLevels.check(speakableCheck);
+
+            // Make sure no other errors in the system.
+            speakableCheck = filter(speakableCheck, EnumSet.of(Level.ERROR));
+            assertEquals(1, speakableCheck.size());
+            List<Issue> allErrors = filter(
+                    result.getIssues(), EnumSet.of(Level.ERROR, Level.WARNING, Level.INFO));
+            checkEquals(speakableCheck, allErrors);
+        });
+    }
+
+    @Test
+    public void testTextContrastCheck() throws Exception {
+        render("a11y_test_text_contrast.xml", session -> {
+            ValidatorResult result = getRenderResult(session);
+            List<Issue> textContrast = filter(result.getIssues(), "TextContrastCheck");
+
+            // ATF doesn't count alpha values unless image is passed.
+            ExpectedLevels expectedLevels = new ExpectedLevels();
+            expectedLevels.expectedErrors = 3;
+            expectedLevels.expectedWarnings = 1; // This is true only if image is passed.
+            expectedLevels.expectedVerboses = 2;
+            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);
+        });
+    }
+
+    @Test
+    public void testTextContrastCheckNoImage() throws Exception {
+        render("a11y_test_text_contrast.xml", session -> {
+            ValidatorResult result = getRenderResult(session);
+            List<Issue> textContrast = filter(result.getIssues(), "TextContrastCheck");
+
+            // ATF doesn't count alpha values unless image is passed.
+            ExpectedLevels expectedLevels = new ExpectedLevels();
+            expectedLevels.expectedErrors = 3;
+            expectedLevels.expectedVerboses = 3;
+            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);
+        }, false);
+    }
+
+    @Test
+    public void testImageContrastCheck() throws Exception {
+        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);
+
+            // Make sure no other errors in the system.
+            imageContrast = filter(imageContrast, EnumSet.of(Level.ERROR, Level.WARNING));
+            List<Issue> filtered = filter(result.getIssues(), EnumSet.of(Level.ERROR, Level.WARNING));
+            checkEquals(filtered, imageContrast);
+        });
+    }
+
+    @Test
+    public void testImageContrastCheckNoImage() throws Exception {
+        render("a11y_test_image_contrast.xml", session -> {
+            ValidatorResult result = getRenderResult(session);
+            List<Issue> imageContrast = filter(result.getIssues(), "ImageContrastCheck");
+
+            ExpectedLevels expectedLevels = new ExpectedLevels();
+            expectedLevels.expectedVerboses = 3;
+            expectedLevels.check(imageContrast);
+
+            // Make sure no other errors in the system.
+            imageContrast = filter(imageContrast, EnumSet.of(Level.ERROR, Level.WARNING));
+            List<Issue> filtered = filter(result.getIssues(), EnumSet.of(Level.ERROR, Level.WARNING));
+            checkEquals(filtered, imageContrast);
+        }, false);
+    }
+
+    @Test
+    public void testTouchTargetSizeCheck() throws Exception {
+        render("a11y_test_touch_target_size.xml", session -> {
+            ValidatorResult result = getRenderResult(session);
+            List<Issue> targetSizes = filter(result.getIssues(), "TouchTargetSizeCheck");
+
+            ExpectedLevels expectedLevels = new ExpectedLevels();
+            expectedLevels.expectedErrors = 5;
+            expectedLevels.expectedVerboses = 1;
+            expectedLevels.check(targetSizes);
+
+            // Make sure no other errors in the system.
+            targetSizes = filter(targetSizes, EnumSet.of(Level.ERROR));
+            List<Issue> filtered = filter(result.getIssues(), EnumSet.of(Level.ERROR));
+            checkEquals(filtered, targetSizes);
+        });
+    }
+
+    private void checkEquals(List<Issue> list1, List<Issue> list2) {
+        assertEquals(list1.size(), list2.size());
+        for (int i = 0; i < list1.size(); i++) {
+            assertEquals(list1.get(i), list2.get(i));
+        }
+    }
+
+    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;
+    }
+    private void render(String fileName, RenderSessionListener verifier) throws Exception {
+        render(fileName, verifier, true);
+    }
+
+    private void render(
+            String fileName,
+            RenderSessionListener verifier,
+            boolean enableImageCheck) throws Exception {
+        LayoutValidator.updatePolicy(new Policy(
+                EnumSet.of(Type.ACCESSIBILITY, Type.RENDER),
+                EnumSet.of(Level.ERROR, Level.WARNING, Level.INFO, Level.VERBOSE)));
+
+        LayoutPullParser parser = createParserFromPath(fileName);
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        SessionParamsBuilder params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(ConfigGenerator.NEXUS_5)
+                .setCallback(layoutLibCallback)
+                .disableDecoration()
+                .enableLayoutValidation();
+
+        if (enableImageCheck) {
+            params.enableLayoutValidationImageCheck();
+        }
+
+        render(sBridge, params.build(), -1, verifier);
+    }
+
+    /**
+     * Helper class that checks the list of issues..
+     */
+    private static class ExpectedLevels {
+        // Number of errors expected
+        public int expectedErrors = 0;
+        // Number of warnings expected
+        public int expectedWarnings = 0;
+        // Number of infos expected
+        public int expectedInfos = 0;
+        // Number of verboses expected
+        public int expectedVerboses = 0;
+
+        public void check(List<Issue> issues) {
+            int errors = 0;
+            int warnings = 0;
+            int infos = 0;
+            int verboses = 0;
+
+            for (Issue issue : issues) {
+                switch (issue.mLevel) {
+                    case ERROR:
+                        errors++;
+                        break;
+                    case WARNING:
+                        warnings++;
+                        break;
+                    case INFO:
+                        infos++;
+                        break;
+                    case VERBOSE:
+                        verboses++;
+                        break;
+                }
+            }
+
+            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);
+
+            int size = expectedErrors + expectedWarnings + expectedInfos + expectedVerboses;
+            assertEquals("expected size", size, issues.size());
+        }
+    };
+}
diff --git a/create/.settings/README.txt b/create/.settings/README.txt
deleted file mode 100644
index 9120b20..0000000
--- a/create/.settings/README.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Copy this in eclipse project as a .settings folder at the root.
-This ensure proper compilation compliance and warning/error levels.
\ No newline at end of file
diff --git a/create/.settings/org.eclipse.jdt.core.prefs b/create/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index 5381a0e..0000000
--- a/create/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,93 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.annotation.nonnull=com.android.annotations.NonNull
-org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=com.android.annotations.NonNullByDefault
-org.eclipse.jdt.core.compiler.annotation.nonnullisdefault=disabled
-org.eclipse.jdt.core.compiler.annotation.nullable=com.android.annotations.Nullable
-org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.6
-org.eclipse.jdt.core.compiler.debug.lineNumber=generate
-org.eclipse.jdt.core.compiler.debug.localVariable=generate
-org.eclipse.jdt.core.compiler.debug.sourceFile=generate
-org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
-org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
-org.eclipse.jdt.core.compiler.problem.deadCode=warning
-org.eclipse.jdt.core.compiler.problem.deprecation=warning
-org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
-org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
-org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
-org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
-org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
-org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
-org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
-org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
-org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
-org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
-org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
-org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
-org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
-org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
-org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
-org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
-org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
-org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
-org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
-org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
-org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
-org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
-org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
-org.eclipse.jdt.core.compiler.problem.nullReference=error
-org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo=warning
-org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
-org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
-org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
-org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
-org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
-org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation=error
-org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning
-org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
-org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
-org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
-org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
-org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
-org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
-org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
-org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
-org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
-org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
-org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
-org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
-org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled
-org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
-org.eclipse.jdt.core.compiler.problem.unclosedCloseable=error
-org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
-org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
-org.eclipse.jdt.core.compiler.problem.unusedImport=warning
-org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
-org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
-org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
-org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
-org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
-org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
-org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
-org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
-org.eclipse.jdt.core.compiler.source=1.6
diff --git a/create/Android.bp b/create/Android.bp
index 961456a..44179a9 100644
--- a/create/Android.bp
+++ b/create/Android.bp
@@ -22,6 +22,7 @@
     main_class: "com.android.tools.layoutlib.create.Main",
     static_libs: [
         "asm-6.0",
+        "asm-commons-6.0",
         "guava",
         "layoutlib-common",
     ],
diff --git a/create/README.txt b/create/README.txt
index 727b194..5625675 100644
--- a/create/README.txt
+++ b/create/README.txt
@@ -4,8 +4,8 @@
 - Description -
 ---------------
 
-Layoutlib_create generates a JAR library used by the Eclipse graphical layout editor to perform
-layout.
+Layoutlib_create generates a JAR library used by the Android Studio graphical layout editors to perform
+layout and rendering.
 
 
 - Usage -
@@ -20,10 +20,10 @@
 Layoutlib_create uses a few jars from the framework containing the Java code used by Android as
 generated by the Android build, right before the classes are converted to a DEX format.
 
-These jars can't be used directly in Eclipse as:
-- they contains references to native code (which we want to avoid in Eclipse),
+These jars can't be used directly in Android Studio as:
+- they contains references to native code (which we cannot support in Android Studio at the moment, but working on it),
 - some classes need to be overridden, for example all the drawing code that is replaced by Java 2D
-  calls in Eclipse.
+  calls.
 - some of the classes that need to be changed are final and/or we need access to their private
   internal state.
 
@@ -32,18 +32,18 @@
 - modifies some of the classes directly using some bytecode manipulation,
 - filters some packages and removes those we don't want in the output JAR,
 - injects some new classes,
-- generates a modified JAR file that is suitable for the Android plugin for Eclipse to perform
+- generates a modified JAR file that is suitable for the Android Studio to perform
   rendering.
 
 The ASM library is used to do the bytecode modification using its visitor pattern API.
 
 The layoutlib_create is *NOT* generic. There is no configuration file. Instead all the configuration
 is done in the main() method and the CreateInfo structure is expected to change with the Android
-platform as new classes are added, changed or removed. Some configuration that may be platform
-dependent is also present elsewhere in code.
+platform as new classes are added, changed or removed. See src/com/android/tools/layoutlib/create/CreateInfo.java
+for more details. Some configuration that may be platform dependent is also present elsewhere in code.
 
 The resulting JAR is used by layoutlib_bridge (a.k.a. "the bridge"), also part of the platform, that
-provides all the necessary missing implementation for rendering graphics in Eclipse.
+provides all the necessary missing implementation for rendering graphics in Android Studio.
 
 
 
@@ -67,14 +67,14 @@
 
 The analyzer is also given a list of class names to keep in the output. This is done using
 shell-like glob patterns that filter on the fully-qualified class names, for example "android.*.R**"
-("*" does not matches dots whilst "**" does, and "." and "$" are interpreted as-is). In practice we
+("*" does not match dots whilst "**" does, and "." and "$" are interpreted as-is). In practice we
 almost but not quite request the inclusion of full packages.
 
 The analyzer is also given a list of classes to exclude. A fake implementation of these classes is
 injected by the Generator.
 
 With this information, the analyzer parses the input zip to find all the classes. All classes
-deriving from the requested bases classes are kept. All classes whose name match the glob pattern
+deriving from the requested base classes are kept. All classes whose name match the glob pattern
 are kept. The analysis then finds all the dependencies of the classes that are to be kept using an
 ASM visitor on the class, the field types, the method types and annotations types. Classes that
 belong to the current JRE are excluded.
@@ -88,7 +88,7 @@
 
 The generator is constructed from a CreateInfo struct that acts as a config file and lists:
 - the classes to inject in the output JAR -- these classes are directly implemented in
-  layoutlib_create and will be used to interface with the renderer in Eclipse.
+  layoutlib_create and will be used to interface with the Java 2D renderer.
 - specific methods to override (see method stubs details below).
 - specific methods for which to delegate calls.
 - specific methods to remove based on their return type.
@@ -96,7 +96,7 @@
 - specific classes to refactor.
 
 Each of these are specific strategies we use to be able to modify the Android code to fit within the
-Eclipse renderer. These strategies are explained below.
+Java 2D renderer. These strategies are explained below.
 
 The core method of the generator is transform(): it takes an input ASM ClassReader and modifies it
 to produce a byte array suitable for the final JAR file.
@@ -104,7 +104,7 @@
 The first step of the transformation is to implement the method delegates.
 
 The TransformClassAdapter is then used to process the potentially renamed class.  All protected or
-private classes are market as public. All classes are made non-final. Interfaces are left as-is.
+private classes are marked as public. All classes are made non-final. Interfaces are left as-is.
 
 If a method has a return type that must be erased, the whole method is skipped.  Methods are also
 changed from protected/private to public. The code of the methods is then kept as-is, except for
@@ -124,13 +124,6 @@
 modified to update all references to these non-desktop classes. An alternate implementation of
 these (com.android.tools.layoutlib.java.*) is injected.
 
-RenameClassAdapter and RefactorClassAdapter both inherit from AbstractClassAdapter which changes the
-class version (version of the JDK used to compile the class) to 50 (corresponding to Java 6), if the
-class was originally compiled with Java 7 (version 51). This is because we don't currently generate
-the StackMapTable correctly and Java 7 VM enforces that classes with version greater than 51 have
-valid StackMapTable. As a side benefit of this, we can continue to support Java 6 because Java 7 on
-Mac has horrible font rendering support.
-
 ReplaceMethodCallsAdapter replaces calls to certain methods. This is different from the
 DelegateMethodAdapter since it doesn't preserve the original copy of the method and more importantly
 changes the calls to a method in each class instead of changing the implementation of the method.
@@ -147,7 +140,7 @@
 --------------
 
 As indicated above, all native and overridden methods are replaced by a stub.  We don't have the
-code to replace with in layoutlib_create. Instead the StubMethodAdapter replaces the code of the
+code to replace with in layoutlib_create. Instead the StubCallMethodAdapter replaces the code of the
 method by a call to OverrideMethod.invokeX(). When using the final JAR, the bridge can register
 listeners from these overridden method calls based on the method signatures.
 
@@ -162,7 +155,7 @@
 ------------
 
 We currently have 6 strategies to deal with overriding the rendering code and make it run in
-Eclipse. Most of these strategies are implemented hand-in-hand by the bridge (which runs in Eclipse)
+Android Studio. Most of these strategies are implemented hand-in-hand by the bridge (which runs in Android Studio)
 and the generator.
 
 
@@ -206,7 +199,7 @@
 code of _original_Paint if it so desires.
 
 Some of the Android classes are basically wrappers over native objects and since we don't have the
-native code in Eclipse, we need to provide a full alternate implementation. Sub-classing doesn't
+native code in Android Studio, we need to provide a full alternate implementation. Sub-classing doesn't
 work as some native methods are static and we don't control object creation.
 
 This won't rename/replace the inner static methods of a given class.
diff --git a/create/create.iml b/create/create.iml
index 8814d91..b2a53d2 100644
--- a/create/create.iml
+++ b/create/create.iml
@@ -4,17 +4,27 @@
     <exclude-output />
     <content url="file://$MODULE_DIR$">
       <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/tests/data" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/tests/mock_data" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/tests/src" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/tests/res" type="java-test-resource" />
       <excludeFolder url="file://$MODULE_DIR$/.settings" />
     </content>
-    <orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
+    <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
     <orderEntry type="module-library">
       <library name="asm-6.0">
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/asm/asm-6.0.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/prebuilts/misc/common/asm/asm-6.0/linux_glibc_common/combined/asm-6.0.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/asm/src-6.0.zip!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library name="asm-commons-6.0">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/prebuilts/misc/common/asm/asm-commons-6.0/linux_glibc_common/combined/asm-commons-6.0.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES>
@@ -24,16 +34,7 @@
     </orderEntry>
     <orderEntry type="library" scope="TEST" name="junit" level="project" />
     <orderEntry type="module" module-name="common" />
-    <orderEntry type="module-library">
-      <library name="guava">
-        <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/22.0/guava-22.0.jar!/" />
-        </CLASSES>
-        <JAVADOC />
-        <SOURCES>
-          <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/22.0/guava-22.0-sources.jar!/" />
-        </SOURCES>
-      </library>
-    </orderEntry>
+    <orderEntry type="library" name="guava" level="project" />
+    <orderEntry type="library" scope="TEST" name="hamcrest" level="project" />
   </component>
-</module>
+</module>
\ No newline at end of file
diff --git a/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
index 35fc584..f468288 100644
--- a/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
+++ b/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -371,9 +371,9 @@
             cv = new PromoteClassClassAdapter(cv, mPromotedClasses);
         }
 
-        // Make sure no class file has a version above 52 (corresponding to Java 8),
-        // so that layoutlib can be run with JDK 8.
-        cv = new ChangeFileVersionAdapter(mLog, 52, 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);
 
         cr.accept(cv, 0);
 
diff --git a/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 02766e2..bbe985e 100644
--- a/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -153,7 +153,6 @@
         "android.content.res.Resources$Theme#obtainStyledAttributes",
         "android.content.res.Resources$Theme#resolveAttribute",
         "android.content.res.Resources$Theme#resolveAttributes",
-        "android.content.res.AssetManager#open",
         "android.content.res.AssetManager#nativeCreate",
         "android.content.res.AssetManager#nativeDestroy",
         "android.content.res.AssetManager#nativeThemeCreate",
@@ -168,9 +167,11 @@
         "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#onDraw",
         "android.graphics.drawable.GradientDrawable#buildRing",
         "android.graphics.drawable.AdaptiveIconDrawable#<init>",
+        "android.graphics.drawable.DrawableInflater#inflateFromClass",
         "android.graphics.FontFamily#addFont",
         "android.graphics.Typeface#create",
         "android.graphics.Typeface$Builder#createAssetUid",
+        "android.graphics.fonts.Font$Builder#createBuffer",
         "android.graphics.fonts.SystemFonts#buildSystemFallback",
         "android.os.Binder#getNativeBBinderHolder",
         "android.os.Binder#getNativeFinalizer",
@@ -189,6 +190,7 @@
         "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",
@@ -287,6 +289,7 @@
         "android.graphics.SumPathEffect",
         "android.graphics.SweepGradient",
         "android.graphics.Typeface",
+        "android.graphics.animation.NativeInterpolatorFactory",
         "android.graphics.drawable.AnimatedVectorDrawable",
         "android.graphics.drawable.VectorDrawable",
         "android.graphics.fonts.Font$Builder",
@@ -299,7 +302,6 @@
         "android.util.PathParser",
         "android.view.Display",
         "com.android.internal.util.VirtualRefBasePtr",
-        "com.android.internal.view.animation.NativeInterpolatorFactoryHelper",
         "libcore.icu.ICU",
     };
 
@@ -311,9 +313,9 @@
         new String[] {
             "android.os.ServiceManager",                       "android.os._Original_ServiceManager",
             "android.view.textservice.TextServicesManager",    "android.view.textservice._Original_TextServicesManager",
-            "android.util.LruCache",                           "android.util._Original_LruCache",
             "android.view.SurfaceView",                        "android.view._Original_SurfaceView",
             "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",
         };
@@ -325,6 +327,7 @@
      */
     private final static String[] JAVA_PKG_CLASSES =
         new String[] {
+                "sun.misc.Cleaner",                                "com.android.layoutlib.bridge.libcore.util.Cleaner",
         };
 
     /**
@@ -344,6 +347,7 @@
             "android.preference.PreferenceActivity",
             "java.**",
             "org.kxml2.io.KXmlParser",
+            "org.xmlpull.**",
             "sun.**",
         };
 
@@ -358,14 +362,13 @@
         "android.graphics.Typeface#DEFAULT_FAMILY",
         "android.graphics.Typeface#sDynamicTypefaceCache",
         "android.graphics.drawable.AdaptiveIconDrawable#sMask",
+        "android.graphics.drawable.DrawableInflater#mRes",
         "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",
-        "libcore.util.NativeAllocationRegistry#freeFunction",
-        "libcore.util.NativeAllocationRegistry#size",
     };
 
     /**
@@ -373,8 +376,6 @@
      * if possible.
      */
     private final static String[] PROMOTED_CLASSES = new String[] {
-        "libcore.util.NativeAllocationRegistry$CleanerRunner",
-        "libcore.util.NativeAllocationRegistry$CleanerThunk",
     };
 
     /**
diff --git a/create/src/com/android/tools/layoutlib/create/Main.java b/create/src/com/android/tools/layoutlib/create/Main.java
index 3037539..4acc754 100644
--- a/create/src/com/android/tools/layoutlib/create/Main.java
+++ b/create/src/com/android/tools/layoutlib/create/Main.java
@@ -136,6 +136,8 @@
                         "android.annotation.Nullable",      // annotations
                         "com.android.internal.transition.EpicenterTranslateClipReveal",
                         "com.android.internal.graphics.drawable.AnimationScaleListDrawable",
+                        "com.google.android.apps.common.testing.accessibility.**",
+                        "com.google.android.libraries.accessibility.**",
                     },
                     info.getExcludedClasses(),
                     new String[] {
diff --git a/create/src/com/android/tools/layoutlib/create/RefactorClassAdapter.java b/create/src/com/android/tools/layoutlib/create/RefactorClassAdapter.java
index 024e32f..c97a5cb 100644
--- a/create/src/com/android/tools/layoutlib/create/RefactorClassAdapter.java
+++ b/create/src/com/android/tools/layoutlib/create/RefactorClassAdapter.java
@@ -16,89 +16,39 @@
 
 package com.android.tools.layoutlib.create;
 
-import java.util.Arrays;
-import java.util.HashMap;
+import java.util.Map;
 
 import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.commons.ClassRemapper;
+import org.objectweb.asm.commons.Remapper;
 
-public class RefactorClassAdapter extends AbstractClassAdapter {
+public class RefactorClassAdapter extends ClassRemapper {
 
-    private final HashMap<String, String> mRefactorClasses;
-
-    RefactorClassAdapter(ClassVisitor cv, HashMap<String, String> refactorClasses) {
-        super(cv);
-        mRefactorClasses = refactorClasses;
+    RefactorClassAdapter(ClassVisitor cv, Map<String, String> refactorClasses) {
+        super(cv, new RefactorRemapper(refactorClasses));
     }
 
-    @Override
-    public MethodVisitor visitMethod(int access, String name, String desc, String signature,
-            String[] exceptions) {
-        MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
+    private static class RefactorRemapper extends Remapper {
+        private final Map<String, String> mRefactorClasses;
 
-        return new RefactorStackMapAdapter(mw);
-    }
-
-    @Override
-    protected String renameInternalType(String oldClassName) {
-        if (oldClassName != null) {
-            String newName = mRefactorClasses.get(oldClassName);
-            if (newName != null) {
-                return newName;
-            }
-            int pos = oldClassName.indexOf('$');
-            if (pos > 0) {
-                newName = mRefactorClasses.get(oldClassName.substring(0, pos));
-                if (newName != null) {
-                    return newName + oldClassName.substring(pos);
-                }
-            }
-        }
-        return oldClassName;
-    }
-
-    /**
-     * A method visitor that renames all references from an old class name to a new class name in
-     * the stackmap of the method.
-     */
-    private class RefactorStackMapAdapter extends MethodVisitor {
-
-        private RefactorStackMapAdapter(MethodVisitor mv) {
-            super(Main.ASM_VERSION, mv);
-        }
-
-
-        private Object[] renameFrame(Object[] elements) {
-            if (elements == null) {
-                return null;
-            }
-
-            // The input array cannot be modified. We only copy the source array on write
-            boolean copied = false;
-            for (int i = 0; i < elements.length; i++) {
-                if (!(elements[i] instanceof String)) {
-                    continue;
-                }
-
-                if (!copied) {
-                    elements = Arrays.copyOf(elements, elements.length);
-                    copied = true;
-                }
-
-                String type = (String)elements[i];
-                if (type.indexOf(';') > 0) {
-                    elements[i] = renameTypeDesc(type);
-                } else {
-                    elements[i] = renameInternalType(type);
-                }
-            }
-
-            return elements;
+        private RefactorRemapper(Map<String, String> refactorClasses) {
+            mRefactorClasses = refactorClasses;
         }
 
         @Override
-        public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
-            super.visitFrame(type, nLocal, renameFrame(local), nStack, renameFrame(stack));
+        public String map(String typeName) {
+            String newName = mRefactorClasses.get(typeName);
+            if (newName != null) {
+                return newName;
+            }
+            int pos = typeName.indexOf('$');
+            if (pos > 0) {
+                newName = mRefactorClasses.get(typeName.substring(0, pos));
+                if (newName != null) {
+                    return newName + typeName.substring(pos);
+                }
+            }
+            return null;
         }
     }
 }
diff --git a/create/tests/Android.bp b/create/tests/Android.bp
index 67c1bb0..c765707 100644
--- a/create/tests/Android.bp
+++ b/create/tests/Android.bp
@@ -16,16 +16,14 @@
     name: "layoutlib-create-tests",
 
     // Only compile source java files in this lib.
-    srcs: ["com/**/*.java"],
+    srcs: ["src/**/*.java"],
 
-    java_resource_dirs: [
-        "data",
-        "mock_data",
-    ],
+    java_resource_dirs: ["res"],
 
     libs: [
         "layoutlib_create",
         "junit",
+        "hamcrest",
     ],
     static_libs: ["asm-6.0"],
 
@@ -41,6 +39,6 @@
 java_library_host {
     name: "mock_android",
 
-    srcs: ["mock_data/**/*.java"],
-    java_resource_dirs: ["mock_data"],
+    srcs: ["res/mock_data/**/*.java"],
+    java_resource_dirs: ["res/mock_data"],
 }
diff --git a/create/tests/data/mock_android.jar b/create/tests/data/mock_android.jar
deleted file mode 100644
index 3fd6999..0000000
--- a/create/tests/data/mock_android.jar
+++ /dev/null
Binary files differ
diff --git a/create/tests/res/data/mock_android.jar b/create/tests/res/data/mock_android.jar
new file mode 100644
index 0000000..580e6f1
--- /dev/null
+++ b/create/tests/res/data/mock_android.jar
Binary files differ
diff --git a/create/tests/mock_data/mock_android/data/anotherDataFile b/create/tests/res/mock_data/mock_android/data/anotherDataFile
similarity index 100%
rename from create/tests/mock_data/mock_android/data/anotherDataFile
rename to create/tests/res/mock_data/mock_android/data/anotherDataFile
diff --git a/create/tests/mock_data/mock_android/data/dataFile b/create/tests/res/mock_data/mock_android/data/dataFile
similarity index 100%
rename from create/tests/mock_data/mock_android/data/dataFile
rename to create/tests/res/mock_data/mock_android/data/dataFile
diff --git a/create/tests/mock_data/mock_android/fake/FakeClass.java b/create/tests/res/mock_data/mock_android/fake/FakeClass.java
similarity index 100%
rename from create/tests/mock_data/mock_android/fake/FakeClass.java
rename to create/tests/res/mock_data/mock_android/fake/FakeClass.java
diff --git a/create/tests/mock_data/mock_android/fake/InnerTest.java b/create/tests/res/mock_data/mock_android/fake/InnerTest.java
similarity index 100%
rename from create/tests/mock_data/mock_android/fake/InnerTest.java
rename to create/tests/res/mock_data/mock_android/fake/InnerTest.java
diff --git a/create/tests/mock_data/mock_android/fake/subpackage/SubpackageClassA.java b/create/tests/res/mock_data/mock_android/fake/subpackage/SubpackageClassA.java
similarity index 100%
rename from create/tests/mock_data/mock_android/fake/subpackage/SubpackageClassA.java
rename to create/tests/res/mock_data/mock_android/fake/subpackage/SubpackageClassA.java
diff --git a/create/tests/mock_data/mock_android/fake/subpackage/SubpackageClassB.java b/create/tests/res/mock_data/mock_android/fake/subpackage/SubpackageClassB.java
similarity index 100%
rename from create/tests/mock_data/mock_android/fake/subpackage/SubpackageClassB.java
rename to create/tests/res/mock_data/mock_android/fake/subpackage/SubpackageClassB.java
diff --git a/create/tests/mock_data/mock_android/fake/subpackage/SubpackageClassC.java b/create/tests/res/mock_data/mock_android/fake/subpackage/SubpackageClassC.java
similarity index 100%
rename from create/tests/mock_data/mock_android/fake/subpackage/SubpackageClassC.java
rename to create/tests/res/mock_data/mock_android/fake/subpackage/SubpackageClassC.java
diff --git a/create/tests/mock_data/mock_android/fake2/FakeClass.java b/create/tests/res/mock_data/mock_android/fake2/FakeClass.java
similarity index 100%
rename from create/tests/mock_data/mock_android/fake2/FakeClass.java
rename to create/tests/res/mock_data/mock_android/fake2/FakeClass.java
diff --git a/create/tests/mock_data/mock_android/fake2/keep/DoNotRemove.java b/create/tests/res/mock_data/mock_android/fake2/keep/DoNotRemove.java
similarity index 100%
rename from create/tests/mock_data/mock_android/fake2/keep/DoNotRemove.java
rename to create/tests/res/mock_data/mock_android/fake2/keep/DoNotRemove.java
diff --git a/create/tests/mock_data/mock_android/util/EmptyArray.java b/create/tests/res/mock_data/mock_android/util/EmptyArray.java
similarity index 100%
rename from create/tests/mock_data/mock_android/util/EmptyArray.java
rename to create/tests/res/mock_data/mock_android/util/EmptyArray.java
diff --git a/create/tests/mock_data/mock_android/util/NotNeeded.java b/create/tests/res/mock_data/mock_android/util/NotNeeded.java
similarity index 100%
rename from create/tests/mock_data/mock_android/util/NotNeeded.java
rename to create/tests/res/mock_data/mock_android/util/NotNeeded.java
diff --git a/create/tests/mock_data/mock_android/view/View.java b/create/tests/res/mock_data/mock_android/view/View.java
similarity index 100%
rename from create/tests/mock_data/mock_android/view/View.java
rename to create/tests/res/mock_data/mock_android/view/View.java
diff --git a/create/tests/mock_data/mock_android/view/ViewGroup.java b/create/tests/res/mock_data/mock_android/view/ViewGroup.java
similarity index 100%
rename from create/tests/mock_data/mock_android/view/ViewGroup.java
rename to create/tests/res/mock_data/mock_android/view/ViewGroup.java
diff --git a/create/tests/mock_data/mock_android/widget/LinearLayout.java b/create/tests/res/mock_data/mock_android/widget/LinearLayout.java
similarity index 100%
rename from create/tests/mock_data/mock_android/widget/LinearLayout.java
rename to create/tests/res/mock_data/mock_android/widget/LinearLayout.java
diff --git a/create/tests/mock_data/mock_android/widget/TableLayout.java b/create/tests/res/mock_data/mock_android/widget/TableLayout.java
similarity index 100%
rename from create/tests/mock_data/mock_android/widget/TableLayout.java
rename to create/tests/res/mock_data/mock_android/widget/TableLayout.java
diff --git a/create/tests/mock_data/notjava/lang/JavaClass.java b/create/tests/res/mock_data/notjava/lang/JavaClass.java
similarity index 100%
rename from create/tests/mock_data/notjava/lang/JavaClass.java
rename to create/tests/res/mock_data/notjava/lang/JavaClass.java
diff --git a/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java b/create/tests/src/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
similarity index 99%
rename from create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
rename to create/tests/src/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
index b95a120..ad9055c 100644
--- a/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 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.
@@ -129,22 +129,22 @@
                 DEFAULT_INCLUDE_FILES);
         Result result = analyzer.analyze();
         assertArrayEquals(new String[] {
-                        "mock_android.fake.InnerTest$NotStaticInner1",
                         "mock_android.fake.FakeClass",
+                        "mock_android.fake.InnerTest$MyIntEnum",
                         "mock_android.util.EmptyArray",
-                        "mock_android.fake2.FakeClass",
                         "mock_android.fake.InnerTest$DerivingClass",
-                        "mock_android.fake.InnerTest$NotStaticInner2",
+                        "mock_android.fake2.FakeClass",
                         "mock_android.fake.subpackage.SubpackageClassC$InnerClass",
                         "mock_android.fake.InnerTest$MyGenerics1",
-                        "mock_android.fake.InnerTest$MyIntEnum",
-                        "mock_android.fake.InnerTest$MyStaticInnerClass",
-                        "mock_android.fake.InnerTest",
-                        "mock_android.fake.subpackage.SubpackageClassB",
-                        "mock_android.fake.subpackage.SubpackageClassA",
-                        "mock_android.fake.InnerTest$1",
                         "mock_android.fake.subpackage.SubpackageClassC$StaticInnerClass",
+                        "mock_android.fake.InnerTest$MyStaticInnerClass",
+                        "mock_android.fake.InnerTest$NotStaticInner1",
+                        "mock_android.fake.InnerTest$NotStaticInner2",
+                        "mock_android.fake.subpackage.SubpackageClassA",
+                        "mock_android.fake.InnerTest",
+                        "mock_android.fake.InnerTest$1",
                         "mock_android.fake.subpackage.SubpackageClassC",
+                        "mock_android.fake.subpackage.SubpackageClassB",
                 },
                 result.getFound().keySet().toArray());
     }
diff --git a/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/create/tests/src/com/android/tools/layoutlib/create/AsmGeneratorTest.java
similarity index 99%
rename from create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
rename to create/tests/src/com/android/tools/layoutlib/create/AsmGeneratorTest.java
index e6f5a5e..3f5e8f6 100644
--- a/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 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.
diff --git a/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java b/create/tests/src/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java
similarity index 97%
rename from create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java
rename to create/tests/src/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java
index b8e54f3..8622882 100644
--- a/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 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.
diff --git a/create/tests/com/android/tools/layoutlib/create/CreateInfoAdapter.java b/create/tests/src/com/android/tools/layoutlib/create/CreateInfoAdapter.java
similarity index 95%
rename from create/tests/com/android/tools/layoutlib/create/CreateInfoAdapter.java
rename to create/tests/src/com/android/tools/layoutlib/create/CreateInfoAdapter.java
index 675159a..668b578 100644
--- a/create/tests/com/android/tools/layoutlib/create/CreateInfoAdapter.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/CreateInfoAdapter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 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.
@@ -18,7 +18,6 @@
 
 import java.util.Collections;
 import java.util.Map;
-import java.util.Set;
 
 class CreateInfoAdapter implements ICreateInfo {
     private static final String[] EMPTY_STRING_ARRAY = new String[0];
diff --git a/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java b/create/tests/src/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
similarity index 99%
rename from create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
rename to create/tests/src/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
index 708e7ce..b7aa4e6 100644
--- a/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 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.
diff --git a/create/tests/com/android/tools/layoutlib/create/LogTest.java b/create/tests/src/com/android/tools/layoutlib/create/LogTest.java
similarity index 97%
rename from create/tests/com/android/tools/layoutlib/create/LogTest.java
rename to create/tests/src/com/android/tools/layoutlib/create/LogTest.java
index 1a5f653..e725599 100644
--- a/create/tests/com/android/tools/layoutlib/create/LogTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/LogTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 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.
diff --git a/create/tests/com/android/tools/layoutlib/create/MockLog.java b/create/tests/src/com/android/tools/layoutlib/create/MockLog.java
similarity index 95%
rename from create/tests/com/android/tools/layoutlib/create/MockLog.java
rename to create/tests/src/com/android/tools/layoutlib/create/MockLog.java
index de750a3..0503922 100644
--- a/create/tests/com/android/tools/layoutlib/create/MockLog.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/MockLog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 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.
diff --git a/create/tests/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java b/create/tests/src/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java
similarity index 93%
rename from create/tests/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java
rename to create/tests/src/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java
index eeb0b10..928ac4d 100644
--- a/create/tests/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 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.
@@ -121,11 +121,11 @@
         ClassReader reader = new ClassReader(PrivateClass.class.getName());
         LoggingClassVisitor log = new LoggingClassVisitor();
 
+        String rootClass = PromoteClassClassAdapterTest.class.getName();
         PromoteClassClassAdapter adapter = new PromoteClassClassAdapter(log, new HashSet<String>() {
             {
-                add("com.android.tools.layoutlib.create.PromoteClassClassAdapterTest$PrivateClass");
-                add("com.android.tools.layoutlib.create" +
-                        ".PromoteClassClassAdapterTest$ClassWithPrivateInnerClass$InnerPrivateClass");
+                add(rootClass + "$PrivateClass");
+                add(rootClass + "$ClassWithPrivateInnerClass$InnerPrivateClass");
             }
         });
         reader.accept(adapter, 0);
@@ -157,7 +157,7 @@
 
         PromoteClassClassAdapter adapter = new PromoteClassClassAdapter(log, new HashSet<String>() {
             {
-                add("com.android.tools.layoutlib.create.PackageProtectedClass");
+                add(PackageProtectedClass.class.getName());
             }
         });
 
diff --git a/create/tests/src/com/android/tools/layoutlib/create/RefactorClassAdapterTest.java b/create/tests/src/com/android/tools/layoutlib/create/RefactorClassAdapterTest.java
new file mode 100644
index 0000000..4a4bd74
--- /dev/null
+++ b/create/tests/src/com/android/tools/layoutlib/create/RefactorClassAdapterTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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 com.android.tools.layoutlib.create.dataclass.OuterClass;
+import com.android.tools.layoutlib.create.dataclass.UsageClass;
+
+import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class RefactorClassAdapterTest {
+    private static final String OUTER_CLASS_NAME = OuterClass.class.getName();
+    private static final String USAGE_CLASS_NAME = UsageClass.class.getName();
+
+    @Test
+    public void testRefactorWithLambdas() throws Exception {
+        Map<String, String> refactorMap = new HashMap<>();
+        String originalClassName = OUTER_CLASS_NAME.replace('.', '/');
+        String newClassName = originalClassName + "Refactored";
+        refactorMap.put(originalClassName, newClassName);
+
+        ClassWriter outerCw = new ClassWriter(0 /*flags*/);
+        RefactorClassAdapter cv = new RefactorClassAdapter(outerCw, refactorMap);
+        ClassReader cr = new ClassReader(OUTER_CLASS_NAME);
+        cr.accept(cv, 0 /* flags */);
+
+        ClassWriter usageCw = new ClassWriter(0 /*flags*/);
+        cv = new RefactorClassAdapter(usageCw, refactorMap);
+        cr = new ClassReader(USAGE_CLASS_NAME);
+        cr.accept(cv, 0 /* flags */);
+
+        ClassLoader2 cl2 = new ClassLoader2() {
+            @Override
+            public void testModifiedInstance() throws Exception {
+                Class<?> clazz2 = loadClass(USAGE_CLASS_NAME);
+                Object usage = clazz2.newInstance();
+                assertNotNull(usage);
+                assertEquals(17, callUsage(usage));
+            }
+        };
+        cl2.add(OUTER_CLASS_NAME + "Refactored", outerCw);
+        cl2.add(USAGE_CLASS_NAME, usageCw);
+        cl2.testModifiedInstance();
+    }
+
+    //-------
+
+    /**
+     * A class loader than can define and instantiate our modified classes.
+     * <p/>
+     * Trying to do so in the original class loader generates all sort of link issues because
+     * there are 2 different definitions of the same class name. This class loader will
+     * define and load the class when requested by name and provide helpers to access the
+     * instance methods via reflection.
+     */
+    private abstract static class ClassLoader2 extends ClassLoader {
+
+        private final Map<String, byte[]> mClassDefs = new HashMap<>();
+
+        public ClassLoader2() {
+            super(null);
+        }
+
+        private void add(String className, ClassWriter rewrittenClass) {
+            mClassDefs.put(className, rewrittenClass.toByteArray());
+        }
+
+        private Set<Entry<String, byte[]>> getByteCode() {
+            return mClassDefs.entrySet();
+        }
+
+        @Override
+        protected Class<?> findClass(String name) {
+            try {
+                return super.findClass(name);
+            } catch (ClassNotFoundException e) {
+
+                byte[] def = mClassDefs.get(name);
+                if (def != null) {
+                    // Load the modified class from its bytes representation.
+                    return defineClass(name, def, 0, def.length);
+                }
+
+                try {
+                    // Load everything else from the original definition into the new class loader.
+                    ClassReader cr = new ClassReader(name);
+                    ClassWriter cw = new ClassWriter(0);
+                    cr.accept(cw, 0);
+                    byte[] bytes = cw.toByteArray();
+                    return defineClass(name, bytes, 0, bytes.length);
+
+                } catch (IOException ioe) {
+                    throw new RuntimeException(ioe);
+                }
+            }
+        }
+
+        /**
+         * Accesses {@link UsageClass#doSomething} via reflection.
+         */
+        public int callUsage(Object instance) throws Exception {
+            Method m = instance.getClass().getMethod("doSomething");
+
+            Object result = m.invoke(instance);
+            return (Integer) result;
+        }
+
+        public abstract void testModifiedInstance() throws Exception;
+    }
+}
diff --git a/create/tests/com/android/tools/layoutlib/create/RenameClassAdapterTest.java b/create/tests/src/com/android/tools/layoutlib/create/RenameClassAdapterTest.java
similarity index 98%
rename from create/tests/com/android/tools/layoutlib/create/RenameClassAdapterTest.java
rename to create/tests/src/com/android/tools/layoutlib/create/RenameClassAdapterTest.java
index 6211e73..16ce4a0 100644
--- a/create/tests/com/android/tools/layoutlib/create/RenameClassAdapterTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/RenameClassAdapterTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 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.
diff --git a/create/tests/com/android/tools/layoutlib/create/StubClassAdapterTest.java b/create/tests/src/com/android/tools/layoutlib/create/StubClassAdapterTest.java
similarity index 97%
rename from create/tests/com/android/tools/layoutlib/create/StubClassAdapterTest.java
rename to create/tests/src/com/android/tools/layoutlib/create/StubClassAdapterTest.java
index 5b3ef20..efc31af 100644
--- a/create/tests/com/android/tools/layoutlib/create/StubClassAdapterTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/StubClassAdapterTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 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.
diff --git a/create/tests/com/android/tools/layoutlib/create/StubMethodAdapterTest.java b/create/tests/src/com/android/tools/layoutlib/create/StubMethodAdapterTest.java
similarity index 98%
rename from create/tests/com/android/tools/layoutlib/create/StubMethodAdapterTest.java
rename to create/tests/src/com/android/tools/layoutlib/create/StubMethodAdapterTest.java
index 5ec208c..3782173 100644
--- a/create/tests/com/android/tools/layoutlib/create/StubMethodAdapterTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/StubMethodAdapterTest.java
@@ -1,5 +1,5 @@
 /*
- * 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");
  * you may not use this file except in compliance with the License.
diff --git a/create/tests/com/android/tools/layoutlib/create/TestClassLoader.java b/create/tests/src/com/android/tools/layoutlib/create/TestClassLoader.java
similarity index 96%
rename from create/tests/com/android/tools/layoutlib/create/TestClassLoader.java
rename to create/tests/src/com/android/tools/layoutlib/create/TestClassLoader.java
index a869141..06b1291 100644
--- a/create/tests/com/android/tools/layoutlib/create/TestClassLoader.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/TestClassLoader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 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.
diff --git a/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative.java b/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithNative.java
similarity index 96%
rename from create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative.java
rename to create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithNative.java
index 8fd97b9..f7f9535 100644
--- a/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithNative.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 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.
diff --git a/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative_Delegate.java b/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithNative_Delegate.java
similarity index 95%
rename from create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative_Delegate.java
rename to create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithNative_Delegate.java
index 02d5a27..1945744 100644
--- a/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative_Delegate.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithNative_Delegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 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.
diff --git a/create/tests/com/android/tools/layoutlib/create/dataclass/JavaClass.java b/create/tests/src/com/android/tools/layoutlib/create/dataclass/JavaClass.java
similarity index 75%
rename from create/tests/com/android/tools/layoutlib/create/dataclass/JavaClass.java
rename to create/tests/src/com/android/tools/layoutlib/create/dataclass/JavaClass.java
index 9b5a918..3309863 100644
--- a/create/tests/com/android/tools/layoutlib/create/dataclass/JavaClass.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/dataclass/JavaClass.java
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
- * Licensed under the Eclipse Public License, Version 1.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
  *
- *      http://www.eclipse.org/org/documents/epl-v10.php
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java b/create/tests/src/com/android/tools/layoutlib/create/dataclass/OuterClass.java
similarity index 85%
rename from create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
rename to create/tests/src/com/android/tools/layoutlib/create/dataclass/OuterClass.java
index a5fc654..914a2e7 100644
--- a/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/dataclass/OuterClass.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 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.
@@ -18,12 +18,15 @@
 
 import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
 
+import java.util.List;
+
 /**
  * Test class with an inner class.
  *
  * Used by {@link DelegateClassAdapterTest}.
  */
 public class OuterClass {
+    public static final int OUTER_CONSTANT = 2;
     private int mOuterValue = 1;
     public OuterClass() {
     }
@@ -35,6 +38,10 @@
         return mOuterValue + a + (int) b;
     }
 
+    public static int useLambdas(List<Integer> list) {
+        return list.stream().mapToInt(i -> 2 * i).sum();
+    }
+
     public class InnerClass {
         public int mInnerId;
 
@@ -49,6 +56,7 @@
     }
 
     public static class StaticInnerClass {
+        public static final int INNER_CONSTANT = 3;
         public int mStaticInnerId;
 
         public StaticInnerClass() {
diff --git a/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java b/create/tests/src/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java
similarity index 94%
rename from create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java
rename to create/tests/src/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java
index 774be8e..0dbd1ff 100644
--- a/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 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.
diff --git a/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_InnerClass_Delegate.java b/create/tests/src/com/android/tools/layoutlib/create/dataclass/OuterClass_InnerClass_Delegate.java
similarity index 95%
rename from create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_InnerClass_Delegate.java
rename to create/tests/src/com/android/tools/layoutlib/create/dataclass/OuterClass_InnerClass_Delegate.java
index e2da81d..5e15c68 100644
--- a/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_InnerClass_Delegate.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/dataclass/OuterClass_InnerClass_Delegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 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.
diff --git a/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java b/create/tests/src/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java
similarity index 95%
rename from create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java
rename to create/tests/src/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java
index 91a90dc..2218882 100644
--- a/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 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.
@@ -16,8 +16,8 @@
 
 package com.android.tools.layoutlib.create.dataclass;
 
-import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
 import com.android.tools.layoutlib.create.dataclass.OuterClass.StaticInnerClass;
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
 
 /**
  * Used by {@link DelegateClassAdapterTest}.
diff --git a/create/tests/com/android/tools/layoutlib/create/dataclass/StubClass.java b/create/tests/src/com/android/tools/layoutlib/create/dataclass/StubClass.java
similarity index 93%
rename from create/tests/com/android/tools/layoutlib/create/dataclass/StubClass.java
rename to create/tests/src/com/android/tools/layoutlib/create/dataclass/StubClass.java
index 3ae8e47..0dd4ee8 100644
--- a/create/tests/com/android/tools/layoutlib/create/dataclass/StubClass.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/dataclass/StubClass.java
@@ -1,5 +1,5 @@
 /*
- * 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");
  * you may not use this file except in compliance with the License.
diff --git a/create/tests/src/com/android/tools/layoutlib/create/dataclass/UsageClass.java b/create/tests/src/com/android/tools/layoutlib/create/dataclass/UsageClass.java
new file mode 100644
index 0000000..a4e08b9
--- /dev/null
+++ b/create/tests/src/com/android/tools/layoutlib/create/dataclass/UsageClass.java
@@ -0,0 +1,32 @@
+/*
+ * 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.dataclass.OuterClass.StaticInnerClass;
+
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+public class UsageClass {
+    public UsageClass() {}
+
+    public int doSomething() {
+        List<Integer> list = Lists.newArrayList(1, 2, 3);
+         return OuterClass.OUTER_CONSTANT + StaticInnerClass.INNER_CONSTANT + OuterClass.useLambdas(list);
+    }
+}
diff --git a/legacy/legacy.iml b/legacy/legacy.iml
deleted file mode 100644
index a167a75..0000000
--- a/legacy/legacy.iml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?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" />
-    </content>
-    <orderEntry type="inheritedJdk" />
-    <orderEntry type="sourceFolder" forTests="false" />
-    <orderEntry type="library" name="layoutlib_api-prebuilt" level="project" />
-  </component>
-</module>
\ No newline at end of file
diff --git a/legacy/src/com/android/layoutlib/bridge/Bridge.java b/legacy/src/com/android/layoutlib/bridge/Bridge.java
deleted file mode 100644
index 0cfc181..0000000
--- a/legacy/src/com/android/layoutlib/bridge/Bridge.java
+++ /dev/null
@@ -1,75 +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.layoutlib.bridge;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.ide.common.rendering.api.SessionParams;
-
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
-
-/**
- * Legacy Bridge used in the SDK version of layoutlib
- */
-public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
-    private static final String SDK_NOT_SUPPORTED = "The SDK layoutlib version is not supported";
-    private static final Result NOT_SUPPORTED_RESULT =
-            Status.NOT_IMPLEMENTED.createResult(SDK_NOT_SUPPORTED);
-    private static BufferedImage sImage;
-
-    private static class BridgeRenderSession extends RenderSession {
-
-        @Override
-        public synchronized BufferedImage getImage() {
-            if (sImage == null) {
-                sImage = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
-                Graphics2D g = sImage.createGraphics();
-                g.clearRect(0, 0, 500, 500);
-                g.drawString(SDK_NOT_SUPPORTED, 20, 20);
-                g.dispose();
-            }
-
-            return sImage;
-        }
-
-        @Override
-        public Result render(long timeout, boolean forceMeasure) {
-            return NOT_SUPPORTED_RESULT;
-        }
-
-        @Override
-        public Result measure(long timeout) {
-            return NOT_SUPPORTED_RESULT;
-        }
-
-        @Override
-        public Result getResult() {
-            return NOT_SUPPORTED_RESULT;
-        }
-    }
-
-
-    @Override
-    public RenderSession createSession(SessionParams params) {
-        return new BridgeRenderSession();
-    }
-
-    @Override
-    public int getApiLevel() {
-        return 0;
-    }
-}
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/RemoteBridgeClient.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/RemoteBridgeClient.java
index 8cd61f9..051fb85 100644
--- a/remote/client/src/com/android/layoutlib/bridge/remote/client/RemoteBridgeClient.java
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/RemoteBridgeClient.java
@@ -55,42 +55,15 @@
     }
 
     @Override
-    public int getApiLevel() {
-        try {
-            return mDelegate.getApiLevel();
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-
-        }
-    }
-
-    @Override
-    public int getRevision() {
-        try {
-            return mDelegate.getRevision();
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    public boolean supports(int feature) {
-        try {
-            return mDelegate.supports(feature);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
     public boolean init(Map<String, String> platformProperties,
             File fontLocation,
+            String nativeLibPath,
             String icuDataPath,
             Map<String, Map<String, Integer>> enumValueMap,
             LayoutLog log) {
         try {
-            return mDelegate.init(platformProperties, fontLocation, icuDataPath, enumValueMap,
-                    RemoteLayoutLogAdapter.create(log));
+            return mDelegate.init(platformProperties, fontLocation, nativeLibPath, icuDataPath,
+                    enumValueMap, RemoteLayoutLogAdapter.create(log));
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
@@ -126,7 +99,7 @@
     }
 
     @Override
-    public void clearCaches(Object projectKey) {
+    public void clearResourceCaches(Object projectKey) {
         throw new UnsupportedOperationException();
     }
 
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutLogAdapter.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutLogAdapter.java
index c5dbfab..23d36bc 100644
--- a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutLogAdapter.java
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutLogAdapter.java
@@ -37,8 +37,8 @@
     }
 
     @Override
-    public void warning(String tag, String message, Serializable data) {
-        mLog.warning(tag, message, null);
+    public void warning(String tag, String message, Object viewCookie, Serializable data) {
+        mLog.warning(tag, message, viewCookie, null);
     }
 
     @Override
@@ -48,12 +48,12 @@
     }
 
     @Override
-    public void error(String tag, String message, Serializable data) {
-        mLog.error(tag, message, null);
+    public void error(String tag, String message, Object viewCookie, Serializable data) {
+        mLog.error(tag, message, viewCookie, null);
     }
 
     @Override
-    public void error(String tag, String message, Throwable throwable, Serializable data) {
-        mLog.error(tag, message, throwable, null);
+    public void error(String tag, String message, Throwable throwable, Object viewCookie, Serializable data) {
+        mLog.error(tag, message, throwable, viewCookie, null);
     }
 }
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 b4236e9..01efd12 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
@@ -51,21 +51,11 @@
     }
 
     @Override
-    public boolean supports(int ideFeature) {
-        return mDelegate.supports(ideFeature);
-    }
-
-    @Override
     public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs) {
         throw new UnsupportedOperationException("Not implemented yet");
     }
 
     @Override
-    public String getNamespace() {
-        return mDelegate.getNamespace();
-    }
-
-    @Override
     public ResourceReference resolveResourceId(int id) {
         return mDelegate.resolveResourceId(id);
     }
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteRenderParamsAdapter.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteRenderParamsAdapter.java
index b9f4e9e..8cebdc0 100644
--- a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteRenderParamsAdapter.java
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteRenderParamsAdapter.java
@@ -106,13 +106,8 @@
     }
 
     @Override
-    public boolean isBgColorOverridden() {
-        return mDelegate.isBgColorOverridden();
-    }
-
-    @Override
-    public int getOverrideBgColor() {
-        return mDelegate.getOverrideBgColor();
+    public boolean isTransparentBackground() {
+        return mDelegate.isTransparentBackground();
     }
 
     @Override
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteBridge.java b/remote/common/src/com/android/layout/remote/api/RemoteBridge.java
index 74a02b3..3634fff 100644
--- a/remote/common/src/com/android/layout/remote/api/RemoteBridge.java
+++ b/remote/common/src/com/android/layout/remote/api/RemoteBridge.java
@@ -35,33 +35,11 @@
  */
 public interface RemoteBridge extends Remote {
     /**
-     * Returns the API level of the layout library.
-     * <p>
-     * While no methods will ever be removed, some may become deprecated, and some new ones will
-     * appear. <p>All Layout libraries based on {@link Bridge} return at minimum an API level of 5.
-     */
-    int getApiLevel() throws RemoteException;
-
-    /**
-     * Returns the revision of the library inside a given (layoutlib) API level. The true revision
-     * number of the library is {@link #getApiLevel()}.{@link #getRevision()}
-     */
-    @SuppressWarnings("JavaDoc")
-    // javadoc pointing to itself.
-    int getRevision() throws RemoteException;
-
-    /**
-     * Returns true if the layout library supports the given feature.
-     *
-     * @see com.android.ide.common.rendering.api.Features
-     */
-    boolean supports(int feature) throws RemoteException;
-
-    /**
      * Initializes the Bridge object.
      *
      * @param platformProperties The build properties for the platform.
      * @param fontLocation the location of the fonts.
+     * @param nativeLibPath the absolute path of the JNI library for layoutlib.
      * @param icuDataPath the location of the ICU data used natively.
      * @param enumValueMap map attrName ⇒ { map enumFlagName ⇒ Integer value }. This is typically
      * read from attrs.xml in the SDK target.
@@ -70,7 +48,8 @@
      * @return true if success.
      */
     boolean init(@NotNull Map<String, String> platformProperties, File fontLocation,
-            String icuDataPath, @NotNull Map<String, Map<String, Integer>> enumValueMap,
+            @Nullable String nativeLibPath, @Nullable String icuDataPath,
+            @NotNull Map<String, Map<String, Integer>> enumValueMap,
             @Nullable RemoteLayoutLog log) throws RemoteException;
 
     /**
@@ -101,16 +80,18 @@
 
     /**
      * Clears the resource cache for a specific project.
+     *
      * <p>This cache contains bitmaps and nine patches that are loaded from the disk and reused
      * until this method is called.
-     * <p>The cache is not configuration dependent and should only be cleared when a
-     * resource changes (at this time only bitmaps and 9 patches go into the cache).
-     * <p>
-     * The project key provided must be similar to the one passed in {@link RenderParams}.
+     *
+     * <p>The cache is not configuration dependent and should only be cleared when a resource
+     * changes (at this time only bitmaps and 9 patches go into the cache).
+     *
+     * <p>The project key provided must be similar to the one passed in {@link RenderParams}.
      *
      * @param projectKey the key for the project.
      */
-    void clearCaches(String projectKey) throws RemoteException;
+    void clearResourceCaches(String projectKey) throws RemoteException;
 
     /**
      * Returns true if the character orientation of the locale is right to left.
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 c3b5c61..01add53 100644
--- a/remote/common/src/com/android/layout/remote/api/RemoteLayoutLog.java
+++ b/remote/common/src/com/android/layout/remote/api/RemoteLayoutLog.java
@@ -31,9 +31,10 @@
      *
      * @param tag a tag describing the type of the warning
      * @param message the message of the warning
+     * @param viewCookie optional cookie of the view associated to this error
      * @param data an optional data bundle that the client can use to improve the warning display.
      */
-    void warning(String tag, String message, Serializable data) throws RemoteException;
+    void warning(String tag, String message, Object viewCookie, Serializable data) throws RemoteException;
 
     /**
      * Logs a fidelity warning.
@@ -55,9 +56,10 @@
      *
      * @param tag a tag describing the type of the error
      * @param message the message of the error
+     * @param viewCookie optional cookie of the view associated to this error
      * @param data an optional data bundle that the client can use to improve the error display.
      */
-    void error(String tag, String message, Serializable data) throws RemoteException;
+    void error(String tag, String message, Object viewCookie, Serializable data) throws RemoteException;
 
     /**
      * Logs an error, and the {@link Throwable} that triggered it.
@@ -65,8 +67,9 @@
      * @param tag a tag describing the type of the error
      * @param message the message of the error
      * @param throwable the Throwable that triggered the error
+     * @param viewCookie optional cookie of the view associated to this error
      * @param data an optional data bundle that the client can use to improve the error display.
      */
-    void error(String tag, String message, Throwable throwable, Serializable data)
+    void error(String tag, String message, Throwable throwable, Object viewCookie, Serializable data)
             throws RemoteException;
 }
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 94e5185..d68934a 100644
--- a/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java
+++ b/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java
@@ -23,8 +23,6 @@
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.SessionParams.Key;
 
-import org.xmlpull.v1.XmlPullParser;
-
 import java.nio.file.Path;
 import java.rmi.Remote;
 import java.rmi.RemoteException;
@@ -33,13 +31,9 @@
  * Remote version of the {@link LayoutlibCallback} class
  */
 public interface RemoteLayoutlibCallback extends Remote {
-    boolean supports(int ideFeature) throws RemoteException;
-
     Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
             throws Exception;
 
-    String getNamespace() throws RemoteException;
-
     ResourceReference resolveResourceId(int id) throws RemoteException;
 
     int getOrGenerateResourceId(ResourceReference resource) throws RemoteException;
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteRenderParams.java b/remote/common/src/com/android/layout/remote/api/RemoteRenderParams.java
index 5dff1fb..d245f2b 100644
--- a/remote/common/src/com/android/layout/remote/api/RemoteRenderParams.java
+++ b/remote/common/src/com/android/layout/remote/api/RemoteRenderParams.java
@@ -42,9 +42,7 @@
 
     RemoteLayoutLog getLog() throws RemoteException;
 
-    boolean isBgColorOverridden() throws RemoteException;
-
-    int getOverrideBgColor() throws RemoteException;
+    boolean isTransparentBackground() throws RemoteException;
 
     long getTimeout() throws RemoteException;
 
diff --git a/remote/server/src/com/android/layoutlib/bridge/remote/server/RemoteBridgeImpl.java b/remote/server/src/com/android/layoutlib/bridge/remote/server/RemoteBridgeImpl.java
index 0013218..ef242c1 100644
--- a/remote/server/src/com/android/layoutlib/bridge/remote/server/RemoteBridgeImpl.java
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/RemoteBridgeImpl.java
@@ -59,25 +59,11 @@
     private Map<String, String> mCachedProjectKeys = new HashMap<>();
 
     @Override
-    public int getApiLevel() {
-        return mBridge.getApiLevel();
-    }
-
-    @Override
-    public int getRevision() {
-        return mBridge.getRevision();
-    }
-
-    @Override
-    public boolean supports(int feature) {
-        return mBridge.supports(feature);
-    }
-
-    @Override
-    public boolean init(Map<String, String> platformProperties, File fontLocation, String icuDataPath,
+    public boolean init(Map<String, String> platformProperties, File fontLocation,
+            String nativeLibPath, String icuDataPath,
             Map<String, Map<String, Integer>> enumValueMap, RemoteLayoutLog log) {
-        return mBridge.init(platformProperties, fontLocation, icuDataPath, enumValueMap,
-                log != null ? new RemoteLayoutLogAdapter(log) : null);
+        return mBridge.init(platformProperties, fontLocation, nativeLibPath, icuDataPath,
+                enumValueMap, log != null ? new RemoteLayoutLogAdapter(log) : null);
     }
 
     @Override
@@ -97,8 +83,8 @@
             params.setForceNoDecor();
         }
         params.setRtlSupport(remoteParams.isRtlSupported());
-        if (remoteParams.isBgColorOverridden()) {
-            params.setOverrideBgColor(remoteParams.getOverrideBgColor());
+        if (remoteParams.isTransparentBackground()) {
+            params.setTransparentBackground();
         }
         params.setImageFactory(remoteParams.getImageFactory());
         // TODO: Also unpack remote flags and pass them to RenderParams
@@ -151,9 +137,9 @@
     }
 
     @Override
-    public void clearCaches(String projectKey) {
+    public void clearResourceCaches(String projectKey) {
         mCachedProjectKeys.remove(projectKey);
-        mBridge.clearCaches(projectKey);
+        mBridge.clearResourceCaches(projectKey);
     }
 
     @Override
diff --git a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutLogAdapter.java b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutLogAdapter.java
index 6878d46..90da083 100644
--- a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutLogAdapter.java
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutLogAdapter.java
@@ -30,9 +30,9 @@
     }
 
     @Override
-    public void warning(String tag, String message, Object data) {
+    public void warning(String tag, String message, Object viewCookie, Object data) {
         try {
-            mLog.warning(tag, message, null);
+            mLog.warning(tag, message, viewCookie, null);
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
@@ -49,18 +49,19 @@
     }
 
     @Override
-    public void error(String tag, String message, Object data) {
+    public void error(String tag, String message, Object viewCookie, Object data) {
         try {
-            mLog.error(tag, message, null);
+            mLog.error(tag, message, viewCookie, null);
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
     }
 
     @Override
-    public void error(String tag, String message, Throwable throwable, Object data) {
+    public void error(String tag, String message, Throwable throwable, Object viewCookie,
+            Object data) {
         try {
-            mLog.error(tag, message, throwable, null);
+            mLog.error(tag, message, throwable, viewCookie, null);
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
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 a6885ef..719de4d 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
@@ -157,15 +157,6 @@
     }
 
     @Override
-    public String getNamespace() {
-        try {
-            return mDelegate.getNamespace();
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
     public ResourceReference resolveResourceId(int id) {
         try {
             return mDelegate.resolveResourceId(id);
@@ -222,15 +213,6 @@
     }
 
     @Override
-    public boolean supports(int ideFeature) {
-        try {
-            return mDelegate.supports(ideFeature);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
     public <T> T getFlag(Key<T> key) {
         return super.getFlag(key);
     }
diff --git a/remote/tests/src/RemoteBridgeTest.java b/remote/tests/src/RemoteBridgeTest.java
index 6ad10cb..5dd3b4f 100644
--- a/remote/tests/src/RemoteBridgeTest.java
+++ b/remote/tests/src/RemoteBridgeTest.java
@@ -75,7 +75,7 @@
         File buildProp = new File(PLATFORM_DIR, "build.prop");
         File attrs = new File(res, "values" + File.separator + "attrs.xml");
 
-        mClient.init(ConfigGenerator.loadProperties(buildProp), fontLocation, null,
+        mClient.init(ConfigGenerator.loadProperties(buildProp), fontLocation, null, null,
                 ConfigGenerator.getEnumMap(attrs), getLayoutLog());
         System.out.printf("Remote client init took %dms\n",
                 System.currentTimeMillis() - startTime);
diff --git a/studio-custom-widgets/src/com/android/tools/idea/editors/theme/widgets/ErrorCatcher.java b/studio-custom-widgets/src/com/android/tools/idea/editors/theme/widgets/ErrorCatcher.java
index ecf39b3..519b127 100644
--- a/studio-custom-widgets/src/com/android/tools/idea/editors/theme/widgets/ErrorCatcher.java
+++ b/studio-custom-widgets/src/com/android/tools/idea/editors/theme/widgets/ErrorCatcher.java
@@ -61,7 +61,7 @@
                     resolveSize(child.getMeasuredHeight(), heightMeasureSpec));
         } catch (Throwable t) {
             Bridge.getLog().warning(LayoutLog.TAG_BROKEN, "Failed to do onMeasure for view " +
-                    child.getClass().getCanonicalName(), t);
+                    child.getClass().getCanonicalName(), null, t);
             setMeasuredDimension(resolveSize(0, widthMeasureSpec),
                     resolveSize(0, heightMeasureSpec));
         }
@@ -73,7 +73,7 @@
             return super.drawChild(canvas, child, drawingTime);
         } catch (Throwable t) {
             Bridge.getLog().warning(LayoutLog.TAG_BROKEN, "Failed to draw for view " +
-                    child.getClass().getCanonicalName(), t);
+                    child.getClass().getCanonicalName(), null, t);
         }
 
         return false;
@@ -88,7 +88,7 @@
             child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
         } catch (Throwable e) {
             Bridge.getLog().warning(LayoutLog.TAG_BROKEN, "Failed to do onLayout for view " +
-                    child.getClass().getCanonicalName(), e);
+                    child.getClass().getCanonicalName(), null, e);
         }
     }
 }
diff --git a/legacy/Android.bp b/validator/Android.bp
similarity index 64%
rename from legacy/Android.bp
rename to validator/Android.bp
index d9a19de..0eff99b 100644
--- a/legacy/Android.bp
+++ b/validator/Android.bp
@@ -1,5 +1,5 @@
 //
-// Copyright (C) 2008 The Android Open Source Project
+// 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.
@@ -15,9 +15,21 @@
 //
 
 java_library_host {
-    name: "layoutlib-legacy",
+    name: "layoutlib-validator",
 
     srcs: ["src/**/*.java"],
+    java_resource_dirs: ["resources"],
 
-    libs: ["layoutlib_api-prebuilt"],
+    libs: [
+        "tools-common-prebuilt",
+        "temp_layoutlib",
+        "layoutlib-common",
+        "guava",
+    ],
+
+    static_libs: [
+        "hamcrest",
+        "jsoup-1.6.3",
+        "protobuf-lite",
+    ],
 }
diff --git a/validator/resources/strings.properties b/validator/resources/strings.properties
new file mode 100644
index 0000000..af7d020
--- /dev/null
+++ b/validator/resources/strings.properties
@@ -0,0 +1,131 @@
+
+#
+# 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.
+#
+actionable = actionable
+button_item_type = button
+check_title_accessibility_traversal = Traversal order
+check_title_class_name_not_supported = Unsupported item type
+check_title_clickablespan = Link
+check_title_duplicate_clickable_bounds = Clickable items
+check_title_duplicate_speakable_text = Item descriptions
+check_title_editable_content_desc = Editable item label
+check_title_image_contrast = Image contrast
+check_title_item_exposed = Exposed items
+check_title_link_test = Link text
+check_title_reading_score = Readability
+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
+clickable = clickable
+clickable_and_long_clickable = clickable and long clickable
+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_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_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.
+underline_text = underline
diff --git a/validator/resources/values.xml b/validator/resources/values.xml
new file mode 100644
index 0000000..ae49a3c
--- /dev/null
+++ b/validator/resources/values.xml
@@ -0,0 +1,133 @@
+<?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="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="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) 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) 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." 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="Describes underlined text styling. [CHAR LIMIT=NONE]" name="underline_text">underline</string>
+</resources>
\ No newline at end of file
diff --git a/validator/src/ResourceConverter.java b/validator/src/ResourceConverter.java
new file mode 100644
index 0000000..18ffda8
--- /dev/null
+++ b/validator/src/ResourceConverter.java
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+import com.android.tools.layoutlib.annotations.Nullable;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+
+public class ResourceConverter {
+
+    /**
+     * Convert the Android strings.xml into generic Java strings.properties.
+     */
+    public static void main(String[] args) throws Exception {
+        System.out.println("Parsing input...");
+        Map<String, String> map = loadStrings("./validator/resources/values.xml");
+        System.out.println("Writing to output...");
+        writeStrings(map, "./validator/resources/strings.properties");
+        System.out.println("Finished converting.");
+    }
+
+    /**
+     * Writes <name, value> pair to outputPath.
+     */
+    private static void writeStrings(Map<String, String> map, String outputPath) throws Exception {
+        File output = new File(outputPath);
+        output.createNewFile();
+        FileWriter writer = new FileWriter(output);
+        try {
+            writer.write(getCopyRight());
+            writer.write("\n");
+            for (Entry<String, String> entry : map.entrySet()) {
+                String name = entry.getKey();
+                String value = entry.getValue();
+                writer.write(name + " = " + value + "\n");
+            }
+        } finally {
+            writer.close();
+        }
+    }
+
+    /**
+     * Very hacky parser for Android-understood-values.xml. It parses <string> </string>
+     * tags, and retrieve the name and the value associated.
+     *
+     * @param path to .xml containing android strings
+     * @return Map containing name and values.
+     */
+    @Nullable
+    private static Map<String, String> loadStrings(String path)
+            throws Exception {
+        // Use ordered map to minimize changes to strings.properties in git.
+        Map<String, String> toReturn = new LinkedHashMap<>();
+
+        File file = new File(path);
+        if (!file.exists()) {
+            System.err.println("The input file "+ path + " does not exist. Terminating.");
+            return toReturn;
+        }
+
+        DocumentBuilder documentBuilder = DocumentBuilderFactory
+                .newInstance().newDocumentBuilder();
+        Document document = documentBuilder.parse(file);
+        NodeList nodeList = document.getElementsByTagName("string");
+        for (int i = 0; i < nodeList.getLength(); i++) {
+            Node node = nodeList.item(i);
+            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);
+                if ("ns1:g".equals(child.getNodeName())) {
+                    valueBuilder.append(child.getFirstChild().getNodeValue());
+                } else {
+                    valueBuilder.append(child.getNodeValue());
+                }
+            }
+            toReturn.put(name, valueBuilder.toString());
+        }
+        return toReturn;
+    }
+
+    private static String getCopyRight() {
+        return "\n" + "#\n" + "# Copyright (C) 2020 The Android Open Source Project\n" + "#\n" +
+                "# Licensed under the Apache License, Version 2.0 (the \"License\");\n" +
+                "# you may not use this file except in compliance with the License.\n" +
+                "# You may obtain a copy of the License at\n" + "#\n" +
+                "#      http://www.apache.org/licenses/LICENSE-2.0\n" + "#\n" +
+                "# Unless required by applicable law or agreed to in writing, software\n" +
+                "# distributed under the License is distributed on an \"AS IS\" BASIS,\n" +
+                "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
+                "# See the License for the specific language governing permissions and\n" +
+                "# limitations under the License.\n" + "#";
+    }
+}
diff --git a/validator/src/com/android/tools/idea/validator/LayoutValidator.java b/validator/src/com/android/tools/idea/validator/LayoutValidator.java
new file mode 100644
index 0000000..dc34e90
--- /dev/null
+++ b/validator/src/com/android/tools/idea/validator/LayoutValidator.java
@@ -0,0 +1,64 @@
+/*
+ * 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.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 android.view.View;
+
+import java.awt.image.BufferedImage;
+import java.util.EnumSet;
+
+/**
+ * Main class for validating layout.
+ */
+public class LayoutValidator {
+
+    public static final ValidatorData.Policy DEFAULT_POLICY = new Policy(
+            EnumSet.of(Type.ACCESSIBILITY, Type.RENDER),
+            EnumSet.of(Level.ERROR, Level.WARNING));
+
+    private static ValidatorData.Policy sPolicy = DEFAULT_POLICY;
+
+    /**
+     * Validate the layout using the default policy.
+     * Precondition: View must be attached to the window.
+     *
+     * @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);
+        }
+        // TODO: Add non-a11y layout validation later.
+        return new ValidatorResult.Builder().build();
+    }
+
+    /**
+     * Update the policy with which to run the validation call.
+     * @param policy new policy.
+     */
+    public static void updatePolicy(@NotNull ValidatorData.Policy policy) {
+        sPolicy = policy;
+    }
+}
diff --git a/validator/src/com/android/tools/idea/validator/ValidatorData.java b/validator/src/com/android/tools/idea/validator/ValidatorData.java
new file mode 100644
index 0000000..6d9d6b6
--- /dev/null
+++ b/validator/src/com/android/tools/idea/validator/ValidatorData.java
@@ -0,0 +1,166 @@
+/*
+ * 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.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
+
+import java.util.EnumSet;
+import java.util.HashSet;
+
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck;
+
+/**
+ * Data used for layout validation.
+ */
+public class ValidatorData {
+
+    /**
+     * Category of validation
+     */
+    public enum Type {
+        ACCESSIBILITY,
+        RENDER,
+        INTERNAL_ERROR
+    }
+
+    /**
+     * Level of importance
+     */
+    public enum Level {
+        ERROR,
+        WARNING,
+        INFO,
+        /** The test not ran or suppressed. */
+        VERBOSE,
+    }
+
+    /**
+     * Determine what types and levels of validation to run.
+     */
+    public static class Policy {
+        @NotNull public final EnumSet<Type> mTypes;
+        @NotNull public final EnumSet<Level> mLevels;
+        @NotNull public final HashSet<AccessibilityHierarchyCheck> mChecks = new HashSet();
+
+        public Policy(@NotNull EnumSet<Type> types, @NotNull EnumSet<Level> levels) {
+            mTypes = types;
+            mLevels = levels;
+        }
+    }
+
+    /**
+     * 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 {
+        @NotNull
+        public final Type mType;
+        @NotNull
+        public final String mMsg;
+        @NotNull
+        public final Level mLevel;
+        @Nullable
+        public final Long mSrcId;
+        @Nullable
+        public final Fix mFix;
+        @NotNull
+        public final String mSourceClass;
+        @Nullable
+        public final String mHelpfulUrl;
+
+        private Issue(
+                @NotNull Type type,
+                @NotNull String msg,
+                @NotNull Level level,
+                @Nullable Long srcId,
+                @Nullable Fix fix,
+                @NotNull String sourceClass,
+                @Nullable String helpfulUrl) {
+            mType = type;
+            mMsg = msg;
+            mLevel = level;
+            mSrcId = srcId;
+            mFix = fix;
+            mSourceClass = sourceClass;
+            mHelpfulUrl = helpfulUrl;
+        }
+
+        public static class IssueBuilder {
+            private Type mType = Type.ACCESSIBILITY;
+            private String mMsg;
+            private Level mLevel;
+            private Long mSrcId;
+            private Fix mFix;
+            private String mSourceClass;
+            private String mHelpfulUrl;
+
+            public IssueBuilder setType(Type type) {
+                mType = type;
+                return this;
+            }
+
+            public IssueBuilder setMsg(String msg) {
+                mMsg = msg;
+                return this;
+            }
+
+            public IssueBuilder setLevel(Level level) {
+                mLevel = level;
+                return this;
+            }
+
+            public IssueBuilder setSrcId(Long srcId) {
+                mSrcId = srcId;
+                return this;
+            }
+
+            public IssueBuilder setFix(Fix fix) {
+                mFix = fix;
+                return this;
+            }
+
+            public IssueBuilder setSourceClass(String sourceClass) {
+                mSourceClass = sourceClass;
+                return this;
+            }
+
+            public IssueBuilder setHelpfulUrl(String url) {
+                mHelpfulUrl = url;
+                return this;
+            }
+
+            public Issue build() {
+                assert(mType != null);
+                assert(mMsg != null);
+                assert(mLevel != null);
+                assert(mSourceClass != null);
+                return new Issue(mType, mMsg, mLevel, mSrcId, mFix, mSourceClass, mHelpfulUrl);
+            }
+        }
+    }
+}
diff --git a/validator/src/com/android/tools/idea/validator/ValidatorResult.java b/validator/src/com/android/tools/idea/validator/ValidatorResult.java
new file mode 100644
index 0000000..8cc5c3d
--- /dev/null
+++ b/validator/src/com/android/tools/idea/validator/ValidatorResult.java
@@ -0,0 +1,144 @@
+/*
+ * 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;
+import com.android.tools.idea.validator.ValidatorData.Level;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableBiMap;
+
+/**
+ * Results of layout validation.
+ */
+public class ValidatorResult {
+
+    @NotNull private final ImmutableBiMap<Long, View> mSrcMap;
+    @NotNull private final ArrayList<Issue> mIssues;
+    @NotNull private final Metric mMetric;
+
+    /**
+     * Please use {@link Builder} for creating results.
+     */
+    private ValidatorResult(BiMap<Long, View> srcMap, ArrayList<Issue> issues, Metric metric) {
+        mSrcMap = ImmutableBiMap.<Long, View>builder().putAll(srcMap).build();
+        mIssues = issues;
+        mMetric = metric;
+    }
+
+    /**
+     * @return the source map of all the Views.
+     */
+    public ImmutableBiMap<Long, View> getSrcMap() {
+        return mSrcMap;
+    }
+
+    /**
+     * @return list of issues.
+     */
+    public List<Issue> getIssues() {
+        return mIssues;
+    }
+
+    /**
+     * @return metric for validation.
+     */
+    public Metric getMetric() {
+        return mMetric;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder()
+                .append("Result containing ")
+                .append(mIssues.size())
+                .append(" issues:\n");
+
+        for (Issue issue : mIssues) {
+            if (issue.mLevel == Level.ERROR) {
+                builder.append(" - [")
+                        .append(issue.mLevel.name())
+                        .append("] ")
+                        .append(issue.mMsg)
+                        .append("\n");
+            }
+        }
+        return builder.toString();
+    }
+
+    public static class Builder {
+        @NotNull public final BiMap<Long, View> mSrcMap = HashBiMap.create();
+        @NotNull public final ArrayList<Issue> mIssues = new ArrayList<>();
+        @NotNull public final Metric mMetric = new Metric();
+
+        public ValidatorResult build() {
+            return new ValidatorResult(mSrcMap, mIssues, mMetric);
+        }
+    }
+
+    /**
+     * Contains metric specific data.
+     */
+    public static class Metric {
+        /** Error message. If null no error was thrown. */
+        public String mErrorMessage = null;
+
+        /** Records how long validation took */
+        public long mElapsedMs = 0;
+
+        /** How many new memories (bytes) validator creates for images. */
+        public long mImageMemoryBytes = 0;
+
+        private long mStart;
+
+        private Metric() { }
+
+        public void startTimer() {
+            mStart = System.currentTimeMillis();
+        }
+
+        public void endTimer() {
+            mElapsedMs = System.currentTimeMillis() - mStart;
+        }
+
+        @Override
+        public String toString() {
+            return "Validation result metric: { elapsed=" + mElapsedMs +
+                    "ms, image memory=" + readableBytes() + " }";
+        }
+
+        private String readableBytes() {
+            if (mImageMemoryBytes > 1000000000) {
+                return mImageMemoryBytes / 1000000000 + "gb";
+            }
+            else if (mImageMemoryBytes > 1000000) {
+                return mImageMemoryBytes / 1000000 + "mb";
+            }
+            else if (mImageMemoryBytes > 1000) {
+                return mImageMemoryBytes / 1000 + "kb";
+            }
+            return mImageMemoryBytes + "bytes";
+        }
+    }
+}
diff --git a/validator/src/com/android/tools/idea/validator/accessibility/AccessibilityValidator.java b/validator/src/com/android/tools/idea/validator/accessibility/AccessibilityValidator.java
new file mode 100644
index 0000000..a2ec2c4
--- /dev/null
+++ b/validator/src/com/android/tools/idea/validator/accessibility/AccessibilityValidator.java
@@ -0,0 +1,186 @@
+/*
+ * 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) {
+            ValidatorData.Level level = convertLevel(result.getType());
+            if (!filter.contains(level)) {
+                continue;
+            }
+
+            try {
+                IssueBuilder issueBuilder = new IssueBuilder()
+                        .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()
+                        .setType(Type.INTERNAL_ERROR)
+                        .setMsg(e.getMessage())
+                        .setLevel(Level.ERROR)
+                        .setSourceClass("AccessibilityValidator").build());
+            }
+        }
+        builder.mMetric.endTimer();
+        return builder.build();
+    }
+
+    @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
new file mode 100644
index 0000000..59d20a8
--- /dev/null
+++ b/validator/src/com/android/tools/idea/validator/accessibility/AtfBufferedImage.java
@@ -0,0 +1,98 @@
+/*
+ * 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/validator.iml b/validator/validator.iml
new file mode 100644
index 0000000..f580aa3
--- /dev/null
+++ b/validator/validator.iml
@@ -0,0 +1,45 @@
+<?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$/resources" type="java-resource" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="framework.jar" level="project" />
+    <orderEntry type="module-library">
+      <library name="guava">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/22.0/guava-22.0.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/22.0/guava-22.0-sources.jar!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module" module-name="common" />
+    <orderEntry type="library" name="framework.jar" level="project" />
+    <orderEntry type="library" scope="TEST" name="hamcrest" level="project" />
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/com/google/protobuf/protobuf-lite/3.0.1/protobuf-lite-3.0.1.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/org/jsoup/jsoup/1.6.3/jsoup-1.6.3.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+  </component>
+</module>
\ No newline at end of file