Merge commit 'e08839c2e6a1b607abbba902ad0d578f8147f6fc' into HEAD

Change-Id: Ia49a7b76ecc99c27b48806f063416451b3df3904
diff --git a/bridge/src/android/animation/PropertyValuesHolder_Delegate.java b/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
index 28a489a..1d7026c 100644
--- a/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
+++ b/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
@@ -106,9 +106,7 @@
         try {
             method.setAccessible(true);
             method.invoke(target, args);
-        } catch (IllegalAccessException e) {
-            Bridge.getLog().error(null, "Unable to update property during animation", e, null);
-        } catch (InvocationTargetException e) {
+        } catch (IllegalAccessException | InvocationTargetException e) {
             Bridge.getLog().error(null, "Unable to update property during animation", e, null);
         }
     }
diff --git a/bridge/src/android/content/res/BridgeTypedArray.java b/bridge/src/android/content/res/BridgeTypedArray.java
index 69242ac..5536c4f 100644
--- a/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/bridge/src/android/content/res/BridgeTypedArray.java
@@ -32,6 +32,7 @@
 import android.content.res.Resources.NotFoundException;
 import android.content.res.Resources.Theme;
 import android.graphics.Typeface;
+import android.graphics.Typeface_Accessor;
 import android.graphics.drawable.Drawable;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
@@ -297,7 +298,7 @@
         }
 
         ColorStateList colorStateList = ResourceHelper.getColorStateList(
-                mResourceData[index], mContext);
+                mResourceData[index], mContext, mTheme);
         if (colorStateList != null) {
             return colorStateList.getDefaultColor();
         }
@@ -311,7 +312,7 @@
             return null;
         }
 
-        return ResourceHelper.getColorStateList(mResourceData[index], mContext);
+        return ResourceHelper.getColorStateList(mResourceData[index], mContext, mTheme);
     }
 
     @Override
@@ -320,7 +321,7 @@
             return null;
         }
 
-        return ResourceHelper.getComplexColor(mResourceData[index], mContext);
+        return ResourceHelper.getComplexColor(mResourceData[index], mContext, mTheme);
     }
 
     /**
@@ -607,6 +608,11 @@
             return defValue;
         }
 
+        if (Typeface_Accessor.isSystemFont(value)) {
+            // A system font family value, do not try to parse
+            return defValue;
+        }
+
         // Handle the @id/<name>, @+id/<name> and @android:id/<name>
         // We need to return the exact value that was compiled (from the various R classes),
         // as these values can be reused internally with calls to findViewById().
diff --git a/bridge/src/android/content/res/Resources_Delegate.java b/bridge/src/android/content/res/Resources_Delegate.java
index 7ea5061..c1e9cd3 100644
--- a/bridge/src/android/content/res/Resources_Delegate.java
+++ b/bridge/src/android/content/res/Resources_Delegate.java
@@ -45,6 +45,7 @@
 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.icu.text.PluralRules;
@@ -259,9 +260,9 @@
 
         if (resValue != null) {
             ColorStateList stateList = ResourceHelper.getColorStateList(resValue.getSecond(),
-                    getContext(resources));
+                    getContext(resources), theme);
             if (stateList != null) {
-                return stateList.obtainForTheme(theme);
+                return stateList;
             }
         }
 
@@ -382,10 +383,18 @@
         for (Iterator<String> iterator = resValue.iterator(); iterator.hasNext(); i++) {
             String element = resolveReference(resources, iterator.next(), resValue.isFramework());
             try {
-                values[i] = getInt(element);
+                if (element.startsWith("#")) {
+                    // This integer represents a color (starts with #)
+                    values[i] = Color.parseColor(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);
+            } catch (IllegalArgumentException e2) {
+                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
+                        "Integer resource array contains wrong color format: " + element, null);
             }
         }
         return values;
diff --git a/bridge/src/android/graphics/BaseCanvas_Delegate.java b/bridge/src/android/graphics/BaseCanvas_Delegate.java
index cc71053..f1c63e6 100644
--- a/bridge/src/android/graphics/BaseCanvas_Delegate.java
+++ b/bridge/src/android/graphics/BaseCanvas_Delegate.java
@@ -393,7 +393,6 @@
         }
 
         final NinePatchChunk chunkObject = NinePatch_Delegate.getChunk(c);
-        assert chunkObject != null;
         if (chunkObject == null) {
             return;
         }
@@ -479,7 +478,7 @@
     @LayoutlibDelegate
     /*package*/ static void nDrawText(long nativeCanvas, char[] text, int index, int count,
             float startX, float startY, int flags, long paint, long typeface) {
-        drawText(nativeCanvas, text, index, count, startX, startY, (flags & 1) != 0,
+        drawText(nativeCanvas, text, index, count, startX, startY, flags,
                 paint, typeface);
     }
 
@@ -502,14 +501,16 @@
         char[] buffer = TemporaryBuffer.obtain(count);
         TextUtils.getChars(text, start, end, buffer, 0);
 
-        drawText(nativeCanvas, buffer, 0, count, x, y, isRtl, paint, typeface);
+        drawText(nativeCanvas, buffer, 0, count, x, y, isRtl ? Paint.BIDI_RTL : Paint.BIDI_LTR,
+                paint,
+                typeface);
     }
 
     @LayoutlibDelegate
     /*package*/ static void nDrawTextRun(long nativeCanvas, char[] text,
             int start, int count, int contextStart, int contextCount,
             float x, float y, boolean isRtl, long paint, long typeface) {
-        drawText(nativeCanvas, text, start, count, x, y, isRtl, paint, typeface);
+        drawText(nativeCanvas, text, start, count, x, y, isRtl ? Paint.BIDI_RTL : Paint.BIDI_LTR, paint, typeface);
     }
 
     @LayoutlibDelegate
@@ -574,7 +575,7 @@
     }
 
     private static void drawText(long nativeCanvas, final char[] text, final int index,
-            final int count, final float startX, final float startY, final boolean isRtl,
+            final int count, final float startX, final float startY, final int bidiFlags,
             long paint, final long typeface) {
 
         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
@@ -591,7 +592,7 @@
                     int limit = index + count;
                     if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
                         RectF bounds =
-                                paintDelegate.measureText(text, index, count, null, 0, isRtl);
+                                paintDelegate.measureText(text, index, count, null, 0, bidiFlags);
                         float m = bounds.right - bounds.left;
                         if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
                             x -= m / 2;
@@ -601,7 +602,7 @@
                     }
 
                     new BidiRenderer(graphics, paintDelegate, text).setRenderLocation(x,
-                            startY).renderText(index, limit, isRtl, null, 0, true);
+                            startY).renderText(index, limit, bidiFlags, null, 0, true);
                 });
     }
 
diff --git a/bridge/src/android/graphics/BidiRenderer.java b/bridge/src/android/graphics/BidiRenderer.java
index c6827a3..63691c3 100644
--- a/bridge/src/android/graphics/BidiRenderer.java
+++ b/bridge/src/android/graphics/BidiRenderer.java
@@ -19,8 +19,9 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.Paint_Delegate.FontInfo;
-import android.icu.lang.UScript;
 import android.icu.lang.UScriptRun;
 import android.icu.text.Bidi;
 import android.icu.text.BidiRun;
@@ -32,7 +33,6 @@
 import java.awt.font.GlyphVector;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Rectangle2D;
-import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -45,26 +45,20 @@
     private static String JAVA_VENDOR = System.getProperty("java.vendor");
 
     private static class ScriptRun {
-        int start;
-        int limit;
-        boolean isRtl;
-        int scriptCode;
-        Font font;
+        private final int start;
+        private final int limit;
+        private final Font font;
 
-        public ScriptRun(int start, int limit, boolean isRtl) {
+        private ScriptRun(int start, int limit, @NonNull Font font) {
             this.start = start;
             this.limit = limit;
-            this.isRtl = isRtl;
-            this.scriptCode = UScript.INVALID_CODE;
+            this.font = font;
         }
     }
 
     private final Graphics2D mGraphics;
     private final Paint_Delegate mPaint;
     private char[] mText;
-    // This List can contain nulls. A null font implies that the we weren't able to load the font
-    // properly. So, if we encounter a situation where we try to use that font, log a warning.
-    private List<Font> mFonts;
     // Bounds of the text drawn so far.
     private RectF mBounds;
     private float mBaseline;
@@ -79,14 +73,6 @@
         mGraphics = graphics;
         mPaint = paint;
         mText = text;
-        mFonts = new ArrayList<Font>(paint.getFonts().size());
-        for (FontInfo fontInfo : paint.getFonts()) {
-            if (fontInfo == null) {
-                mFonts.add(null);
-                continue;
-            }
-            mFonts.add(fontInfo.mFont);
-        }
         mBounds = new RectF();
     }
 
@@ -98,7 +84,7 @@
      *
      */
     public BidiRenderer setRenderLocation(float x, float y) {
-        mBounds = new RectF(x, y, x, y);
+        mBounds.set(x, y, x, y);
         mBaseline = y;
         return this;
     }
@@ -112,6 +98,7 @@
     public RectF renderText(int start, int limit, int bidiFlags, float[] advances,
             int advancesIndex, boolean draw) {
         Bidi bidi = new Bidi(mText, start, null, 0, limit - start, getIcuFlags(bidiFlags));
+        mText = bidi.getText();
         for (int i = 0; i < bidi.countRuns(); i++) {
             BidiRun visualRun = bidi.getVisualRun(i);
             boolean isRtl = visualRun.getDirection() == Bidi.RTL;
@@ -140,7 +127,7 @@
             int advancesIndex, boolean draw) {
         // We break the text into scripts and then select font based on it and then render each of
         // the script runs.
-        for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mFonts)) {
+        for (ScriptRun run : getScriptRuns(mText, start, limit, mPaint.getFonts())) {
             int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT;
             flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT;
             renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw);
@@ -156,7 +143,7 @@
      */
     private void renderScript(int start, int limit, Font preferredFont, int flag,
             float[] advances, int advancesIndex, boolean draw) {
-        if (mFonts.size() == 0 || preferredFont == null) {
+        if (mPaint.getFonts().size() == 0 || preferredFont == null) {
             return;
         }
 
@@ -178,7 +165,10 @@
             // The current character cannot be drawn with the preferred font. Cycle through all the
             // fonts to check which one can draw it.
             int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1;
-            for (Font font : mFonts) {
+            List<FontInfo> fontInfos = mPaint.getFonts();
+            //noinspection ForLoopReplaceableByForEach (avoid iterator allocation)
+            for (int i = 0; i < fontInfos.size(); i++) {
+                Font font = fontInfos.get(i).mFont;
                 if (font == null) {
                     logFontWarning();
                     continue;
@@ -249,60 +239,62 @@
 
         // Update the bounds.
         Rectangle2D awtBounds = gv.getLogicalBounds();
-        RectF bounds = awtRectToAndroidRect(awtBounds, mBounds.right, mBaseline);
         // If the width of the bounds is zero, no text had been drawn earlier. Hence, use the
         // coordinates from the bounds as an offset.
         if (Math.abs(mBounds.right - mBounds.left) == 0) {
-            mBounds = bounds;
+            mBounds = awtRectToAndroidRect(awtBounds, mBounds.right, mBaseline, mBounds);
         } else {
-            mBounds.union(bounds);
+            mBounds.union(awtRectToAndroidRect(awtBounds, mBounds.right, mBaseline, null));
         }
     }
 
     // --- Static helper methods ---
 
-    private static RectF awtRectToAndroidRect(Rectangle2D awtRec, float offsetX, float offsetY) {
+    private static RectF awtRectToAndroidRect(Rectangle2D awtRec, float offsetX, float offsetY,
+            @Nullable RectF destination) {
         float left = (float) awtRec.getX();
         float top = (float) awtRec.getY();
         float right = (float) (left + awtRec.getWidth());
         float bottom = (float) (top + awtRec.getHeight());
-        RectF androidRect = new RectF(left, top, right, bottom);
-        androidRect.offset(offsetX, offsetY);
-        return androidRect;
+        if (destination != null) {
+            destination.set(left, top, right, bottom);
+        } else {
+            destination = new RectF(left, top, right, bottom);
+        }
+        destination.offset(offsetX, offsetY);
+        return destination;
     }
 
-    /* package */  static List<ScriptRun> getScriptRuns(char[] text, int start, int limit,
-            boolean isRtl, List<Font> fonts) {
-        LinkedList<ScriptRun> scriptRuns = new LinkedList<ScriptRun>();
+    private static List<ScriptRun> getScriptRuns(char[] text, int start, int limit, List<FontInfo> fonts) {
+        LinkedList<ScriptRun> scriptRuns = new LinkedList<>();
 
         int count = limit - start;
         UScriptRun uScriptRun = new UScriptRun(text, start, count);
         while (uScriptRun.next()) {
             int scriptStart = uScriptRun.getScriptStart();
             int scriptLimit = uScriptRun.getScriptLimit();
-            ScriptRun run = new ScriptRun(scriptStart, scriptLimit, isRtl);
-            run.scriptCode = uScriptRun.getScriptCode();
-            setScriptFont(text, run, fonts);
+            ScriptRun run = new ScriptRun(
+                    scriptStart, scriptLimit,
+                    getScriptFont(text, scriptStart, scriptLimit, fonts));
             scriptRuns.add(run);
         }
-
         return scriptRuns;
     }
 
     // TODO: Replace this method with one which returns the font based on the scriptCode.
-    private static void setScriptFont(char[] text, ScriptRun run,
-            List<Font> fonts) {
-        for (Font font : fonts) {
-            if (font == null) {
+    @NonNull
+    private static Font getScriptFont(char[] text, int start, int limit, List<FontInfo> fonts) {
+        for (FontInfo fontInfo : fonts) {
+            if (fontInfo.mFont == null) {
                 logFontWarning();
                 continue;
             }
-            if (font.canDisplayUpTo(text, run.start, run.limit) == -1) {
-                run.font = font;
-                return;
+            if (fontInfo.mFont.canDisplayUpTo(text, start, limit) == -1) {
+                return fontInfo.mFont;
             }
         }
-        run.font = fonts.get(0);
+
+        return fonts.get(0).mFont;
     }
 
     private static int getIcuFlags(int bidiFlag) {
diff --git a/bridge/src/android/graphics/ColorFilter_Delegate.java b/bridge/src/android/graphics/ColorFilter_Delegate.java
index cb013b6..84424bc 100644
--- a/bridge/src/android/graphics/ColorFilter_Delegate.java
+++ b/bridge/src/android/graphics/ColorFilter_Delegate.java
@@ -21,6 +21,8 @@
 
 import java.awt.Graphics2D;
 
+import libcore.util.NativeAllocationRegistry_Delegate;
+
 /**
  * Delegate implementing the native methods of android.graphics.ColorFilter
  *
@@ -41,6 +43,7 @@
     // ---- delegate manager ----
     protected static final DelegateManager<ColorFilter_Delegate> sManager =
             new DelegateManager<ColorFilter_Delegate>(ColorFilter_Delegate.class);
+    private static long sFinalizer = -1;
 
     // ---- delegate helper data ----
 
@@ -66,8 +69,14 @@
     // ---- native methods ----
 
     @LayoutlibDelegate
-    /*package*/ static void nSafeUnref(long native_instance) {
-        sManager.removeJavaReferenceFor(native_instance);
+    /*package*/ static long nativeGetFinalizer() {
+        synchronized (ColorFilter_Delegate.class) {
+            if (sFinalizer == -1) {
+                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
+                        sManager::removeJavaReferenceFor);
+            }
+        }
+        return sFinalizer;
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/bridge/src/android/graphics/NinePatch_Delegate.java b/bridge/src/android/graphics/NinePatch_Delegate.java
index 1f0eb3b..43e5b0f 100644
--- a/bridge/src/android/graphics/NinePatch_Delegate.java
+++ b/bridge/src/android/graphics/NinePatch_Delegate.java
@@ -19,14 +19,11 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.GcSnapshot;
 import com.android.ninepatch.NinePatchChunk;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.graphics.drawable.NinePatchDrawable;
 
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -50,7 +47,7 @@
 
     // ---- delegate manager ----
     private static final DelegateManager<NinePatch_Delegate> sManager =
-            new DelegateManager<NinePatch_Delegate>(NinePatch_Delegate.class);
+            new DelegateManager<>(NinePatch_Delegate.class);
 
     // ---- delegate helper data ----
     /**
@@ -62,8 +59,7 @@
      * Using the cache map allows us to not have to deserialize the byte[] back into a
      * {@link NinePatchChunk} every time a rendering is done.
      */
-    private final static Map<byte[], SoftReference<NinePatchChunk>> sChunkCache =
-        new HashMap<byte[], SoftReference<NinePatchChunk>>();
+    private final static Map<byte[], SoftReference<NinePatchChunk>> sChunkCache = new HashMap<>();
 
     // ---- delegate data ----
     private byte[] chunk;
@@ -97,7 +93,7 @@
 
         // get the array and add it to the cache
         byte[] array = baos.toByteArray();
-        sChunkCache.put(array, new SoftReference<NinePatchChunk>(chunk));
+        sChunkCache.put(array, new SoftReference<>(chunk));
         return array;
     }
 
@@ -122,7 +118,7 @@
 
                 // put back the chunk in the cache
                 if (chunk != null) {
-                    sChunkCache.put(array, new SoftReference<NinePatchChunk>(chunk));
+                    sChunkCache.put(array, new SoftReference<>(chunk));
                 }
             } catch (IOException e) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
@@ -151,7 +147,6 @@
     /*package*/ static boolean isNinePatchChunk(byte[] chunk) {
         NinePatchChunk chunkObject = getChunk(chunk);
         return chunkObject != null;
-
     }
 
     @LayoutlibDelegate
diff --git a/bridge/src/android/graphics/Paint_Delegate.java b/bridge/src/android/graphics/Paint_Delegate.java
index 60e5cd9..406157e 100644
--- a/bridge/src/android/graphics/Paint_Delegate.java
+++ b/bridge/src/android/graphics/Paint_Delegate.java
@@ -1158,6 +1158,26 @@
         return distanceToI > distanceToIMinus1 ? i : i - 1;
     }
 
+    @LayoutlibDelegate
+    /*package*/ static float nGetUnderlinePosition(long paintPtr, long typefacePtr) {
+        return (1.0f / 9.0f) * nGetTextSize(paintPtr);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float nGetUnderlineThickness(long paintPtr, long typefacePtr) {
+        return (1.0f / 18.0f) * nGetTextSize(paintPtr);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float nGetStrikeThruPosition(long paintPtr, long typefacePtr) {
+        return (-79.0f / 252.0f) * nGetTextSize(paintPtr);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float nGetStrikeThruThickness(long paintPtr, long typefacePtr) {
+        return (1.0f / 18.0f) * nGetTextSize(paintPtr);
+    }
+
     // ---- Private delegate/helper methods ----
 
     /*package*/ Paint_Delegate() {
diff --git a/bridge/src/android/graphics/drawable/AdaptiveIconDrawable_Delegate.java b/bridge/src/android/graphics/drawable/AdaptiveIconDrawable_Delegate.java
new file mode 100644
index 0000000..ca6bc85
--- /dev/null
+++ b/bridge/src/android/graphics/drawable/AdaptiveIconDrawable_Delegate.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.content.res.Resources;
+import android.content.res.Resources_Delegate;
+import android.util.PathParser;
+
+import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_ADAPTIVE_ICON_MASK_PATH;
+
+public class AdaptiveIconDrawable_Delegate {
+
+    @LayoutlibDelegate
+    /*package*/ static void constructor_after(AdaptiveIconDrawable icon) {
+        String pathString = Resources_Delegate.getLayoutlibCallback(Resources.getSystem()).getFlag(
+                FLAG_KEY_ADAPTIVE_ICON_MASK_PATH);
+        if (pathString != null) {
+            AdaptiveIconDrawable.sMask = PathParser.createPathFromPathData(pathString);
+        }
+    }
+}
diff --git a/bridge/src/android/preference/BridgePreferenceInflater.java b/bridge/src/android/preference/BridgePreferenceInflater.java
index aa393a9..cb56116 100644
--- a/bridge/src/android/preference/BridgePreferenceInflater.java
+++ b/bridge/src/android/preference/BridgePreferenceInflater.java
@@ -30,7 +30,7 @@
     }
 
     @Override
-    protected Preference onCreateItem(String name, AttributeSet attrs)
+    public Preference createItem(String name, String prefix, AttributeSet attrs)
             throws ClassNotFoundException {
         Object viewKey = null;
         BridgeContext bc = null;
@@ -39,17 +39,19 @@
         if (context instanceof BridgeContext) {
             bc = (BridgeContext) context;
         }
+
         if (attrs instanceof BridgeXmlBlockParser) {
             viewKey = ((BridgeXmlBlockParser) attrs).getViewCookie();
         }
 
         Preference preference = null;
         try {
-            preference = super.onCreateItem(name, attrs);
+            preference = super.createItem(name, prefix, attrs);
         } catch (ClassNotFoundException | InflateException exception) {
             // name is probably not a valid preference type
-            if ("SwitchPreferenceCompat".equals(name)) {
-                preference = super.onCreateItem("SwitchPreference", attrs);
+            if ("android.support.v7.preference".equals(prefix) &&
+                    "SwitchPreferenceCompat".equals(name)) {
+                preference = super.createItem("SwitchPreference", prefix, attrs);
             }
         }
 
diff --git a/bridge/src/android/text/StaticLayout_Delegate.java b/bridge/src/android/text/StaticLayout_Delegate.java
index cc03143..54854b5 100644
--- a/bridge/src/android/text/StaticLayout_Delegate.java
+++ b/bridge/src/android/text/StaticLayout_Delegate.java
@@ -59,11 +59,12 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nSetLocale(long nativeBuilder, String locale, long nativeHyphenator) {
+    /*package*/ static void nSetLocales(long nativeBuilder, String locales,
+            long[] nativeHyphenators) {
         Builder builder = sBuilderManager.getDelegate(nativeBuilder);
         if (builder != null) {
-            builder.mLocale = locale;
-            builder.mNativeHyphenator = nativeHyphenator;
+            builder.mLocales = locales;
+            builder.mNativeHyphenators = nativeHyphenators;
         }
     }
 
@@ -138,7 +139,7 @@
 
         // compute all possible breakpoints.
         int length = builder.mWidths.length;
-        BreakIterator it = BreakIterator.getLineInstance(new ULocale(builder.mLocale));
+        BreakIterator it = BreakIterator.getLineInstance(new ULocale(builder.mLocales));
         it.setText(new Segment(builder.mText, 0, length));
 
         // average word length in english is 5. So, initialize the possible breaks with a guess.
@@ -226,11 +227,11 @@
      * Java representation of the native Builder class.
      */
     private static class Builder {
-        String mLocale;
+        String mLocales;
         char[] mText;
         float[] mWidths;
         LineBreaker mLineBreaker;
-        long mNativeHyphenator;
+        long[] mNativeHyphenators;
         int mBreakStrategy;
         LineWidth mLineWidth;
         TabStops mTabStopCalculator;
diff --git a/bridge/src/android/util/PathParser_Delegate.java b/bridge/src/android/util/PathParser_Delegate.java
index 7b69388..4010b67 100644
--- a/bridge/src/android/util/PathParser_Delegate.java
+++ b/bridge/src/android/util/PathParser_Delegate.java
@@ -286,6 +286,8 @@
             switch (currentChar) {
                 case ' ':
                 case ',':
+                case '\t':
+                case '\n':
                     foundSeparator = true;
                     break;
                 case '-':
diff --git a/bridge/src/android/view/Choreographer_Delegate.java b/bridge/src/android/view/Choreographer_Delegate.java
index 494ffa1..1dc7778 100644
--- a/bridge/src/android/view/Choreographer_Delegate.java
+++ b/bridge/src/android/view/Choreographer_Delegate.java
@@ -15,12 +15,12 @@
  */
 package android.view;
 
-import com.android.ide.common.rendering.api.LayoutLog;
-import com.android.layoutlib.bridge.Bridge;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-import com.android.tools.layoutlib.java.System_Delegate;
 
-import java.lang.reflect.Field;
+import android.animation.AnimationHandler;
+import android.util.TimeUtils;
+import android.view.animation.AnimationUtils;
+
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -31,7 +31,7 @@
  *
  */
 public class Choreographer_Delegate {
-    static final AtomicReference<Choreographer> mInstance = new AtomicReference<Choreographer>();
+    private static final AtomicReference<Choreographer> mInstance = new AtomicReference<Choreographer>();
 
     @LayoutlibDelegate
     public static Choreographer getInstance() {
@@ -55,31 +55,39 @@
     public static void doFrame(long frameTimeNanos) {
         Choreographer thisChoreographer = Choreographer.getInstance();
 
-        thisChoreographer.mLastFrameTimeNanos = frameTimeNanos - thisChoreographer
-                .getFrameIntervalNanos();
-        thisChoreographer.mFrameInfo.markInputHandlingStart();
-        thisChoreographer.doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
+        AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
 
-        thisChoreographer.mFrameInfo.markAnimationsStart();
-        thisChoreographer.doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
+        try {
+            thisChoreographer.mLastFrameTimeNanos = frameTimeNanos - thisChoreographer.getFrameIntervalNanos();
+            thisChoreographer.mFrameInfo.markInputHandlingStart();
+            thisChoreographer.doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
 
-        thisChoreographer.mFrameInfo.markPerformTraversalsStart();
-        thisChoreographer.doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
+            thisChoreographer.mFrameInfo.markAnimationsStart();
+            thisChoreographer.doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
 
-        thisChoreographer.doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
+            thisChoreographer.mFrameInfo.markPerformTraversalsStart();
+            thisChoreographer.doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
+
+            thisChoreographer.doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
+        } finally {
+            AnimationUtils.unlockAnimationClock();
+        }
+    }
+
+    public static void clearFrames() {
+        Choreographer thisChoreographer = Choreographer.getInstance();
+
+        thisChoreographer.removeCallbacks(Choreographer.CALLBACK_INPUT, null, null);
+        thisChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, null, null);
+        thisChoreographer.removeCallbacks(Choreographer.CALLBACK_TRAVERSAL, null, null);
+        thisChoreographer.removeCallbacks(Choreographer.CALLBACK_COMMIT, null, null);
+
+        // Release animation handler instance since it holds references to the callbacks
+        AnimationHandler.sAnimatorHandler.set(null);
     }
 
     public static void dispose() {
-        try {
-            Field threadInstanceField = Choreographer.class.getDeclaredField("sThreadInstance");
-            threadInstanceField.setAccessible(true);
-            @SuppressWarnings("unchecked") ThreadLocal<Choreographer> threadInstance =
-                    (ThreadLocal<Choreographer>) threadInstanceField.get(null);
-            threadInstance.remove();
-        } catch (ReflectiveOperationException e) {
-            assert false;
-            Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                    "Unable to clear Choreographer memory.", e, null);
-        }
+        clearFrames();
+        Choreographer.releaseInstance();
     }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
index fdf6d63..dd2668f 100644
--- a/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -16,9 +16,6 @@
 
 package com.android.layoutlib.bridge;
 
-import com.android.ide.common.rendering.api.IAnimationListener;
-import com.android.ide.common.rendering.api.ILayoutPullParser;
-import com.android.ide.common.rendering.api.RenderParams;
 import com.android.ide.common.rendering.api.RenderSession;
 import com.android.ide.common.rendering.api.Result;
 import com.android.ide.common.rendering.api.ViewInfo;
@@ -28,8 +25,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.view.View;
-import android.view.ViewGroup;
 
 import java.awt.image.BufferedImage;
 import java.util.Collections;
@@ -122,100 +117,6 @@
     }
 
     @Override
-    public Result animate(Object targetObject, String animationName,
-            boolean isFrameworkAnimation, IAnimationListener listener) {
-        if (mSession != null) {
-            try {
-                Bridge.prepareThread();
-                mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
-                if (mLastResult.isSuccess()) {
-                    mLastResult = mSession.animate(targetObject, animationName, isFrameworkAnimation,
-                            listener);
-                }
-            } finally {
-                mSession.release();
-                Bridge.cleanupThread();
-            }
-        }
-
-        return mLastResult;
-    }
-
-    @Override
-    public Result insertChild(Object parentView, ILayoutPullParser childXml, int index,
-            IAnimationListener listener) {
-        if (!(parentView instanceof ViewGroup)) {
-            throw new IllegalArgumentException("parentView is not a ViewGroup");
-        }
-
-        if (mSession != null) {
-            try {
-                Bridge.prepareThread();
-                mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
-                if (mLastResult.isSuccess()) {
-                    mLastResult =
-                            mSession.insertChild((ViewGroup) parentView, childXml, index, listener);
-                }
-            } finally {
-                mSession.release();
-                Bridge.cleanupThread();
-            }
-        }
-
-        return mLastResult;
-    }
-
-
-    @Override
-    public Result moveChild(Object parentView, Object childView, int index,
-            Map<String, String> layoutParams, IAnimationListener listener) {
-        if (!(parentView instanceof ViewGroup)) {
-            throw new IllegalArgumentException("parentView is not a ViewGroup");
-        }
-        if (!(childView instanceof View)) {
-            throw new IllegalArgumentException("childView is not a View");
-        }
-
-        if (mSession != null) {
-            try {
-                Bridge.prepareThread();
-                mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
-                if (mLastResult.isSuccess()) {
-                    mLastResult = mSession.moveChild((ViewGroup) parentView, (View) childView, index,
-                            layoutParams, listener);
-                }
-            } finally {
-                mSession.release();
-                Bridge.cleanupThread();
-            }
-        }
-
-        return mLastResult;
-    }
-
-    @Override
-    public Result removeChild(Object childView, IAnimationListener listener) {
-        if (!(childView instanceof View)) {
-            throw new IllegalArgumentException("childView is not a View");
-        }
-
-        if (mSession != null) {
-            try {
-                Bridge.prepareThread();
-                mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
-                if (mLastResult.isSuccess()) {
-                    mLastResult = mSession.removeChild((View) childView, listener);
-                }
-            } finally {
-                mSession.release();
-                Bridge.cleanupThread();
-            }
-        }
-
-        return mLastResult;
-    }
-
-    @Override
     public void setSystemTimeNanos(long nanos) {
         System_Delegate.setNanosTime(nanos);
     }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 0f4cbb7..4c6c9d4 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -2017,7 +2017,7 @@
 
     @Override
     public boolean canLoadUnsafeResources() {
-        return false;
+        return true;
     }
 
     private class AttributeHolder {
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index 59a9e6c..98937ef 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -288,6 +288,11 @@
     }
 
     @Override
+    public String[] getNamesForUids(int[] uids) {
+        return null;
+    }
+
+    @Override
     public int getUidForSharedUser(String sharedUserName) throws NameNotFoundException {
         return 0;
     }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java b/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
index 051de90..a20652f 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
@@ -59,6 +59,11 @@
      */
     public static final Key<Boolean> FLAG_DO_NOT_RENDER_ON_CREATE =
             new Key<Boolean>("doNotRenderOnCreate", Boolean.class);
+    /**
+     * The adaptive icon mask path. Used via {@link LayoutlibCallback#getFlag(Key)}
+     */
+    public static final Key<String> FLAG_KEY_ADAPTIVE_ICON_MASK_PATH =
+            new Key<>("adaptiveIconMaskPath", String.class);
 
     // Disallow instances.
     private RenderParamsFlags() {}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java
new file mode 100644
index 0000000..730ac13
--- /dev/null
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.android.support;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+
+import android.content.Context;
+import android.widget.TabHost;
+
+import static com.android.layoutlib.bridge.util.ReflectionUtils.getCause;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
+
+/**
+ * Utility class for working with android.support.v4.app.FragmentTabHost
+ */
+public class FragmentTabHostUtil {
+
+    public static final String CN_FRAGMENT_TAB_HOST = "android.support.v4.app.FragmentTabHost";
+
+    /**
+     * Calls the setup method for the FragmentTabHost tabHost
+     */
+    public static void setup(TabHost tabHost, Context context) {
+        try {
+            invoke(getMethod(tabHost.getClass(), "setup", Context.class,
+                    Class.forName("android.support.v4.app.FragmentManager", true,
+                            tabHost.getClass().getClassLoader()), int.class), tabHost, context, null,
+                    android.R.id.tabcontent);
+        } catch (ReflectionException | ClassNotFoundException e) {
+            Throwable cause = getCause(e);
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                    "Error occurred while trying to setup FragmentTabHost.", cause, null);
+        }
+    }
+}
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 2984fc0..369f1c6 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -209,7 +209,7 @@
                 textColor = res.resolveResValue(textColor);
                 if (textColor != null) {
                     ColorStateList stateList = ResourceHelper.getColorStateList(
-                            textColor, bridgeContext);
+                            textColor, bridgeContext, null);
                     if (stateList != null) {
                         textView.setTextColor(stateList);
                     }
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index 4696b56..5b42df1 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -29,6 +29,7 @@
 import com.android.resources.ScreenOrientation;
 import com.android.resources.ScreenRound;
 import com.android.resources.ScreenSize;
+import com.android.tools.layoutlib.annotations.VisibleForTesting;
 
 import android.content.res.Configuration;
 import android.os.HandlerThread_Delegate;
@@ -67,7 +68,8 @@
      * The current context being rendered. This is set through {@link #acquire(long)} and
      * {@link #init(long)}, and unset in {@link #release()}.
      */
-    private static BridgeContext sCurrentContext = null;
+    @VisibleForTesting
+    static BridgeContext sCurrentContext = null;
 
     private final T mParams;
 
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 70a854f..2e31980 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -18,8 +18,6 @@
 
 import com.android.ide.common.rendering.api.AdapterBinding;
 import com.android.ide.common.rendering.api.HardwareConfig;
-import com.android.ide.common.rendering.api.IAnimationListener;
-import com.android.ide.common.rendering.api.ILayoutPullParser;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.RenderResources;
@@ -27,7 +25,6 @@
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.Result;
-import com.android.ide.common.rendering.api.Result.Status;
 import com.android.ide.common.rendering.api.SessionParams;
 import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
 import com.android.ide.common.rendering.api.ViewInfo;
@@ -40,24 +37,20 @@
 import com.android.internal.view.menu.MenuView;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
-import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
 import com.android.layoutlib.bridge.android.RenderParamsFlags;
 import com.android.layoutlib.bridge.android.graphics.NopCanvas;
 import com.android.layoutlib.bridge.android.support.DesignLibUtil;
+import com.android.layoutlib.bridge.android.support.FragmentTabHostUtil;
 import com.android.layoutlib.bridge.android.support.SupportPreferencesUtil;
 import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
 import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
+import com.android.layoutlib.bridge.util.ReflectionUtils;
 import com.android.resources.ResourceType;
 import com.android.tools.layoutlib.java.System_Delegate;
 import com.android.util.Pair;
 import com.android.util.PropertiesMap;
 
-import android.animation.AnimationThread;
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.animation.LayoutTransition;
-import android.animation.LayoutTransition.TransitionListener;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Fragment_Delegate;
@@ -96,11 +89,9 @@
 import java.util.List;
 import java.util.Map;
 
-import static com.android.ide.common.rendering.api.Result.Status.ERROR_ANIM_NOT_FOUND;
 import static com.android.ide.common.rendering.api.Result.Status.ERROR_INFLATION;
 import static com.android.ide.common.rendering.api.Result.Status.ERROR_NOT_INFLATED;
 import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN;
-import static com.android.ide.common.rendering.api.Result.Status.ERROR_VIEWGROUP_NO_CHILDREN;
 import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
 import static com.android.layoutlib.bridge.util.ReflectionUtils.isInstanceOf;
 
@@ -357,6 +348,8 @@
                     visitAllChildren(mViewRoot, 0, 0, params.getExtendedViewInfoMode(),
                     false);
 
+            Choreographer_Delegate.clearFrames();
+
             return SUCCESS.createResult();
         } catch (PostInflateException e) {
             return ERROR_INFLATION.createResult(e.getMessage(), e);
@@ -604,407 +597,6 @@
     }
 
     /**
-     * Animate an object
-     * <p>
-     * {@link #acquire(long)} must have been called before this.
-     *
-     * @throws IllegalStateException if the current context is different than the one owned by
-     *      the scene, or if {@link #acquire(long)} was not called.
-     *
-     * @see RenderSession#animate(Object, String, boolean, IAnimationListener)
-     */
-    public Result animate(Object targetObject, String animationName,
-            boolean isFrameworkAnimation, IAnimationListener listener) {
-        checkLock();
-
-        BridgeContext context = getContext();
-
-        // find the animation file.
-        ResourceValue animationResource;
-        int animationId = 0;
-        if (isFrameworkAnimation) {
-            animationResource = context.getRenderResources().getFrameworkResource(
-                    ResourceType.ANIMATOR, animationName);
-            if (animationResource != null) {
-                animationId = Bridge.getResourceId(ResourceType.ANIMATOR, animationName);
-            }
-        } else {
-            animationResource = context.getRenderResources().getProjectResource(
-                    ResourceType.ANIMATOR, animationName);
-            if (animationResource != null) {
-                animationId = context.getLayoutlibCallback().getResourceId(
-                        ResourceType.ANIMATOR, animationName);
-            }
-        }
-
-        if (animationResource != null) {
-            try {
-                Animator anim = AnimatorInflater.loadAnimator(context, animationId);
-                if (anim != null) {
-                    anim.setTarget(targetObject);
-
-                    new PlayAnimationThread(anim, this, animationName, listener).start();
-
-                    return SUCCESS.createResult();
-                }
-            } catch (Exception e) {
-                // get the real cause of the exception.
-                Throwable t = e;
-                while (t.getCause() != null) {
-                    t = t.getCause();
-                }
-
-                return ERROR_UNKNOWN.createResult(t.getMessage(), t);
-            }
-        }
-
-        return ERROR_ANIM_NOT_FOUND.createResult();
-    }
-
-    /**
-     * Insert a new child into an existing parent.
-     * <p>
-     * {@link #acquire(long)} must have been called before this.
-     *
-     * @throws IllegalStateException if the current context is different than the one owned by
-     *      the scene, or if {@link #acquire(long)} was not called.
-     *
-     * @see RenderSession#insertChild(Object, ILayoutPullParser, int, IAnimationListener)
-     */
-    public Result insertChild(final ViewGroup parentView, ILayoutPullParser childXml,
-            final int index, IAnimationListener listener) {
-        checkLock();
-
-        BridgeContext context = getContext();
-
-        // create a block parser for the XML
-        BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
-                childXml, context, false /* platformResourceFlag */);
-
-        // inflate the child without adding it to the root since we want to control where it'll
-        // get added. We do pass the parentView however to ensure that the layoutParams will
-        // be created correctly.
-        final View child = mInflater.inflate(blockParser, parentView, false /*attachToRoot*/);
-        blockParser.ensurePopped();
-
-        invalidateRenderingSize();
-
-        if (listener != null) {
-            new AnimationThread(this, "insertChild", listener) {
-
-                @Override
-                public Result preAnimation() {
-                    parentView.setLayoutTransition(new LayoutTransition());
-                    return addView(parentView, child, index);
-                }
-
-                @Override
-                public void postAnimation() {
-                    parentView.setLayoutTransition(null);
-                }
-            }.start();
-
-            // always return success since the real status will come through the listener.
-            return SUCCESS.createResult(child);
-        }
-
-        // add it to the parentView in the correct location
-        Result result = addView(parentView, child, index);
-        if (!result.isSuccess()) {
-            return result;
-        }
-
-        result = render(false /*freshRender*/);
-        if (result.isSuccess()) {
-            result = result.getCopyWithData(child);
-        }
-
-        return result;
-    }
-
-    /**
-     * Adds a given view to a given parent at a given index.
-     *
-     * @param parent the parent to receive the view
-     * @param view the view to add to the parent
-     * @param index the index where to do the add.
-     *
-     * @return a Result with {@link Status#SUCCESS} or
-     *     {@link Status#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support
-     *     adding views.
-     */
-    private Result addView(ViewGroup parent, View view, int index) {
-        try {
-            parent.addView(view, index);
-            return SUCCESS.createResult();
-        } catch (UnsupportedOperationException e) {
-            // looks like this is a view class that doesn't support children manipulation!
-            return ERROR_VIEWGROUP_NO_CHILDREN.createResult();
-        }
-    }
-
-    /**
-     * Moves a view to a new parent at a given location
-     * <p>
-     * {@link #acquire(long)} must have been called before this.
-     *
-     * @throws IllegalStateException if the current context is different than the one owned by
-     *      the scene, or if {@link #acquire(long)} was not called.
-     *
-     * @see RenderSession#moveChild(Object, Object, int, Map, IAnimationListener)
-     */
-    public Result moveChild(final ViewGroup newParentView, final View childView, final int index,
-            Map<String, String> layoutParamsMap, final IAnimationListener listener) {
-        checkLock();
-
-        invalidateRenderingSize();
-
-        LayoutParams layoutParams = null;
-        if (layoutParamsMap != null) {
-            // need to create a new LayoutParams object for the new parent.
-            layoutParams = newParentView.generateLayoutParams(
-                    new BridgeLayoutParamsMapAttributes(layoutParamsMap));
-        }
-
-        // get the current parent of the view that needs to be moved.
-        final ViewGroup previousParent = (ViewGroup) childView.getParent();
-
-        if (listener != null) {
-            final LayoutParams params = layoutParams;
-
-            // there is no support for animating views across layouts, so in case the new and old
-            // parent views are different we fake the animation through a no animation thread.
-            if (previousParent != newParentView) {
-                new Thread("not animated moveChild") {
-                    @Override
-                    public void run() {
-                        Result result = moveView(previousParent, newParentView, childView, index,
-                                params);
-                        if (!result.isSuccess()) {
-                            listener.done(result);
-                        }
-
-                        // ready to do the work, acquire the scene.
-                        result = acquire(250);
-                        if (!result.isSuccess()) {
-                            listener.done(result);
-                            return;
-                        }
-
-                        try {
-                            result = render(false /*freshRender*/);
-                            if (result.isSuccess()) {
-                                listener.onNewFrame(RenderSessionImpl.this.getSession());
-                            }
-                        } finally {
-                            release();
-                        }
-
-                        listener.done(result);
-                    }
-                }.start();
-            } else {
-                new AnimationThread(this, "moveChild", listener) {
-
-                    @Override
-                    public Result preAnimation() {
-                        // set up the transition for the parent.
-                        LayoutTransition transition = new LayoutTransition();
-                        previousParent.setLayoutTransition(transition);
-
-                        // tweak the animation durations and start delays (to match the duration of
-                        // animation playing just before).
-                        // Note: Cannot user Animation.setDuration() directly. Have to set it
-                        // on the LayoutTransition.
-                        transition.setDuration(LayoutTransition.DISAPPEARING, 100);
-                        // CHANGE_DISAPPEARING plays after DISAPPEARING
-                        transition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 100);
-
-                        transition.setDuration(LayoutTransition.CHANGE_DISAPPEARING, 100);
-
-                        transition.setDuration(LayoutTransition.CHANGE_APPEARING, 100);
-                        // CHANGE_APPEARING plays after CHANGE_APPEARING
-                        transition.setStartDelay(LayoutTransition.APPEARING, 100);
-
-                        transition.setDuration(LayoutTransition.APPEARING, 100);
-
-                        return moveView(previousParent, newParentView, childView, index, params);
-                    }
-
-                    @Override
-                    public void postAnimation() {
-                        previousParent.setLayoutTransition(null);
-                        newParentView.setLayoutTransition(null);
-                    }
-                }.start();
-            }
-
-            // always return success since the real status will come through the listener.
-            return SUCCESS.createResult(layoutParams);
-        }
-
-        Result result = moveView(previousParent, newParentView, childView, index, layoutParams);
-        if (!result.isSuccess()) {
-            return result;
-        }
-
-        result = render(false /*freshRender*/);
-        if (layoutParams != null && result.isSuccess()) {
-            result = result.getCopyWithData(layoutParams);
-        }
-
-        return result;
-    }
-
-    /**
-     * Moves a View from its current parent to a new given parent at a new given location, with
-     * an optional new {@link LayoutParams} instance
-     *
-     * @param previousParent the previous parent, still owning the child at the time of the call.
-     * @param newParent the new parent
-     * @param movedView the view to move
-     * @param index the new location in the new parent
-     * @param params an option (can be null) {@link LayoutParams} instance.
-     *
-     * @return a Result with {@link Status#SUCCESS} or
-     *     {@link Status#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support
-     *     adding views.
-     */
-    private Result moveView(ViewGroup previousParent, final ViewGroup newParent,
-            final View movedView, final int index, final LayoutParams params) {
-        try {
-            // check if there is a transition on the previousParent.
-            LayoutTransition previousTransition = previousParent.getLayoutTransition();
-            if (previousTransition != null) {
-                // in this case there is an animation. This means we have to wait for the child's
-                // parent reference to be null'ed out so that we can add it to the new parent.
-                // It is technically removed right before the DISAPPEARING animation is done (if
-                // the animation of this type is not null, otherwise it's after which is impossible
-                // to handle).
-                // Because there is no move animation, if the new parent is the same as the old
-                // parent, we need to wait until the CHANGE_DISAPPEARING animation is done before
-                // adding the child or the child will appear in its new location before the
-                // other children have made room for it.
-
-                // add a listener to the transition to be notified of the actual removal.
-                previousTransition.addTransitionListener(new TransitionListener() {
-                    private int mChangeDisappearingCount = 0;
-
-                    @Override
-                    public void startTransition(LayoutTransition transition, ViewGroup container,
-                            View view, int transitionType) {
-                        if (transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
-                            mChangeDisappearingCount++;
-                        }
-                    }
-
-                    @Override
-                    public void endTransition(LayoutTransition transition, ViewGroup container,
-                            View view, int transitionType) {
-                        if (transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
-                            mChangeDisappearingCount--;
-                        }
-
-                        if (transitionType == LayoutTransition.CHANGE_DISAPPEARING &&
-                                mChangeDisappearingCount == 0) {
-                            // add it to the parentView in the correct location
-                            if (params != null) {
-                                newParent.addView(movedView, index, params);
-                            } else {
-                                newParent.addView(movedView, index);
-                            }
-                        }
-                    }
-                });
-
-                // remove the view from the current parent.
-                previousParent.removeView(movedView);
-
-                // and return since adding the view to the new parent is done in the listener.
-                return SUCCESS.createResult();
-            } else {
-                // standard code with no animation. pretty simple.
-                previousParent.removeView(movedView);
-
-                // add it to the parentView in the correct location
-                if (params != null) {
-                    newParent.addView(movedView, index, params);
-                } else {
-                    newParent.addView(movedView, index);
-                }
-
-                return SUCCESS.createResult();
-            }
-        } catch (UnsupportedOperationException e) {
-            // looks like this is a view class that doesn't support children manipulation!
-            return ERROR_VIEWGROUP_NO_CHILDREN.createResult();
-        }
-    }
-
-    /**
-     * Removes a child from its current parent.
-     * <p>
-     * {@link #acquire(long)} must have been called before this.
-     *
-     * @throws IllegalStateException if the current context is different than the one owned by
-     *      the scene, or if {@link #acquire(long)} was not called.
-     *
-     * @see RenderSession#removeChild(Object, IAnimationListener)
-     */
-    public Result removeChild(final View childView, IAnimationListener listener) {
-        checkLock();
-
-        invalidateRenderingSize();
-
-        final ViewGroup parent = (ViewGroup) childView.getParent();
-
-        if (listener != null) {
-            new AnimationThread(this, "moveChild", listener) {
-
-                @Override
-                public Result preAnimation() {
-                    parent.setLayoutTransition(new LayoutTransition());
-                    return removeView(parent, childView);
-                }
-
-                @Override
-                public void postAnimation() {
-                    parent.setLayoutTransition(null);
-                }
-            }.start();
-
-            // always return success since the real status will come through the listener.
-            return SUCCESS.createResult();
-        }
-
-        Result result = removeView(parent, childView);
-        if (!result.isSuccess()) {
-            return result;
-        }
-
-        return render(false /*freshRender*/);
-    }
-
-    /**
-     * Removes a given view from its current parent.
-     *
-     * @param view the view to remove from its parent
-     *
-     * @return a Result with {@link Status#SUCCESS} or
-     *     {@link Status#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support
-     *     adding views.
-     */
-    private Result removeView(ViewGroup parent, View view) {
-        try {
-            parent.removeView(view);
-            return SUCCESS.createResult();
-        } catch (UnsupportedOperationException e) {
-            // looks like this is a view class that doesn't support children manipulation!
-            return ERROR_VIEWGROUP_NO_CHILDREN.createResult();
-        }
-    }
-
-    /**
      * Post process on a view hierarchy that was just inflated.
      * <p/>
      * At the moment this only supports TabHost: If {@link TabHost} is detected, look for the
@@ -1240,7 +832,11 @@
 
         // this must be called before addTab() so that the TabHost searches its TabWidget
         // and FrameLayout.
-        tabHost.setup();
+        if (ReflectionUtils.isInstanceOf(tabHost, FragmentTabHostUtil.CN_FRAGMENT_TAB_HOST)) {
+            FragmentTabHostUtil.setup(tabHost, getContext());
+        } else {
+            tabHost.setup();
+        }
 
         if (count == 0) {
             // Create a dummy child to get a single tab
@@ -1539,8 +1135,8 @@
         mContentRoot = null;
 
         if (createdLooper) {
-            Bridge.cleanupThread();
             Choreographer_Delegate.dispose();
+            Bridge.cleanupThread();
         }
     }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index 2826050..c3b9aed 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -40,6 +40,7 @@
 import android.content.res.ComplexColor_Accessor;
 import android.content.res.FontResourcesParser;
 import android.content.res.GradientColor;
+import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap_Delegate;
@@ -80,55 +81,57 @@
      * @return the color as an int
      * @throws NumberFormatException if the conversion failed.
      */
-    public static int getColor(String value) {
-        if (value != null) {
-            value = value.trim();
-            if (!value.startsWith("#")) {
-                if (value.startsWith(SdkConstants.PREFIX_THEME_REF)) {
-                    throw new NumberFormatException(String.format(
-                            "Attribute '%s' not found. Are you using the right theme?", value));
-                }
-                throw new NumberFormatException(
-                        String.format("Color value '%s' must start with #", value));
-            }
-
-            value = value.substring(1);
-
-            // make sure it's not longer than 32bit
-            if (value.length() > 8) {
-                throw new NumberFormatException(String.format(
-                        "Color value '%s' is too long. Format is either" +
-                        "#AARRGGBB, #RRGGBB, #RGB, or #ARGB",
-                        value));
-            }
-
-            if (value.length() == 3) { // RGB format
-                char[] color = new char[8];
-                color[0] = color[1] = 'F';
-                color[2] = color[3] = value.charAt(0);
-                color[4] = color[5] = value.charAt(1);
-                color[6] = color[7] = value.charAt(2);
-                value = new String(color);
-            } else if (value.length() == 4) { // ARGB format
-                char[] color = new char[8];
-                color[0] = color[1] = value.charAt(0);
-                color[2] = color[3] = value.charAt(1);
-                color[4] = color[5] = value.charAt(2);
-                color[6] = color[7] = value.charAt(3);
-                value = new String(color);
-            } else if (value.length() == 6) {
-                value = "FF" + value;
-            }
-
-            // this is a RRGGBB or AARRGGBB value
-
-            // Integer.parseInt will fail to parse strings like "ff191919", so we use
-            // a Long, but cast the result back into an int, since we know that we're only
-            // dealing with 32 bit values.
-            return (int)Long.parseLong(value, 16);
+    public static int getColor(@Nullable String value) {
+        if (value == null) {
+            throw new NumberFormatException("null value");
         }
 
-        throw new NumberFormatException();
+        value = value.trim();
+        int len = value.length();
+
+        // make sure it's not longer than 32bit or smaller than the RGB format
+        if (len < 2 || len > 9) {
+            throw new NumberFormatException(String.format(
+                    "Color value '%s' has wrong size. Format is either" +
+                            "#AARRGGBB, #RRGGBB, #RGB, or #ARGB",
+                    value));
+        }
+
+        if (value.charAt(0) != '#') {
+            if (value.startsWith(SdkConstants.PREFIX_THEME_REF)) {
+                throw new NumberFormatException(String.format(
+                        "Attribute '%s' not found. Are you using the right theme?", value));
+            }
+            throw new NumberFormatException(
+                    String.format("Color value '%s' must start with #", value));
+        }
+
+        value = value.substring(1);
+
+        if (len == 4) { // RGB format
+            char[] color = new char[8];
+            color[0] = color[1] = 'F';
+            color[2] = color[3] = value.charAt(0);
+            color[4] = color[5] = value.charAt(1);
+            color[6] = color[7] = value.charAt(2);
+            value = new String(color);
+        } else if (len == 5) { // ARGB format
+            char[] color = new char[8];
+            color[0] = color[1] = value.charAt(0);
+            color[2] = color[3] = value.charAt(1);
+            color[4] = color[5] = value.charAt(2);
+            color[6] = color[7] = value.charAt(3);
+            value = new String(color);
+        } else if (len == 7) {
+            value = "FF" + value;
+        }
+
+        // this is a RRGGBB or AARRGGBB value
+
+        // Integer.parseInt will fail to parse strings like "ff191919", so we use
+        // a Long, but cast the result back into an int, since we know that we're only
+        // dealing with 32 bit values.
+        return (int)Long.parseLong(value, 16);
     }
 
     /**
@@ -149,6 +152,13 @@
             return null;
         }
 
+        // try to load the color state list from an int
+        try {
+            int color = getColor(value);
+            return ColorStateList.valueOf(color);
+        } catch (NumberFormatException ignored) {
+        }
+
         XmlPullParser parser = null;
         // first check if the value is a file (xml most likely)
         Boolean psiParserSupport = context.getLayoutlibCallback().getFlag(
@@ -216,16 +226,6 @@
 
                 return null;
             }
-        } else {
-            // try to load the color state list from an int
-            try {
-                int color = getColor(value);
-                return ColorStateList.valueOf(color);
-            } catch (NumberFormatException e) {
-                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
-                        "Failed to convert " + value + " into a ColorStateList", e,
-                        null /*data*/);
-            }
         }
 
         return null;
@@ -240,8 +240,9 @@
      */
     @Nullable
     public static ColorStateList getColorStateList(@NonNull ResourceValue resValue,
-            @NonNull BridgeContext context) {
-        return (ColorStateList) getInternalComplexColor(resValue, context, context.getTheme(),
+            @NonNull BridgeContext context, @Nullable Resources.Theme theme) {
+        return (ColorStateList) getInternalComplexColor(resValue, context,
+                theme != null ? theme : context.getTheme(),
                 false);
     }
 
@@ -254,8 +255,10 @@
      */
     @Nullable
     public static ComplexColor getComplexColor(@NonNull ResourceValue resValue,
-            @NonNull BridgeContext context) {
-        return getInternalComplexColor(resValue, context, context.getTheme(), true);
+            @NonNull BridgeContext context, @Nullable Resources.Theme theme) {
+        return getInternalComplexColor(resValue, context,
+                theme != null ? theme : context.getTheme(),
+                true);
     }
 
     /**
@@ -316,10 +319,19 @@
         }
 
         String lowerCaseValue = stringValue.toLowerCase();
+        // try the simple case first. Attempt to get a color from the value
+        try {
+            int color = getColor(stringValue);
+            return new ColorDrawable(color);
+        } catch (NumberFormatException ignore) {
+        }
 
         Density density = Density.MEDIUM;
         if (value instanceof DensityBasedResourceValue) {
             density = ((DensityBasedResourceValue) value).getResourceDensity();
+            if (density == Density.NODPI || density == Density.ANYDPI) {
+                density = Density.getEnum(context.getConfiguration().densityDpi);
+            }
         }
 
         if (lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) {
@@ -375,17 +387,6 @@
                     Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
                             "Failed lot load " + bmpFile.getAbsolutePath(), e, null /*data*/);
                 }
-            } else {
-                // attempt to get a color from the value
-                try {
-                    int color = getColor(stringValue);
-                    return new ColorDrawable(color);
-                } catch (NumberFormatException e) {
-                    // we'll return null below.
-                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
-                            "Failed to convert " + stringValue + " into a drawable", e,
-                            null /*data*/);
-                }
             }
         }
 
diff --git a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_circle.png b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_circle.png
new file mode 100644
index 0000000..3e837da
--- /dev/null
+++ 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
new file mode 100644
index 0000000..0c09075
--- /dev/null
+++ 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
new file mode 100644
index 0000000..f3fb015
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_squircle.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 bb69ca8..bca3347 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/ninepatch_background.png b/bridge/tests/res/testApp/MyApplication/golden/ninepatch_background.png
new file mode 100644
index 0000000..45a63aa
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/ninepatch_background.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
new file mode 100644
index 0000000..5fc6052
--- /dev/null
+++ 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/src/main/res/drawable-nodpi/ninepatch.9.png b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/ninepatch.9.png
new file mode 100644
index 0000000..fb3660e
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/ninepatch.9.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_line_of_path_data.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_line_of_path_data.xml
new file mode 100644
index 0000000..c5b0f01
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_line_of_path_data.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="16dp"
+        android:viewportHeight="16"
+        android:viewportWidth="16"
+        android:width="16dp">
+
+    <group
+        android:translateX="-2.000000"
+        android:translateY="-14.000000">
+        <group
+            android:translateX="2.000000"
+            android:translateY="14.000000">
+            <group
+                android:translateX="0.538462"
+                android:translateY="1.846154">
+                <path
+                    android:fillColor="#D8D8D8"
+                    android:fillType="evenOdd"
+                    android:pathData="M 7.5502538 11.69230767 C 8.90972395258 11.69230767 10.01179226 12.2433418237 10.01179226 12.9230769 C 10.01179226 13.6028119763 8.90972395258 14.15384613 7.5502538 14.15384613 C 6.19078364742 14.15384613 5.08871534 13.6028119763 5.08871534 12.9230769 C 5.08871534 12.2433418237 6.19078364742 11.69230767 7.5502538 11.69230767 Z"
+                    android:strokeWidth="1"/>
+                <!-- The ugly format below is intended for testing, please don't modify it-->
+                <path
+                    android:fillColor="#FFFF00FF"
+                    android:fillType="evenOdd"
+                    android:pathData="M9.69758952,10.4615385 L13.6123406,10.4615385 C14.2852173,10.4615385
+
+14.8461538,9.9121408 14.8461538,9.23442444 L14.8461538,1.84249863
+C14.8461538,1.17296776 14.2937568,0.615384615      13.6123406,0.615384615
+L1.31073636,0.615384615 C0.637859638,0.615384615 0.0769230769,1.16478227
+  0.0769230769,1.84249863 L0.0769230769,9.23442444 C0.0769230769,9.90395531
+
+0.629320101,10.4615385 1.31073636,10.4615385 L5.1043961,10.4615385
+C5.12507441,10.5113515 5.16340706,10.5657803 5.2204207,10.622794
+		L6.96901704,12.3713903 C7.21238046,12.6147537	7.59317013,12.6094967
+7.83127651,12.3713903 L9.57987285,10.622794	C9.63729929,10.5653675
+9.676186,10.5110368 9.69758952,10.4615385 Z"
+                    android:strokeWidth="1"
+                />
+            </group>
+            <path
+                android:fillType="evenOdd"
+                android:pathData="M4.92307692,1.229485 C4.92307692,0.550459184 5.46918972,0 6.15360922,0
+L9.84639078,0 C10.525995,0 11.0769231,0.544812716 11.0769231,1.229485
+L11.0769231,3.69230769 L4.92307692,3.69230769 L4.92307692,1.229485 Z"
+                android:strokeColor="#FFFF00FF"
+                android:strokeWidth="2.46153846"/>
+            <path
+                android:fillType="evenOdd"
+                android:pathData="M4.92307692,1.229485 C4.92307692,0.550459184 5.46918972,0 6.15360922,0
+L9.84639078,0 C10.525995,0 11.0769231,0.544812716 11.0769231,1.229485
+L11.0769231,3.69230769 L4.92307692,3.69230769 L4.92307692,1.229485 Z"
+                android:strokeColor="#FFFF00FF"
+                android:strokeWidth="2.46153846"/>
+            <path
+                android:fillColor="#FFFFFF"
+                android:fillType="evenOdd"
+                android:pathData="M 4.307692305 4.92307692 L 11.692307695 4.92307692 Q 12.30769231 4.92307692 12.30769231 5.538461535 L 12.30769231 5.538461535 Q 12.30769231 6.15384615 11.692307695 6.15384615 L 4.307692305 6.15384615 Q 3.69230769 6.15384615 3.69230769 5.538461535 L 3.69230769 5.538461535 Q 3.69230769 4.92307692 4.307692305 4.92307692 Z"
+                android:strokeWidth="1"/>
+        </group>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java b/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
index d8937f4..46e3778 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
@@ -173,6 +173,11 @@
                 continue;
             }
 
+            // constructor_after methods are called by the constructor of the original class
+            if ("constructor_after".equals(delegateMethod.getName())) {
+                continue;
+            }
+
             if (!checkedDelegateMethods.contains(delegateMethod)) {
                 mErrors.add(String.format(
                         "Delegate method %1$s.%2$s is not used anymore and must be removed",
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/impl/RenderActionTestUtil.java b/bridge/tests/src/com/android/layoutlib/bridge/impl/RenderActionTestUtil.java
new file mode 100644
index 0000000..67f1b7e
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/impl/RenderActionTestUtil.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.impl;
+
+import com.android.layoutlib.bridge.android.BridgeContext;
+
+import android.annotation.Nullable;
+
+public class RenderActionTestUtil {
+    @Nullable
+    public static BridgeContext setBridgeContext(@Nullable BridgeContext context) {
+        BridgeContext oldContext = RenderAction.sCurrentContext;
+        RenderAction.sCurrentContext = context;
+        return oldContext;
+    }
+}
\ No newline at end of file
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/impl/ResourceHelperTest.java b/bridge/tests/src/com/android/layoutlib/bridge/impl/ResourceHelperTest.java
new file mode 100644
index 0000000..4e18f07
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/impl/ResourceHelperTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.impl;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class ResourceHelperTest {
+    private static void assertNumberFormatException(Runnable runnable) {
+        try {
+            runnable.run();
+            fail("NumberFormatException expected");
+        } catch (NumberFormatException ignored) {
+        }
+    }
+
+    @Test
+    public void testGetColor() {
+        assertNumberFormatException(() -> ResourceHelper.getColor(""));
+        assertNumberFormatException(() -> ResourceHelper.getColor("AFAFAF"));
+        assertNumberFormatException(() -> ResourceHelper.getColor("AAA"));
+        assertNumberFormatException(() -> ResourceHelper.getColor("#JFAFAF"));
+        assertNumberFormatException(() -> ResourceHelper.getColor("#AABBCCDDEE"));
+        assertNumberFormatException(() -> ResourceHelper.getColor("#JAAA"));
+        assertNumberFormatException(() -> ResourceHelper.getColor("#AA BBCC"));
+
+        assertEquals(0xffaaaaaa, ResourceHelper.getColor("#AAA"));
+        assertEquals(0xffaaaaaa, ResourceHelper.getColor("  #AAA"));
+        assertEquals(0xffaaaaaa, ResourceHelper.getColor("#AAA   "));
+        assertEquals(0xffaaaaaa, ResourceHelper.getColor("  #AAA   "));
+        assertEquals(0xaaaaaa, ResourceHelper.getColor("#0AAA"));
+        assertEquals(0xffaabbcc, ResourceHelper.getColor("#AABBCC"));
+        assertEquals(0x12aabbcc, ResourceHelper.getColor("#12AABBCC"));
+        assertEquals(0x12345, ResourceHelper.getColor("#12345"));
+    }
+}
\ No newline at end of file
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index eb264d6..e98c789 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -20,6 +20,7 @@
 import com.android.layoutlib.bridge.TestDelegates;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParserTest;
 import com.android.layoutlib.bridge.impl.LayoutParserWrapperTest;
+import com.android.layoutlib.bridge.impl.ResourceHelperTest;
 
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
@@ -36,7 +37,7 @@
         RenderTests.class, LayoutParserWrapperTest.class,
         BridgeXmlBlockParserTest.class, BridgeXmlPullAttributesTest.class,
         Matrix_DelegateTest.class, TestDelegates.class, PerformanceTests.class,
-        BridgeRenderSessionTest.class
+        BridgeRenderSessionTest.class, ResourceHelperTest.class
 })
 public class Main {
 }
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 208c2d7..b09184b 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
@@ -17,30 +17,45 @@
 package com.android.layoutlib.bridge.intensive;
 
 import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.SessionParams;
 import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
 import com.android.ide.common.rendering.api.ViewInfo;
 import com.android.internal.R;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.RenderParamsFlags;
+import com.android.layoutlib.bridge.impl.ParserFactory;
 import com.android.layoutlib.bridge.impl.RenderAction;
+import com.android.layoutlib.bridge.impl.RenderActionTestUtil;
+import com.android.layoutlib.bridge.impl.ResourceHelper;
 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.resources.Density;
 import com.android.resources.Navigation;
 import com.android.resources.ResourceType;
+import com.android.resources.ResourceUrl;
 
 import org.junit.Test;
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.res.AssetManager;
+import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.Resources_Delegate;
+import android.graphics.Color;
 import android.util.DisplayMetrics;
+import android.util.StateSet;
 import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
 
+import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
 import java.lang.reflect.Field;
 import java.util.concurrent.TimeUnit;
 
@@ -348,6 +363,37 @@
         renderAndVerify(params, "vector_drawable_91383.png", TimeUnit.SECONDS.toNanos(2));
     }
 
+    /**
+     * Test a vector drawable that uses trimStart and trimEnd. It also tests all the primitives
+     * for vector drawables (lines, moves and cubic and quadratic curves).
+     */
+    @Test
+    public void testVectorDrawableHasMultipleLineInPathData() throws ClassNotFoundException {
+        // Create the layout pull parser.
+        LayoutPullParser parser = LayoutPullParser.createFromString(
+                "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "              android:padding=\"16dp\"\n" +
+                        "              android:orientation=\"horizontal\"\n" +
+                        "              android:layout_width=\"match_parent\"\n" +
+                        "              android:layout_height=\"match_parent\">\n" +
+                        "    <ImageView\n" +
+                        "             android:layout_height=\"match_parent\"\n" +
+                        "             android:layout_width=\"match_parent\"\n" +
+                        "             android:src=\"@drawable/multi_line_of_path_data\" />\n\n" +
+                        "</LinearLayout>");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+                layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
+                RenderingMode.V_SCROLL, 22);
+
+        renderAndVerify(params, "vector_drawable_multi_line_of_path_data.png",
+                TimeUnit.SECONDS.toNanos(2));
+    }
+
     /** Test activity.xml */
     @Test
     public void testScrollingAndMeasure() throws ClassNotFoundException, FileNotFoundException {
@@ -481,7 +527,7 @@
     @Test
     public void testAdaptiveIcon() throws ClassNotFoundException, FileNotFoundException {
         // Create the layout pull parser.
-        LayoutPullParser parser = LayoutPullParser.createFromString(
+        String layout =
                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
                         "              android:padding=\"16dp\"\n" +
                         "              android:orientation=\"horizontal\"\n" +
@@ -491,7 +537,8 @@
                         "             android:layout_height=\"wrap_content\"\n" +
                         "             android:layout_width=\"wrap_content\"\n" +
                         "             android:src=\"@drawable/adaptive\" />\n" +
-                        "</LinearLayout>\n");
+                        "</LinearLayout>\n";
+        LayoutPullParser parser = LayoutPullParser.createFromString(layout);
         // Create LayoutLibCallback.
         LayoutLibTestCallback layoutLibCallback =
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
@@ -502,6 +549,32 @@
                 RenderingMode.V_SCROLL, 22);
 
         renderAndVerify(params, "adaptive_icon.png");
+
+        layoutLibCallback.setAdaptiveIconMaskPath(
+                "M50 0C77.6 0 100 22.4 100 50C100 77.6 77.6 100 50 100C22.4 100 0 77.6 0 50C0 " +
+                        "22.4 22.4 0 50 0Z");
+        params =
+                getSessionParams(LayoutPullParser.createFromString(layout), ConfigGenerator.NEXUS_5,
+                        layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
+                        RenderingMode.V_SCROLL, 22);
+        renderAndVerify(params, "adaptive_icon_circle.png");
+
+        layoutLibCallback.setAdaptiveIconMaskPath(
+                "M50,0L92,0C96.42,0 100,4.58 100 8L100,92C100, 96.42 96.42 100 92 100L8 100C4.58," +
+                        " 100 0 96.42 0 92L0 8 C 0 4.42 4.42 0 8 0L50 0Z");
+        params =
+                getSessionParams(LayoutPullParser.createFromString(layout), ConfigGenerator.NEXUS_5,
+                        layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
+                        RenderingMode.V_SCROLL, 22);
+        renderAndVerify(params, "adaptive_icon_rounded_corners.png");
+
+        layoutLibCallback.setAdaptiveIconMaskPath(
+                "M50,0 C10,0 0,10 0,50 0,90 10,100 50,100 90,100 100,90 100,50 100,10 90,0 50,0 Z");
+        params =
+                getSessionParams(LayoutPullParser.createFromString(layout), ConfigGenerator.NEXUS_5,
+                        layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
+                        RenderingMode.V_SCROLL, 22);
+        renderAndVerify(params, "adaptive_icon_squircle.png");
     }
 
     @Test
@@ -532,6 +605,84 @@
     }
 
     @Test
+    public void testColorStateList() throws Exception {
+        final String STATE_LIST = "<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n" +
+                "    <item android:state_pressed=\"true\"\n" +
+                "          android:color=\"?android:attr/colorForeground\"/> \n" +
+                "    <item android:state_focused=\"true\"\n" +
+                "          android:color=\"?android:attr/colorBackground\"/> \n" +
+                "    <item android:color=\"#a000\"/> <!-- default -->\n" + "</selector>";
+
+        File tmpColorList = File.createTempFile("statelist", "xml");
+        try(PrintWriter output = new PrintWriter(new FileOutputStream(tmpColorList))) {
+            output.println(STATE_LIST);
+        }
+
+        // Setup
+        // Create the layout pull parser for our resources (empty.xml can not be part of the test
+        // app as it won't compile).
+        ParserFactory.setParserFactory(new com.android.ide.common.rendering.api.ParserFactory() {
+            @Override
+            public XmlPullParser createParser(String debugName) throws XmlPullParserException {
+                return new KXmlParser();
+            }
+        });
+
+        LayoutPullParser parser = LayoutPullParser.createFromPath("/empty.xml");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(RenderTestBase.getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_4,
+                layoutLibCallback, "Theme.Material", false, RenderingMode.NORMAL, 22);
+        DisplayMetrics metrics = new DisplayMetrics();
+        Configuration configuration = RenderAction.getConfiguration(params);
+
+        BridgeContext mContext =
+                new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
+                        params.getAssets(), params.getLayoutlibCallback(), configuration,
+                        params.getTargetSdkVersion(), params.isRtlSupported());
+        mContext.initResources();
+        BridgeContext oldContext = RenderActionTestUtil.setBridgeContext(mContext);
+
+        try {
+            ColorStateList stateList = ResourceHelper.getColorStateList(
+                    new ResourceValue(ResourceUrl.create(null, ResourceType.COLOR, "test_list"),
+                            tmpColorList.getAbsolutePath()), mContext, null);
+            assertNotNull(stateList);
+            assertEquals(Color.parseColor("#ffffffff"), stateList.getColorForState(
+                    StateSet.get(StateSet.VIEW_STATE_PRESSED),
+                    0
+            ));
+            assertEquals(Color.parseColor("#ff303030"), stateList.getColorForState(
+                    StateSet.get(StateSet.VIEW_STATE_FOCUSED),
+                    0
+            ));
+            assertEquals(Color.parseColor("#AA000000"), stateList.getDefaultColor());
+
+            // Now apply theme overlay and check the colors changed
+            Resources.Theme theme = mContext.getResources().newTheme();
+            theme.applyStyle(R.style.ThemeOverlay_Material_Light, true);
+            stateList = ResourceHelper.getColorStateList(
+                    new ResourceValue(ResourceUrl.create(null, ResourceType.COLOR, "test_list"),
+                            tmpColorList.getAbsolutePath()), mContext, theme);
+            assertNotNull(stateList);
+            assertEquals(Color.parseColor("#ff000000"), stateList.getColorForState(
+                    StateSet.get(StateSet.VIEW_STATE_PRESSED),
+                    0
+            ));
+            assertEquals(Color.parseColor("#fffafafa"), stateList.getColorForState(
+                    StateSet.get(StateSet.VIEW_STATE_FOCUSED),
+                    0
+            ));
+            assertEquals(Color.parseColor("#AA000000"), stateList.getDefaultColor());
+        } finally {
+            RenderActionTestUtil.setBridgeContext(oldContext);
+        }
+        mContext.disposeResources();
+    }
+
+    @Test
     public void testRectangleShadow() throws Exception {
         renderAndVerify("shadows_test.xml", "shadows_test.png");
     }
@@ -573,4 +724,42 @@
 
         context.disposeResources();
     }
+
+    /**
+     * If a 9patch image was in the nodpi or anydpi folder, the density of the image was 0 resulting
+     * in a float division by 0 and thus an infinite padding
+     * when layoutlib tries to scale the padding of the 9patch.
+     *
+     * http://b/37136109
+     */
+    @Test
+    public void test9PatchNoDPIBackground() throws Exception {
+        String layout =
+                "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "    android:layout_width=\"match_parent\"\n" +
+                        "    android:layout_height=\"match_parent\"\n" +
+                        "    android:background=\"@drawable/ninepatch\"\n" +
+                        "    android:layout_margin=\"20dp\"\n" +
+                        "    android:orientation=\"vertical\">\n\n" +
+                        "    <Button\n" +
+                        "        android:layout_width=\"wrap_content\"\n" +
+                        "        android:layout_height=\"wrap_content\"\n" +
+                        "        android:text=\"Button\" />\n\n" +
+                        "    <Button\n" +
+                        "        android:layout_width=\"wrap_content\"\n" +
+                        "        android:layout_height=\"wrap_content\"\n" +
+                        "        android:text=\"Button\" />\n"
+                        + "</LinearLayout>";
+
+        LayoutPullParser parser = LayoutPullParser.createFromString(layout);
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5, layoutLibCallback,
+                "Theme.Material.NoActionBar.Fullscreen", false, RenderingMode.V_SCROLL, 22);
+
+        renderAndVerify(params, "ninepatch_background.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 75145d7..184538e 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
@@ -43,9 +43,6 @@
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
 import java.util.Map;
 
 import com.google.android.collect.Maps;
@@ -64,6 +61,7 @@
     private final ILogger mLog;
     private final ActionBarCallback mActionBarCallback = new ActionBarCallback();
     private final ClassLoader mModuleClassLoader;
+    private String mAdaptiveIconMaskPath;
 
     public LayoutLibTestCallback(ILogger logger, ClassLoader classLoader) {
         mLog = logger;
@@ -197,6 +195,13 @@
         if (key.equals(RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE)) {
             return (T) PACKAGE_NAME;
         }
+        if (key.equals(RenderParamsFlags.FLAG_KEY_ADAPTIVE_ICON_MASK_PATH)) {
+            return (T) mAdaptiveIconMaskPath;
+        }
         return null;
     }
+
+    public void setAdaptiveIconMaskPath(String adaptiveIconMaskPath) {
+        mAdaptiveIconMaskPath = adaptiveIconMaskPath;
+    }
 }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java
index cdf5633..45facf5 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java
@@ -56,7 +56,7 @@
 
     private static final int THUMBNAIL_SIZE = 1000;
 
-    private static final double MAX_PERCENT_DIFFERENCE = 0.3;
+    private static final double MAX_PERCENT_DIFFERENCE = 0.1;
 
     public static void requireSimilar(@NonNull String relativePath, @NonNull BufferedImage image)
             throws IOException {
diff --git a/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 5951a67..f595803 100644
--- a/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -167,6 +167,7 @@
         "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#useLastSeenTarget",
         "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#onDraw",
         "android.graphics.drawable.GradientDrawable#buildRing",
+        "android.graphics.drawable.AdaptiveIconDrawable#<init>",
         "android.graphics.FontFamily#addFont",
         "android.graphics.Typeface#getSystemFontConfigLocation",
         "android.graphics.Typeface#makeFamilyFromParsed",
@@ -336,7 +337,8 @@
         "android.graphics.drawable.VectorDrawable#mVectorState",
         "android.view.Choreographer#mLastFrameTimeNanos",
         "android.graphics.FontFamily#mBuilderPtr",
-        "android.graphics.Typeface#sDynamicTypefaceCache"
+        "android.graphics.Typeface#sDynamicTypefaceCache",
+        "android.graphics.drawable.AdaptiveIconDrawable#sMask",
     };
 
     /**