Merge commit '1af55876a53bb28daa8b7533e0c934875b695f2f' into HEAD
diff --git a/common/src/main/java/com/android/SdkConstants.java b/common/src/main/java/com/android/SdkConstants.java
index 2138dcb..37510ce 100644
--- a/common/src/main/java/com/android/SdkConstants.java
+++ b/common/src/main/java/com/android/SdkConstants.java
@@ -203,6 +203,7 @@
      * filename for gdbserver.
      */
     public static final String FN_GDBSERVER = "gdbserver";              //$NON-NLS-1$
+    public static final String FN_GDB_SETUP = "gdb.setup";              //$NON-NLS-1$
 
     /** global Android proguard config file */
     public static final String FN_ANDROID_PROGUARD_FILE = "proguard-android.txt";   //$NON-NLS-1$
diff --git a/common/src/main/java/com/android/utils/HtmlBuilder.java b/common/src/main/java/com/android/utils/HtmlBuilder.java
index 3c3159e..f2e0d20 100644
--- a/common/src/main/java/com/android/utils/HtmlBuilder.java
+++ b/common/src/main/java/com/android/utils/HtmlBuilder.java
@@ -70,6 +70,14 @@
         return this;
     }
 
+    public HtmlBuilder newlineIfNecessary() {
+        if (!SdkUtils.endsWith(mStringBuilder, "<BR/>\n")) {
+            mStringBuilder.append("<BR/>\n");
+        }
+
+        return this;
+    }
+
     public HtmlBuilder addLink(@Nullable String textBefore,
             @NonNull String linkText,
             @Nullable String textAfter,
diff --git a/common/src/test/java/com/android/utils/HtmlBuilderTest.java b/common/src/test/java/com/android/utils/HtmlBuilderTest.java
index ff54db3..ab80bdd 100644
--- a/common/src/test/java/com/android/utils/HtmlBuilderTest.java
+++ b/common/src/test/java/com/android/utils/HtmlBuilderTest.java
@@ -122,4 +122,20 @@
         }
         assertEquals(expected, actual);
     }
+
+    public void testNewlineIfNecessary() {
+        HtmlBuilder builder = new HtmlBuilder();
+        builder.newlineIfNecessary();
+        assertEquals("<BR/>\n", builder.getHtml());
+        builder.newlineIfNecessary();
+        assertEquals("<BR/>\n", builder.getHtml());
+        builder.add("a");
+        builder.newlineIfNecessary();
+        assertEquals("<BR/>\na<BR/>\n", builder.getHtml());
+        builder.newline();
+        builder.newlineIfNecessary();
+        builder.newlineIfNecessary();
+        builder.newlineIfNecessary();
+        assertEquals("<BR/>\na<BR/>\n<BR/>\n", builder.getHtml());
+    }
 }
diff --git a/ddmlib/src/main/java/com/android/ddmlib/CollectingOutputReceiver.java b/ddmlib/src/main/java/com/android/ddmlib/CollectingOutputReceiver.java
index e262cf3..efe092c 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/CollectingOutputReceiver.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/CollectingOutputReceiver.java
@@ -18,6 +18,7 @@
 
 import java.io.UnsupportedEncodingException;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * A {@link IShellOutputReceiver} which collects the whole shell output into one
@@ -26,7 +27,7 @@
 public class CollectingOutputReceiver implements IShellOutputReceiver {
     private CountDownLatch mCompletionLatch;
     private StringBuffer mOutputBuffer = new StringBuffer();
-    private boolean mIsCanceled = false;
+    private AtomicBoolean mIsCanceled = new AtomicBoolean(false);
 
     public CollectingOutputReceiver() {
     }
@@ -41,20 +42,20 @@
 
     @Override
     public boolean isCancelled() {
-        return mIsCanceled;
+        return mIsCanceled.get();
     }
 
     /**
      * Cancel the output collection
      */
     public void cancel() {
-        mIsCanceled = true;
+        mIsCanceled.set(true);
     }
 
     @Override
     public void addOutput(byte[] data, int offset, int length) {
         if (!isCancelled()) {
-            String s = null;
+            String s;
             try {
                 s = new String(data, offset, length, "UTF-8"); //$NON-NLS-1$
             } catch (UnsupportedEncodingException e) {
diff --git a/ddmlib/src/main/java/com/android/ddmlib/Device.java b/ddmlib/src/main/java/com/android/ddmlib/Device.java
index 0bf7cb3..8e9bece 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/Device.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/Device.java
@@ -16,6 +16,9 @@
 
 package com.android.ddmlib;
 
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
 import com.android.annotations.concurrency.GuardedBy;
 import com.android.ddmlib.log.LogReceiver;
 
@@ -38,7 +41,6 @@
  * A Device. It can be a physical device or an emulator.
  */
 final class Device implements IDevice {
-
     private static final int INSTALL_TIMEOUT = 2*60*1000; //2min
     private static final int BATTERY_TIMEOUT = 2*1000; //2 seconds
     private static final int GETPROP_TIMEOUT = 2*1000; //2 seconds
@@ -81,6 +83,13 @@
     private Integer mLastBatteryLevel = null;
     private long mLastBatteryCheckTime = 0;
 
+    /** Path to the screen recorder binary on the device. */
+    private static final String SCREEN_RECORDER_DEVICE_PATH = "/system/bin/screenrecord";
+
+    /** Flag indicating whether the device has the screen recorder binary. */
+    private Boolean mHasScreenRecorder;
+
+    private int mApiLevel;
     private String mName;
 
     /**
@@ -175,6 +184,50 @@
         }
     }
 
+    /**
+     * Output receiver for "cat /sys/class/power_supply/.../capacity" command line.
+     */
+    static final class SysFsBatteryLevelReceiver extends MultiLineReceiver {
+
+        private static final Pattern BATTERY_LEVEL = Pattern.compile("^(\\d+)[.\\s]*");
+        private Integer mBatteryLevel = null;
+
+        /**
+         * Get the parsed battery level.
+         * @return battery level or <code>null</code> if it cannot be determined
+         */
+        @Nullable
+        public Integer getBatteryLevel() {
+            return mBatteryLevel;
+        }
+
+        @Override
+        public boolean isCancelled() {
+            return false;
+        }
+
+        @Override
+        public void processNewLines(String[] lines) {
+            for (String line : lines) {
+                Matcher batteryMatch = BATTERY_LEVEL.matcher(line);
+                if (batteryMatch.matches()) {
+                    if (mBatteryLevel == null) {
+                        mBatteryLevel = Integer.parseInt(batteryMatch.group(1));
+                    } else {
+                        // multiple matches, check if they are different
+                        Integer tmpLevel = Integer.parseInt(batteryMatch.group(1));
+                        if (!mBatteryLevel.equals(tmpLevel)) {
+                            Log.w(LOG_TAG, String.format(
+                                    "Multiple lines matched with different value; " +
+                                    "Original: %s, Current: %s (keeping original)",
+                                    mBatteryLevel.toString(), tmpLevel.toString()));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#getSerialNumber()
@@ -354,6 +407,56 @@
     }
 
     @Override
+    public boolean supportsFeature(Feature feature) {
+        switch (feature) {
+            case SCREEN_RECORD:
+                if (getApiLevel() < 19) {
+                    return false;
+                }
+                if (mHasScreenRecorder == null) {
+                    mHasScreenRecorder = hasBinary(SCREEN_RECORDER_DEVICE_PATH);
+                }
+                return mHasScreenRecorder;
+            case PROCSTATS:
+                return getApiLevel() >= 19;
+            default:
+                return false;
+        }
+    }
+
+    private int getApiLevel() {
+        if (mApiLevel > 0) {
+            return mApiLevel;
+        }
+
+        try {
+            mApiLevel = Integer.parseInt(getPropertyCacheOrSync(PROP_BUILD_API_LEVEL));
+            return mApiLevel;
+        } catch (Exception e) {
+            return -1;
+        }
+    }
+
+    private boolean hasBinary(String path) {
+        CountDownLatch latch = new CountDownLatch(1);
+        CollectingOutputReceiver receiver = new CollectingOutputReceiver(latch);
+        try {
+            executeShellCommand("ls " + path, receiver);
+        } catch (Exception e) {
+            return false;
+        }
+
+        try {
+            latch.await(GETPROP_TIMEOUT, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            return false;
+        }
+
+        String value = receiver.getOutput().trim();
+        return !value.endsWith("No such file or directory");
+    }
+
+    @Override
     public String getMountPoint(String name) {
         return mMountPoints.get(name);
     }
@@ -431,6 +534,50 @@
     }
 
     @Override
+    public void startScreenRecorder(String remoteFilePath, ScreenRecorderOptions options,
+            IShellOutputReceiver receiver) throws TimeoutException, AdbCommandRejectedException,
+            IOException, ShellCommandUnresponsiveException {
+        executeShellCommand(getScreenRecorderCommand(remoteFilePath, options), receiver, 0, null);
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    static String getScreenRecorderCommand(@NonNull String remoteFilePath,
+            @NonNull ScreenRecorderOptions options) {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("screenrecord");
+        sb.append(' ');
+
+        if (options.width > 0 && options.height > 0) {
+            sb.append("--size ");
+            sb.append(options.width);
+            sb.append('x');
+            sb.append(options.height);
+            sb.append(' ');
+        }
+
+        if (options.bitrateMbps > 0) {
+            sb.append("--bit-rate ");
+            sb.append(options.bitrateMbps * 1000000);
+            sb.append(' ');
+        }
+
+        if (options.timeLimit > 0) {
+            sb.append("--time-limit ");
+            long seconds = TimeUnit.SECONDS.convert(options.timeLimit, options.timeLimitUnits);
+            if (seconds > 180) {
+                seconds = 180;
+            }
+            sb.append(seconds);
+            sb.append(' ');
+        }
+
+        sb.append(remoteFilePath);
+
+        return sb.toString();
+    }
+
+    @Override
     public void executeShellCommand(String command, IShellOutputReceiver receiver)
             throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
             IOException {
@@ -874,6 +1021,16 @@
                 && mLastBatteryCheckTime > (System.currentTimeMillis() - freshnessMs)) {
             return mLastBatteryLevel;
         }
+        // first try to get it from sysfs
+        SysFsBatteryLevelReceiver sysBattReceiver = new SysFsBatteryLevelReceiver();
+        executeShellCommand("cat /sys/class/power_supply/*/capacity",
+                sysBattReceiver, BATTERY_TIMEOUT);
+        mLastBatteryLevel = sysBattReceiver.getBatteryLevel();
+        if (mLastBatteryLevel != null) {
+            mLastBatteryCheckTime = System.currentTimeMillis();
+            return mLastBatteryLevel;
+        }
+        // now try dumpsys
         BatteryReceiver receiver = new BatteryReceiver();
         executeShellCommand("dumpsys battery", receiver, BATTERY_TIMEOUT);
         mLastBatteryLevel = receiver.getBatteryLevel();
diff --git a/ddmlib/src/main/java/com/android/ddmlib/IDevice.java b/ddmlib/src/main/java/com/android/ddmlib/IDevice.java
index 7b70acb..c8d72ae 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/IDevice.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/IDevice.java
@@ -16,6 +16,7 @@
 
 package com.android.ddmlib;
 
+import com.android.annotations.NonNull;
 import com.android.ddmlib.log.LogReceiver;
 
 import java.io.IOException;
@@ -31,6 +32,8 @@
     public static final String PROP_BUILD_CODENAME = "ro.build.version.codename";
     public static final String PROP_DEVICE_MODEL = "ro.product.model";
     public static final String PROP_DEVICE_MANUFACTURER = "ro.product.manufacturer";
+    public static final String PROP_DEVICE_CPU_ABI = "ro.product.cpu.abi";
+    public static final String PROP_DEVICE_CPU_ABI2 = "ro.product.cpu.abi2";
 
     public static final String PROP_DEBUGGABLE = "ro.debuggable";
 
@@ -43,6 +46,12 @@
     /** Device change bit mask: build info change. */
     public static final int CHANGE_BUILD_INFO = 0x0004;
 
+    /** List of device level features. */
+    public enum Feature {
+        SCREEN_RECORD,      // screen recorder available?
+        PROCSTATS,          // procstats service (dumpsys procstats) available
+    };
+
     /** @deprecated Use {@link #PROP_BUILD_API_LEVEL}. */
     @Deprecated
     public static final String PROP_BUILD_VERSION_NUMBER = PROP_BUILD_API_LEVEL;
@@ -176,6 +185,9 @@
     public String getPropertyCacheOrSync(String name) throws TimeoutException,
             AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException;
 
+    /** Returns whether this device supports the given feature. */
+    boolean supportsFeature(@NonNull Feature feature);
+
     /**
      * Returns a mount point.
      *
@@ -262,6 +274,14 @@
             IOException;
 
     /**
+     * Initiates screen recording on the device if the device supports {@link Feature#SCREEN_RECORD}.
+     */
+    public void startScreenRecorder(@NonNull String remoteFilePath,
+            @NonNull ScreenRecorderOptions options, @NonNull IShellOutputReceiver receiver) throws
+            TimeoutException, AdbCommandRejectedException, IOException,
+            ShellCommandUnresponsiveException;
+
+    /**
      * @deprecated Use {@link #executeShellCommand(String, IShellOutputReceiver, long, java.util.concurrent.TimeUnit)}.
      */
     @Deprecated
diff --git a/ddmlib/src/main/java/com/android/ddmlib/ScreenRecorderOptions.java b/ddmlib/src/main/java/com/android/ddmlib/ScreenRecorderOptions.java
new file mode 100644
index 0000000..af8952a
--- /dev/null
+++ b/ddmlib/src/main/java/com/android/ddmlib/ScreenRecorderOptions.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ddmlib;
+
+import java.util.concurrent.TimeUnit;
+
+public class ScreenRecorderOptions {
+    // video size is given by width x height, defaults to device's main display resolution
+    // or 1280x720.
+    public final int width;
+    public final int height;
+
+    // bit rate in Mbps. Defaults to 4Mbps
+    public final int bitrateMbps;
+
+    // time limit, maximum of 3 seconds
+    public final long timeLimit;
+    public final TimeUnit timeLimitUnits;
+
+    private ScreenRecorderOptions(Builder builder) {
+        width = builder.mWidth;
+        height = builder.mHeight;
+
+        bitrateMbps = builder.mBitRate;
+
+        timeLimit = builder.mTime;
+        timeLimitUnits = builder.mTimeUnits;
+    }
+
+    public static class Builder {
+        private int mWidth;
+        private int mHeight;
+        private int mBitRate;
+        private long mTime;
+        private TimeUnit mTimeUnits;
+
+        public Builder setSize(int w, int h) {
+            mWidth = w;
+            mHeight = h;
+            return this;
+        }
+
+        public Builder setBitRate(int bitRateMbps) {
+            mBitRate = bitRateMbps;
+            return this;
+        }
+
+        public Builder setTimeLimit(long time, TimeUnit units) {
+            mTime = time;
+            mTimeUnits = units;
+            return this;
+        }
+
+        public ScreenRecorderOptions build() {
+            return new ScreenRecorderOptions(this);
+        }
+    }
+}
diff --git a/ddmlib/src/test/java/com/android/ddmlib/DeviceTest.java b/ddmlib/src/test/java/com/android/ddmlib/DeviceTest.java
new file mode 100644
index 0000000..fb55987
--- /dev/null
+++ b/ddmlib/src/test/java/com/android/ddmlib/DeviceTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ddmlib;
+
+import junit.framework.TestCase;
+
+import java.util.concurrent.TimeUnit;
+
+public class DeviceTest extends TestCase {
+    public void testScreenRecorderOptions() {
+        ScreenRecorderOptions options =
+                new ScreenRecorderOptions.Builder()
+                        .setBitRate(6)
+                        .setSize(600,400)
+                        .build();
+        assertEquals("screenrecord --size 600x400 --bit-rate 6000000 /sdcard/1.mp4",
+                Device.getScreenRecorderCommand("/sdcard/1.mp4", options));
+
+        options = new ScreenRecorderOptions.Builder().setTimeLimit(100, TimeUnit.SECONDS).build();
+        assertEquals("screenrecord --time-limit 100 /sdcard/1.mp4",
+                Device.getScreenRecorderCommand("/sdcard/1.mp4", options));
+
+        options = new ScreenRecorderOptions.Builder().setTimeLimit(4, TimeUnit.MINUTES).build();
+        assertEquals("screenrecord --time-limit 180 /sdcard/1.mp4",
+                Device.getScreenRecorderCommand("/sdcard/1.mp4", options));
+    }
+}
diff --git a/ddmlib/src/test/java/com/android/ddmlib/SysFsBatteryLevelReceiverTest.java b/ddmlib/src/test/java/com/android/ddmlib/SysFsBatteryLevelReceiverTest.java
new file mode 100644
index 0000000..70970c3
--- /dev/null
+++ b/ddmlib/src/test/java/com/android/ddmlib/SysFsBatteryLevelReceiverTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ddmlib;
+
+import com.android.ddmlib.Device.SysFsBatteryLevelReceiver;
+
+import junit.framework.TestCase;
+
+import java.util.Random;
+
+public class SysFsBatteryLevelReceiverTest extends TestCase {
+
+    private SysFsBatteryLevelReceiver mReceiver;
+    private Integer mExpected1, mExpected2;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mReceiver = new SysFsBatteryLevelReceiver();
+        Random r = new Random(System.currentTimeMillis());
+        mExpected1 = r.nextInt(101);
+        mExpected2 = r.nextInt(101);
+    }
+
+    public void testSingleLine() {
+        String[] lines = {mExpected1.toString()};
+        mReceiver.processNewLines(lines);
+        assertEquals(mExpected1, mReceiver.getBatteryLevel());
+    }
+
+    public void testWithTrailingWhitespace1() {
+        String[] lines = {mExpected1 + " "};
+        mReceiver.processNewLines(lines);
+        assertEquals(mExpected1, mReceiver.getBatteryLevel());
+    }
+
+    public void testWithTrailingWhitespace2() {
+        String[] lines = {mExpected1 + "\n"};
+        mReceiver.processNewLines(lines);
+        assertEquals(mExpected1, mReceiver.getBatteryLevel());
+    }
+
+    public void testWithTrailingWhitespace3() {
+        String[] lines = {mExpected1 + "\r"};
+        mReceiver.processNewLines(lines);
+        assertEquals(mExpected1, mReceiver.getBatteryLevel());
+    }
+
+    public void testWithTrailingWhitespace4() {
+        String[] lines = {mExpected1 + "\r\n"};
+        mReceiver.processNewLines(lines);
+        assertEquals(mExpected1, mReceiver.getBatteryLevel());
+    }
+
+    public void testMultipleLinesSame() {
+        String[] lines = {mExpected1 + "\n", mExpected2.toString()};
+        mReceiver.processNewLines(lines);
+        assertEquals(mExpected1, mReceiver.getBatteryLevel());
+    }
+
+    public void testMultipleLinesDifferent() {
+        String[] lines = {mExpected1 + "\n", mExpected2.toString()};
+        mReceiver.processNewLines(lines);
+        assertEquals(mExpected1, mReceiver.getBatteryLevel());
+    }
+
+    public void testInvalid() {
+        String[] lines = {"foo\n", "bar", "yadda"};
+        mReceiver.processNewLines(lines);
+        assertNull(mReceiver.getBatteryLevel());
+    }
+}
diff --git a/device_validator/dvlib/src/main/resources/com/android/dvlib/devices.xsd b/device_validator/dvlib/src/main/resources/com/android/dvlib/devices.xsd
index abb1443..34d197a 100644
--- a/device_validator/dvlib/src/main/resources/com/android/dvlib/devices.xsd
+++ b/device_validator/dvlib/src/main/resources/com/android/dvlib/devices.xsd
@@ -465,6 +465,7 @@
                         <xsd:enumeration value="hdpi" />
                         <xsd:enumeration value="xhdpi" />
                         <xsd:enumeration value="xxhdpi" />
+                        <xsd:enumeration value="xxxhdpi" />
                     </xsd:restriction>
                 </xsd:simpleType>
             </xsd:element>
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index b0e28e1..71afaf0 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=../../../external/gradle/gradle-1.7-bin.zip
+distributionUrl=../../../external/gradle/gradle-1.9-bin.zip
diff --git a/layoutlib-api/src/main/java/com/android/resources/FolderTypeRelationship.java b/layoutlib-api/src/main/java/com/android/resources/FolderTypeRelationship.java
index d174940..a951056 100644
--- a/layoutlib-api/src/main/java/com/android/resources/FolderTypeRelationship.java
+++ b/layoutlib-api/src/main/java/com/android/resources/FolderTypeRelationship.java
@@ -63,6 +63,7 @@
         add(ResourceType.STRING, ResourceFolderType.VALUES);
         add(ResourceType.STYLE, ResourceFolderType.VALUES);
         add(ResourceType.STYLEABLE, ResourceFolderType.VALUES);
+        add(ResourceType.TRANSITION, ResourceFolderType.TRANSITION);
         add(ResourceType.XML, ResourceFolderType.XML);
 
         makeSafe();
diff --git a/layoutlib-api/src/main/java/com/android/resources/ResourceConstants.java b/layoutlib-api/src/main/java/com/android/resources/ResourceConstants.java
index 4e92e3c..7d73f70 100644
--- a/layoutlib-api/src/main/java/com/android/resources/ResourceConstants.java
+++ b/layoutlib-api/src/main/java/com/android/resources/ResourceConstants.java
@@ -43,6 +43,8 @@
     public static final String FD_RES_XML = "xml"; //$NON-NLS-1$
     /** Default raw resource folder name, i.e. "raw" */
     public static final String FD_RES_RAW = "raw"; //$NON-NLS-1$
+    /** Default transition resource folder name, i.e. "transition" */
+    public static final String FD_RES_TRANSITION = "transition"; //$NON-NLS-1$
 
     /** Separator between the resource folder qualifier. */
     public static final String RES_QUALIFIER_SEP = "-"; //$NON-NLS-1$
diff --git a/layoutlib-api/src/main/java/com/android/resources/ResourceFolderType.java b/layoutlib-api/src/main/java/com/android/resources/ResourceFolderType.java
index 5a271cf..0e78697 100644
--- a/layoutlib-api/src/main/java/com/android/resources/ResourceFolderType.java
+++ b/layoutlib-api/src/main/java/com/android/resources/ResourceFolderType.java
@@ -29,6 +29,7 @@
     MENU(ResourceConstants.FD_RES_MENU),
     MIPMAP(ResourceConstants.FD_RES_MIPMAP),
     RAW(ResourceConstants.FD_RES_RAW),
+    TRANSITION(ResourceConstants.FD_RES_TRANSITION),
     VALUES(ResourceConstants.FD_RES_VALUES),
     XML(ResourceConstants.FD_RES_XML);
 
diff --git a/layoutlib-api/src/main/java/com/android/resources/ResourceType.java b/layoutlib-api/src/main/java/com/android/resources/ResourceType.java
index f02152a..891c65b 100644
--- a/layoutlib-api/src/main/java/com/android/resources/ResourceType.java
+++ b/layoutlib-api/src/main/java/com/android/resources/ResourceType.java
@@ -42,6 +42,7 @@
     STRING("string", "String"), //$NON-NLS-1$
     STYLE("style", "Style"), //$NON-NLS-1$
     STYLEABLE("styleable", "Styleable"), //$NON-NLS-1$
+    TRANSITION("transition", "Transition"), //$NON-NLS-1$
     XML("xml", "XML"), //$NON-NLS-1$
     // this is not actually used. Only there because they get parsed and since we want to
     // detect new resource type, we need to have this one exist.
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/ComputeDependencyTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/ComputeDependencyTask.java
index 33a1fa7..7b974d4 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/ComputeDependencyTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/ComputeDependencyTask.java
@@ -142,7 +142,7 @@
             throw new BuildException("Missing attribute buildToolsFolder");
         }
         if (mRenderscriptSupportLibsOut == null) {
-            throw new BuildException("Missing attribute renderscriptSupportOutOut");
+            throw new BuildException("Missing attribute renderscriptSupportLibsOut");
         }
 
 
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/CheckPermissionDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/CheckPermissionDetectorTest.java
new file mode 100644
index 0000000..caca6a5
--- /dev/null
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/CheckPermissionDetectorTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+public class CheckPermissionDetectorTest extends AbstractCheckTest {
+  @Override
+  protected Detector getDetector() {
+    return new CheckPermissionDetector();
+  }
+
+    public void test() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/CheckPermissions.java:7: Warning: The result of checkCallingOrSelfPermission is not used; did you mean to call enforceCallingOrSelfPermission? [UseCheckPermission]\n"
+                + "      context.checkCallingOrSelfPermission(Manifest.permission.INTERNET); // WRONG\n"
+                + "      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "0 errors, 1 warnings\n",
+
+                lintProject("src/test/pkg/CheckPermissions.java.txt=>" +
+                        "src/test/pkg/CheckPermissions.java"));
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SecureRandomGeneratorDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/SecureRandomGeneratorDetectorTest.java
new file mode 100644
index 0000000..3b7e1fd
--- /dev/null
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/SecureRandomGeneratorDetectorTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+@SuppressWarnings("SpellCheckingInspection")
+public class SecureRandomGeneratorDetectorTest extends AbstractCheckTest {
+
+    @Override
+    protected Detector getDetector() {
+        return new SecureRandomGeneratorDetector();
+    }
+
+    public void testWithoutWorkaround() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/PrngCalls.java:13: Warning: Potentially insecure random numbers "
+                + "on Android 4.3 and older. Read "
+                + "https://android-developers.blogspot.com/2013/08/some-securerandom-thoughts.html"
+                + " for more info. [TrulyRandom]\n"
+                + "        KeyGenerator generator = KeyGenerator.getInstance(\"AES\", \"BC\");\n"
+                + "                                              ~~~~~~~~~~~\n"
+                + "0 errors, 1 warnings\n",
+            lintProject(
+                "bytecode/.classpath=>.classpath",
+                "bytecode/AndroidManifest.xml=>AndroidManifest.xml",
+                "bytecode/PrngCalls.java.txt=>src/test/pkg/PrngCalls.java",
+                "bytecode/PrngCalls.class.data=>bin/classes/test/pkg/PrngCalls.class"
+            ));
+    }
+
+    public void testWithWorkaround() throws Exception {
+        assertEquals(
+            "No warnings.",
+            lintProject(
+                "bytecode/.classpath=>.classpath",
+                "bytecode/AndroidManifest.xml=>AndroidManifest.xml",
+                "bytecode/PrngCalls.java.txt=>src/test/pkg/PrngCalls.java",
+                "bytecode/PrngCalls.class.data=>bin/classes/test/pkg/PrngCalls.class",
+                "bytecode/PrngWorkaround$LinuxPRNGSecureRandom.class.data=>bin/classes/test/pkg/PrngWorkaround$LinuxPRNGSecureRandom.class",
+                "bytecode/PrngWorkaround$LinuxPRNGSecureRandomProvider.class.data=>bin/classes/test/pkg/PrngWorkaround$LinuxPRNGSecureRandomProvider.class",
+                "bytecode/PrngWorkaround.class.data=>bin/classes/test/pkg/PrngWorkaround.class"
+            ));
+    }
+
+    public void testCipherInit() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/CipherTest1.java:11: Warning: Potentially insecure random "
+                + "numbers on Android 4.3 and older. Read "
+                + "https://android-developers.blogspot.com/2013/08/some-securerandom-thoughts.html"
+                + " for more info. [TrulyRandom]\n"
+                + "        cipher.init(Cipher.WRAP_MODE, key); // FLAG\n"
+                + "               ~~~~\n"
+                + "0 errors, 1 warnings\n",
+                lintProject(
+                        "bytecode/.classpath=>.classpath",
+                        "bytecode/AndroidManifest.xml=>AndroidManifest.xml",
+                        "bytecode/CipherTest1.java.txt=>src/test/pkg/CipherTest1.java",
+                        "bytecode/CipherTest1.class.data=>bin/classes/test/pkg/CipherTest1.class"
+                ));
+    }
+
+    public void testGetArity()  {
+        assertEquals(2, SecureRandomGeneratorDetector.getDescArity("(ILjava/security/Key;)V"));
+        assertEquals(0, SecureRandomGeneratorDetector.getDescArity("()V"));
+        assertEquals(1, SecureRandomGeneratorDetector.getDescArity("(I)V"));
+        assertEquals(3, SecureRandomGeneratorDetector.getDescArity(
+                "(Ljava/lang/String;Ljava/lang/String;I)V"));
+        assertEquals(0, SecureRandomGeneratorDetector.getDescArity("()Lfoo/bar/Baz;"));
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.class.data b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.class.data
new file mode 100644
index 0000000..73cd72f
--- /dev/null
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.java.txt b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.java.txt
new file mode 100644
index 0000000..200de79
--- /dev/null
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.java.txt
@@ -0,0 +1,21 @@
+package test.pkg;
+
+import java.security.Key;
+import java.security.SecureRandom;
+
+import javax.crypto.Cipher;
+
+@SuppressWarnings("all")
+public class CipherTest1 {
+    public void test1(Cipher cipher, Key key) throws Exception {
+        cipher.init(Cipher.WRAP_MODE, key); // FLAG
+    }
+
+    public void test2(Cipher cipher, Key key, SecureRandom random) throws Exception {
+        cipher.init(Cipher.ENCRYPT_MODE, key, random);
+    }
+
+    public void setup(String transform) throws Exception {
+        Cipher cipher = Cipher.getInstance(transform);
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.class.data b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.class.data
new file mode 100644
index 0000000..fb46952
--- /dev/null
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.java.txt b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.java.txt
new file mode 100644
index 0000000..5414ec8
--- /dev/null
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.java.txt
@@ -0,0 +1,26 @@
+package test.pkg;
+
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+
+import javax.crypto.KeyAgreement;
+import javax.crypto.KeyGenerator;
+
+public class PrngCalls {
+    public void testKeyGenerator() throws NoSuchAlgorithmException, NoSuchProviderException {
+        KeyGenerator generator = KeyGenerator.getInstance("AES", "BC");
+        generator.init(128);
+
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+        keyGen.initialize(512);
+
+        KeyAgreement agreement = KeyAgreement.getInstance("DH", "BC");
+        agreement.generateSecret();
+
+        SecureRandom random = new SecureRandom();
+        byte bytes[] = new byte[20];
+        random.nextBytes(bytes);
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandom.class.data b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandom.class.data
new file mode 100644
index 0000000..1741ccc
--- /dev/null
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandom.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandomProvider.class.data b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandomProvider.class.data
new file mode 100644
index 0000000..b1e6d3b
--- /dev/null
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandomProvider.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.class.data b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.class.data
new file mode 100644
index 0000000..32a785a
--- /dev/null
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.java.txt b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.java.txt
new file mode 100644
index 0000000..a63e267
--- /dev/null
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.java.txt
@@ -0,0 +1,336 @@
+/*
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will Google be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, as long as the origin is not misrepresented.
+ */
+
+package test.pkg;
+
+import android.os.Build;
+import android.os.Process;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.SecureRandomSpi;
+import java.security.Security;
+
+/**
+ * Fixes for the output of the default PRNG having low entropy.
+ *
+ * The fixes need to be applied via {@link #apply()} before any use of Java
+ * Cryptography Architecture primitives. A good place to invoke them is in the
+ * application's {@code onCreate}.
+ */
+public final class PrngWorkaround {
+
+    private static final int VERSION_CODE_JELLY_BEAN = 16;
+    private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18;
+    private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL =
+        getBuildFingerprintAndDeviceSerial();
+
+    /** Hidden constructor to prevent instantiation. */
+    private PrngWorkaround() {}
+
+    /**
+     * Applies all fixes.
+     *
+     * @throws SecurityException if a fix is needed but could not be applied.
+     */
+    public static void apply() {
+        applyOpenSSLFix();
+        installLinuxPRNGSecureRandom();
+    }
+
+    /**
+     * Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the
+     * fix is not needed.
+     *
+     * @throws SecurityException if the fix is needed but could not be applied.
+     */
+    private static void applyOpenSSLFix() throws SecurityException {
+        if ((Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN)
+                || (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2)) {
+            // No need to apply the fix
+            return;
+        }
+
+        try {
+            // Mix in the device- and invocation-specific seed.
+            Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
+                    .getMethod("RAND_seed", byte[].class)
+                    .invoke(null, generateSeed());
+
+            // Mix output of Linux PRNG into OpenSSL's PRNG
+            int bytesRead = (Integer) Class.forName(
+                    "org.apache.harmony.xnet.provider.jsse.NativeCrypto")
+                    .getMethod("RAND_load_file", String.class, long.class)
+                    .invoke(null, "/dev/urandom", 1024);
+            if (bytesRead != 1024) {
+                throw new IOException(
+                        "Unexpected number of bytes read from Linux PRNG: "
+                                + bytesRead);
+            }
+        } catch (Exception e) {
+            throw new SecurityException("Failed to seed OpenSSL PRNG", e);
+        }
+    }
+
+    /**
+     * Installs a Linux PRNG-backed {@code SecureRandom} implementation as the
+     * default. Does nothing if the implementation is already the default or if
+     * there is not need to install the implementation.
+     *
+     * @throws SecurityException if the fix is needed but could not be applied.
+     */
+    private static void installLinuxPRNGSecureRandom()
+            throws SecurityException {
+        if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) {
+            // No need to apply the fix
+            return;
+        }
+
+        // Install a Linux PRNG-based SecureRandom implementation as the
+        // default, if not yet installed.
+        Provider[] secureRandomProviders =
+                Security.getProviders("SecureRandom.SHA1PRNG");
+        if ((secureRandomProviders == null)
+                || (secureRandomProviders.length < 1)
+                || (!LinuxPRNGSecureRandomProvider.class.equals(
+                        secureRandomProviders[0].getClass()))) {
+            Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1);
+        }
+
+        // Assert that new SecureRandom() and
+        // SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed
+        // by the Linux PRNG-based SecureRandom implementation.
+        SecureRandom rng1 = new SecureRandom();
+        if (!LinuxPRNGSecureRandomProvider.class.equals(
+                rng1.getProvider().getClass())) {
+            throw new SecurityException(
+                    "new SecureRandom() backed by wrong Provider: "
+                            + rng1.getProvider().getClass());
+        }
+
+        SecureRandom rng2;
+        try {
+            rng2 = SecureRandom.getInstance("SHA1PRNG");
+        } catch (NoSuchAlgorithmException e) {
+            throw new SecurityException("SHA1PRNG not available", e);
+        }
+        if (!LinuxPRNGSecureRandomProvider.class.equals(
+                rng2.getProvider().getClass())) {
+            throw new SecurityException(
+                    "SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
+                    + " Provider: " + rng2.getProvider().getClass());
+        }
+    }
+
+    /**
+     * {@code Provider} of {@code SecureRandom} engines which pass through
+     * all requests to the Linux PRNG.
+     */
+    private static class LinuxPRNGSecureRandomProvider extends Provider {
+
+        public LinuxPRNGSecureRandomProvider() {
+            super("LinuxPRNG",
+                    1.0,
+                    "A Linux-specific random number provider that uses"
+                        + " /dev/urandom");
+            // Although /dev/urandom is not a SHA-1 PRNG, some apps
+            // explicitly request a SHA1PRNG SecureRandom and we thus need to
+            // prevent them from getting the default implementation whose output
+            // may have low entropy.
+            put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom.class.getName());
+            put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
+        }
+    }
+
+    /**
+     * {@link SecureRandomSpi} which passes all requests to the Linux PRNG
+     * ({@code /dev/urandom}).
+     */
+    public static class LinuxPRNGSecureRandom extends SecureRandomSpi {
+
+        /*
+         * IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed
+         * are passed through to the Linux PRNG (/dev/urandom). Instances of
+         * this class seed themselves by mixing in the current time, PID, UID,
+         * build fingerprint, and hardware serial number (where available) into
+         * Linux PRNG.
+         *
+         * Concurrency: Read requests to the underlying Linux PRNG are
+         * serialized (on sLock) to ensure that multiple threads do not get
+         * duplicated PRNG output.
+         */
+
+        private static final File URANDOM_FILE = new File("/dev/urandom");
+
+        private static final Object sLock = new Object();
+
+        /**
+         * Input stream for reading from Linux PRNG or {@code null} if not yet
+         * opened.
+         *
+         * @GuardedBy("sLock")
+         */
+        private static DataInputStream sUrandomIn;
+
+        /**
+         * Output stream for writing to Linux PRNG or {@code null} if not yet
+         * opened.
+         *
+         * @GuardedBy("sLock")
+         */
+        private static OutputStream sUrandomOut;
+
+        /**
+         * Whether this engine instance has been seeded. This is needed because
+         * each instance needs to seed itself if the client does not explicitly
+         * seed it.
+         */
+        private boolean mSeeded;
+
+        @Override
+        protected void engineSetSeed(byte[] bytes) {
+            try {
+                OutputStream out;
+                synchronized (sLock) {
+                    out = getUrandomOutputStream();
+                }
+                out.write(bytes);
+                out.flush();
+            } catch (IOException e) {
+                // On a small fraction of devices /dev/urandom is not writable.
+                // Log and ignore.
+                Log.w(PrngWorkaround.class.getSimpleName(),
+                        "Failed to mix seed into " + URANDOM_FILE);
+            } finally {
+                mSeeded = true;
+            }
+        }
+
+        @Override
+        protected void engineNextBytes(byte[] bytes) {
+            if (!mSeeded) {
+                // Mix in the device- and invocation-specific seed.
+                engineSetSeed(generateSeed());
+            }
+
+            try {
+                DataInputStream in;
+                synchronized (sLock) {
+                    in = getUrandomInputStream();
+                }
+                synchronized (in) {
+                    in.readFully(bytes);
+                }
+            } catch (IOException e) {
+                throw new SecurityException(
+                        "Failed to read from " + URANDOM_FILE, e);
+            }
+        }
+
+        @Override
+        protected byte[] engineGenerateSeed(int size) {
+            byte[] seed = new byte[size];
+            engineNextBytes(seed);
+            return seed;
+        }
+
+        private DataInputStream getUrandomInputStream() {
+            synchronized (sLock) {
+                if (sUrandomIn == null) {
+                    // NOTE: Consider inserting a BufferedInputStream between
+                    // DataInputStream and FileInputStream if you need higher
+                    // PRNG output performance and can live with future PRNG
+                    // output being pulled into this process prematurely.
+                    try {
+                        sUrandomIn = new DataInputStream(
+                                new FileInputStream(URANDOM_FILE));
+                    } catch (IOException e) {
+                        throw new SecurityException("Failed to open "
+                                + URANDOM_FILE + " for reading", e);
+                    }
+                }
+                return sUrandomIn;
+            }
+        }
+
+        private OutputStream getUrandomOutputStream() throws IOException {
+            synchronized (sLock) {
+                if (sUrandomOut == null) {
+                    sUrandomOut = new FileOutputStream(URANDOM_FILE);
+                }
+                return sUrandomOut;
+            }
+        }
+    }
+
+    /**
+     * Generates a device- and invocation-specific seed to be mixed into the
+     * Linux PRNG.
+     */
+    private static byte[] generateSeed() {
+        try {
+            ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
+            DataOutputStream seedBufferOut =
+                    new DataOutputStream(seedBuffer);
+            seedBufferOut.writeLong(System.currentTimeMillis());
+            seedBufferOut.writeLong(System.nanoTime());
+            seedBufferOut.writeInt(Process.myPid());
+            seedBufferOut.writeInt(Process.myUid());
+            seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL);
+            seedBufferOut.close();
+            return seedBuffer.toByteArray();
+        } catch (IOException e) {
+            throw new SecurityException("Failed to generate seed", e);
+        }
+    }
+
+    /**
+     * Gets the hardware serial number of this device.
+     *
+     * @return serial number or {@code null} if not available.
+     */
+    private static String getDeviceSerialNumber() {
+        // We're using the Reflection API because Build.SERIAL is only available
+        // since API Level 9 (Gingerbread, Android 2.3).
+        try {
+            return (String) Build.class.getField("SERIAL").get(null);
+        } catch (Exception ignored) {
+            return null;
+        }
+    }
+
+    private static byte[] getBuildFingerprintAndDeviceSerial() {
+        StringBuilder result = new StringBuilder();
+        String fingerprint = Build.FINGERPRINT;
+        if (fingerprint != null) {
+            result.append(fingerprint);
+        }
+        String serial = getDeviceSerialNumber();
+        if (serial != null) {
+            result.append(serial);
+        }
+        try {
+            return result.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("UTF-8 encoding not supported");
+        }
+    }
+}
\ No newline at end of file
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckPermissions.java.txt b/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckPermissions.java.txt
new file mode 100644
index 0000000..1d17110
--- /dev/null
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckPermissions.java.txt
@@ -0,0 +1,17 @@
+package test.pkg;
+
+import android.view.View;
+
+public class CheckPermissions {
+  private void foo() {
+      context.checkCallingOrSelfPermission(Manifest.permission.INTERNET); // WRONG
+      context.checkPermission(Manifest.permission.INTERNET); // UNKNOWN (without type resolution)
+      check(context.checkCallingOrSelfPermission(Manifest.permission.INTERNET)); // OK
+      int check = context.checkCallingOrSelfPermission(Manifest.permission.INTERNET); // OK
+      if (context.checkCallingOrSelfPermission(Manifest.permission.INTERNET) // OK
+              != PackageManager.PERMISSION_GRANTED) {
+          Util.showAlert(context, "Error",
+                  "Application requires permission to access the Internet");
+      }
+  }
+}
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaVisitor.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaVisitor.java
index be52510..96a973d 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaVisitor.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaVisitor.java
@@ -24,6 +24,7 @@
 import com.android.tools.lint.detector.api.Detector.JavaScanner;
 import com.android.tools.lint.detector.api.Detector.XmlScanner;
 import com.android.tools.lint.detector.api.JavaContext;
+import com.google.common.collect.Maps;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -133,7 +134,7 @@
     private static final int SAME_TYPE_COUNT = 8;
 
     private final Map<String, List<VisitingDetector>> mMethodDetectors =
-            new HashMap<String, List<VisitingDetector>>();
+            Maps.newHashMapWithExpectedSize(24);
     private final List<VisitingDetector> mResourceFieldDetectors =
             new ArrayList<VisitingDetector>();
     private final List<VisitingDetector> mAllDetectors;
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/JavaContext.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/JavaContext.java
index d91f423..623101f 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/JavaContext.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/JavaContext.java
@@ -23,6 +23,7 @@
 
 import java.io.File;
 
+import lombok.ast.ClassDeclaration;
 import lombok.ast.ConstructorDeclaration;
 import lombok.ast.MethodDeclaration;
 import lombok.ast.Node;
@@ -124,4 +125,21 @@
 
         return null;
     }
+
+    @Nullable
+    public static ClassDeclaration findSurroundingClass(Node scope) {
+        while (scope != null) {
+            Class<? extends Node> type = scope.getClass();
+            // The Lombok AST uses a flat hierarchy of node type implementation classes
+            // so no need to do instanceof stuff here.
+            if (type == ClassDeclaration.class) {
+                return (ClassDeclaration) scope;
+            }
+
+            scope = scope.getParent();
+        }
+
+        return null;
+    }
+
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java
index 4d70a8b..6bd30e2 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java
@@ -36,7 +36,7 @@
     private static final List<Issue> sIssues;
 
     static {
-        final int initialCapacity = 158;
+        final int initialCapacity = 160;
         List<Issue> issues = new ArrayList<Issue>(initialCapacity);
 
         issues.add(AccessibilityDetector.ISSUE);
@@ -135,6 +135,8 @@
         issues.add(SecurityDetector.WORLD_READABLE);
         issues.add(SecurityDetector.WORLD_WRITEABLE);
         issues.add(SecureRandomDetector.ISSUE);
+        issues.add(CheckPermissionDetector.ISSUE);
+        issues.add(SecureRandomGeneratorDetector.ISSUE);
         issues.add(IconDetector.GIF_USAGE);
         issues.add(IconDetector.ICON_DENSITIES);
         issues.add(IconDetector.ICON_MISSING_FOLDER);
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CheckPermissionDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CheckPermissionDetector.java
new file mode 100644
index 0000000..f0a9233
--- /dev/null
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CheckPermissionDetector.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.tools.lint.detector.api.*;
+
+import lombok.ast.*;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Ensures that calls to check permission use the result (otherwise they probably meant to call the
+ * <b>enforce</b> permission methods instead)
+ */
+public class CheckPermissionDetector extends Detector implements Detector.JavaScanner {
+    /** Main issue checked by this detector */
+    public static final Issue ISSUE = Issue.create("UseCheckPermission", //$NON-NLS-1$
+        "Using the result of check permission calls",
+        "Ensures that the return value of check permission calls are used",
+
+        "You normally want to use the result of checking a permission; these methods " +
+        "return whether the permission is held; they do not throw an error if the permission " +
+        "is not granted. Code which does not do anything with the return value probably " +
+        "meant to be calling the enforce methods instead, e.g. rather than " +
+        "`Context#checkCallingPermission` it should call `Context#enforceCallingPermission`.",
+
+        Category.SECURITY, 6, Severity.WARNING,
+        new Implementation(CheckPermissionDetector.class, Scope.JAVA_FILE_SCOPE));
+
+    private static final String CHECK_PERMISSION = "checkPermission";
+
+    /**
+     * Constructs a new {@link com.android.tools.lint.checks.CheckPermissionDetector} check
+     */
+    public CheckPermissionDetector() {
+    }
+
+    // ---- Implements JavaScanner ----
+
+    @Override
+    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
+            @NonNull MethodInvocation node) {
+        if (node.getParent() instanceof ExpressionStatement) {
+            String check = node.astName().astValue();
+            if (CHECK_PERMISSION.equals(check)) {
+                if (!ensureContextMethod(context, node)) {
+                    return;
+                }
+            }
+            assert check.startsWith("check") : check;
+            String enforce = "enforce" + check.substring("check".length());
+            context.report(ISSUE, node, context.getLocation(node),
+                    String.format(
+                            "The result of %1$s is not used; did you mean to call %2$s?",
+                            check, enforce), null);
+        }
+    }
+
+    private static boolean ensureContextMethod(
+            @NonNull JavaContext context,
+            @NonNull MethodInvocation node) {
+        // Method name used in many other contexts where it doesn't have the
+        // same semantics; only use this one if we can resolve types
+        // and we're certain this is the Context method
+        Node resolved = context.parser.resolve(context, node);
+        if (resolved instanceof MethodDeclaration) {
+            ClassDeclaration declaration = JavaContext.findSurroundingClass(resolved);
+            if (declaration != null && declaration.astName() != null) {
+                String className = declaration.astName().astValue();
+                if ("ContextWrapper".equals(className) || "Context".equals(className)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public List<String> getApplicableMethodNames() {
+        return Arrays.asList(
+                CHECK_PERMISSION,
+                "checkUriPermission",
+                "checkCallingOrSelfPermission",
+                "checkCallingPermission",
+                "checkCallingUriPermission",
+                "checkCallingOrSelfUriPermission"
+        );
+    }
+}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecureRandomDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecureRandomDetector.java
index 33af4a0..b75ead7 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecureRandomDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecureRandomDetector.java
@@ -66,7 +66,7 @@
             .addMoreInfo("http://developer.android.com/reference/java/security/SecureRandom.html");
 
     private static final String SET_SEED = "setSeed"; //$NON-NLS-1$
-    private static final String OWNER_SECURE_RANDOM = "java/security/SecureRandom"; //$NON-NLS-1$
+    static final String OWNER_SECURE_RANDOM = "java/security/SecureRandom"; //$NON-NLS-1$
     private static final String OWNER_RANDOM = "java/util/Random"; //$NON-NLS-1$
     private static final String VM_SECURE_RANDOM = 'L' + OWNER_SECURE_RANDOM + ';';
     /** Method description for a method that takes a long argument (no return type specified */
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecureRandomGeneratorDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecureRandomGeneratorDetector.java
new file mode 100644
index 0000000..7c9012d
--- /dev/null
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecureRandomGeneratorDetector.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import static com.android.SdkConstants.CONSTRUCTOR_NAME;
+import static com.android.tools.lint.checks.SecureRandomDetector.OWNER_SECURE_RANDOM;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
+import com.android.tools.lint.detector.api.Category;
+import com.android.tools.lint.detector.api.ClassContext;
+import com.android.tools.lint.detector.api.Context;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Detector.ClassScanner;
+import com.android.tools.lint.detector.api.Implementation;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.LintUtils;
+import com.android.tools.lint.detector.api.Location;
+import com.android.tools.lint.detector.api.Scope;
+import com.android.tools.lint.detector.api.Severity;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.LdcInsnNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Checks for pseudo random number generator initialization issues
+ */
+public class SecureRandomGeneratorDetector extends Detector implements ClassScanner {
+
+    @SuppressWarnings("SpellCheckingInspection")
+    private static final String BLOG_URL
+            = "https://android-developers.blogspot.com/2013/08/some-securerandom-thoughts.html";
+
+    /** Whether the random number generator is initialized correctly */
+    public static final Issue ISSUE = Issue.create(
+            "TrulyRandom", //$NON-NLS-1$
+            "Weak RNG",
+            "Looks for calls to JCA primitives that may be affected by SecureRandom vulnerability",
+            "Key generation, signing, encryption, and random number generation may not " +
+            "receive cryptographically strong values due to improper initialization of " +
+            "the underlying PRNG on Android 4.3 and below.\n" +
+            "\n" +
+            "If your application relies on cryptographically secure random number generation " +
+            "you should apply the workaround described in " + BLOG_URL + " .\n" +
+            "\n" +
+            "This lint rule is mostly informational; it does not accurately detect whether " +
+            "cryptographically secure RNG is required, or whether the workaround has already " +
+            "been applied. After reading the blog entry and updating your code if necessary, " +
+            "you can disable this lint issue.",
+
+            Category.SECURITY,
+            9,
+            Severity.WARNING,
+            new Implementation(
+                    SecureRandomGeneratorDetector.class,
+                    Scope.CLASS_FILE_SCOPE))
+            .addMoreInfo(BLOG_URL);
+
+    private static final String WRAP = "wrap";                        //$NON-NLS-1$
+    private static final String UNWRAP = "unwrap";                    //$NON-NLS-1$
+    private static final String INIT = "init";                        //$NON-NLS-1$
+    private static final String INIT_SIGN = "initSign";               //$NON-NLS-1$
+    private static final String GET_INSTANCE = "getInstance";         //$NON-NLS-1$
+    private static final String FOR_NAME = "forName";                 //$NON-NLS-1$
+    private static final String JAVA_LANG_CLASS = "java/lang/Class";  //$NON-NLS-1$
+    private static final String JAVAX_CRYPTO_KEY_GENERATOR = "javax/crypto/KeyGenerator";
+    private static final String JAVAX_CRYPTO_KEY_AGREEMENT = "javax/crypto/KeyAgreement";
+    private static final String JAVA_SECURITY_KEY_PAIR_GENERATOR =
+            "java/security/KeyPairGenerator";
+    private static final String JAVAX_CRYPTO_SIGNATURE = "javax/crypto/Signature";
+    private static final String JAVAX_CRYPTO_CIPHER = "javax/crypto/Cipher";
+    private static final String JAVAX_NET_SSL_SSLENGINE = "javax/net/ssl/SSLEngine";
+
+    /** Constructs a new {@link SecureRandomGeneratorDetector} */
+    public SecureRandomGeneratorDetector() {
+    }
+
+    // ---- Implements ClassScanner ----
+
+    @Nullable
+    @Override
+    public List<String> getApplicableCallOwners() {
+        return Arrays.asList(
+                JAVAX_CRYPTO_KEY_GENERATOR,
+                JAVA_SECURITY_KEY_PAIR_GENERATOR,
+                JAVAX_CRYPTO_KEY_AGREEMENT,
+                OWNER_SECURE_RANDOM,
+                JAVAX_NET_SSL_SSLENGINE,
+                JAVAX_CRYPTO_SIGNATURE,
+                JAVAX_CRYPTO_CIPHER
+        );
+    }
+
+    @Nullable
+    @Override
+    public List<String> getApplicableCallNames() {
+        return Collections.singletonList(FOR_NAME);
+    }
+
+    /** Location of first call to key generator (etc), if any */
+    private Location mLocation;
+
+    /** Whether the issue should be ignored (because we have a workaround, or because
+     * we're only targeting correct implementations, etc */
+    private boolean mIgnore;
+
+    @Override
+    public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode,
+            @NonNull MethodNode method, @NonNull MethodInsnNode call) {
+        if (mIgnore) {
+            return;
+        }
+
+        String owner = call.owner;
+        String name = call.name;
+
+        // Look for the workaround code: if we see a Class.forName on the harmony NativeCrypto,
+        // we'll consider that a sign.
+
+        if (name.equals(FOR_NAME)) {
+            if (call.getOpcode() != Opcodes.INVOKESTATIC ||
+                    !owner.equals(JAVA_LANG_CLASS)) {
+                return;
+            }
+            AbstractInsnNode prev = LintUtils.getPrevInstruction(call);
+            if (prev instanceof LdcInsnNode) {
+                Object cst = ((LdcInsnNode)prev).cst;
+                //noinspection SpellCheckingInspection
+                if (cst instanceof String &&
+                        "org.apache.harmony.xnet.provider.jsse.NativeCrypto".equals(cst)) {
+                    mIgnore = true;
+                }
+            }
+            return;
+        }
+
+        // Look for calls that probably require a properly initialized random number generator.
+        assert owner.equals(JAVAX_CRYPTO_KEY_GENERATOR)
+                || owner.equals(JAVA_SECURITY_KEY_PAIR_GENERATOR)
+                || owner.equals(JAVAX_CRYPTO_KEY_AGREEMENT)
+                || owner.equals(OWNER_SECURE_RANDOM)
+                || owner.equals(JAVAX_CRYPTO_CIPHER)
+                || owner.equals(JAVAX_CRYPTO_SIGNATURE)
+                || owner.equals(JAVAX_NET_SSL_SSLENGINE) : owner;
+        boolean warn = false;
+
+        if (owner.equals(JAVAX_CRYPTO_SIGNATURE)) {
+            warn = name.equals(INIT_SIGN);
+        } else if (owner.equals(JAVAX_CRYPTO_CIPHER)) {
+            if (name.equals(INIT)) {
+                int arity = getDescArity(call.desc);
+                AbstractInsnNode node = call;
+                for (int i = 0; i < arity; i++) {
+                    node = LintUtils.getPrevInstruction(node);
+                    if (node == null) {
+                        break;
+                    }
+                }
+                if (node != null) {
+                    int opcode = node.getOpcode();
+                    if (opcode == Opcodes.ICONST_3 || // Cipher.WRAP_MODE
+                        opcode == Opcodes.ICONST_1) { // Cipher.ENCRYPT_MODE
+                        warn = true;
+                    }
+                }
+            }
+        } else if (name.equals(GET_INSTANCE) || name.equals(CONSTRUCTOR_NAME)
+                || name.equals(WRAP) || name.equals(UNWRAP)) { // For SSLEngine
+            warn = true;
+        }
+
+        if (warn) {
+            if (mLocation != null) {
+                return;
+            }
+            if (context.getMainProject().getMinSdk() > 18) {
+                // Fix no longer needed
+                mIgnore = true;
+                return;
+            }
+
+            if (context.getDriver().isSuppressed(ISSUE, classNode, method, call)) {
+                mIgnore = true;
+            } else {
+                mLocation = context.getLocation(call);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    static int getDescArity(String desc) {
+        int arity = 0;
+        // For example, (ILjava/security/Key;)V => 2
+        for (int i = 1, max = desc.length(); i < max; i++) {
+            char c = desc.charAt(i);
+            if (c == ')') {
+                break;
+            } else if (c == 'L') {
+                arity++;
+                i = desc.indexOf(';', i);
+                assert i != -1 : desc;
+            } else {
+                arity++;
+            }
+        }
+
+        return arity;
+    }
+
+    @Override
+    public void afterCheckProject(@NonNull Context context) {
+        if (mLocation != null && !mIgnore) {
+            String message = "Potentially insecure random numbers on Android 4.3 and older. "
+                    + "Read " + BLOG_URL + " for more info.";
+            context.report(ISSUE, mLocation, message, null);
+        }
+    }
+}
diff --git a/sdk-common/sdk-common.iml b/sdk-common/sdk-common.iml
index 03a2b95..4f564af 100644
--- a/sdk-common/sdk-common.iml
+++ b/sdk-common/sdk-common.iml
@@ -5,7 +5,7 @@
     <content url="file://$MODULE_DIR$">
       <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
       <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
       <excludeFolder url="file://$MODULE_DIR$/.settings" />
       <excludeFolder url="file://$MODULE_DIR$/build" />
     </content>
diff --git a/sdk-common/src/main/java/com/android/ide/common/packaging/PackagingUtils.java b/sdk-common/src/main/java/com/android/ide/common/packaging/PackagingUtils.java
index 0471555..b41b84e 100644
--- a/sdk-common/src/main/java/com/android/ide/common/packaging/PackagingUtils.java
+++ b/sdk-common/src/main/java/com/android/ide/common/packaging/PackagingUtils.java
@@ -72,8 +72,8 @@
                 !"swp".equalsIgnoreCase(extension) &&         // vi swap file
                 !"thumbs.db".equalsIgnoreCase(fileName) &&    // image index file
                 !"picasa.ini".equalsIgnoreCase(fileName) &&   // image index file
+                !"about.html".equalsIgnoreCase(fileName) &&   // Javadoc
                 !"package.html".equalsIgnoreCase(fileName) && // Javadoc
                 !"overview.html".equalsIgnoreCase(fileName);  // Javadoc
-
     }
 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/rendering/HardwareConfigHelper.java b/sdk-common/src/main/java/com/android/ide/common/rendering/HardwareConfigHelper.java
index 1e5a760..aff9780 100644
--- a/sdk-common/src/main/java/com/android/ide/common/rendering/HardwareConfigHelper.java
+++ b/sdk-common/src/main/java/com/android/ide/common/rendering/HardwareConfigHelper.java
@@ -271,10 +271,10 @@
         if (id.equals("Nexus S")) {        //$NON-NLS-1$
             return 2;
         }
-        if (id.equals("Galaxy Nexus")) {  //$NON-NLS-1$
+        if (id.equals("Galaxy Nexus")) {   //$NON-NLS-1$
             return 3;
         }
-        if (id.equals("Nexus 7")) { //$NON-NLS-1$
+        if (id.equals("Nexus 7")) {        //$NON-NLS-1$
             return 4; // 2012 version
         }
         if (id.equals("Nexus 10")) {       //$NON-NLS-1$
@@ -283,11 +283,14 @@
         if (id.equals("Nexus 4")) {        //$NON-NLS-1$
             return 6;
         }
-        if (id.equals("Nexus 7 2013")) {        //$NON-NLS-1$
+        if (id.equals("Nexus 7 2013")) {   //$NON-NLS-1$
             return 7;
         }
+        if (id.equals("Nexus 5")) {        //$NON-NLS-1$
+          return 8;
+        }
 
-        return 8; // devices released in the future?
+        return 100; // devices released in the future?
     }
 
     /**
diff --git a/sdk-common/src/main/java/com/android/ide/common/rendering/RenderSecurityException.java b/sdk-common/src/main/java/com/android/ide/common/rendering/RenderSecurityException.java
new file mode 100644
index 0000000..38dcce9
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/rendering/RenderSecurityException.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.common.rendering;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+
+/** Exception thrown when custom view code makes an illegal code while rendering under layoutlib */
+public class RenderSecurityException extends SecurityException {
+
+    private final String myMessage;
+
+    /** Use one of the create factory methods */
+    private RenderSecurityException(@NonNull String message) {
+        super(message);
+        myMessage = message;
+    }
+
+    @Override
+    public String getMessage() {
+        return myMessage;
+    }
+
+    @Override
+    public String toString() {
+        // super prepends the fully qualified name of the exception
+        return getMessage();
+    }
+    /**
+     * Creates a new {@linkplain RenderSecurityException}
+     *
+     * @param resource the type of resource being accessed - "Thread", "Write", "Socket", etc
+     * @param context more information about the object, such as the path of the file being read
+     * @return a new exception
+     */
+    @NonNull
+    public static RenderSecurityException create(@NonNull String resource,
+            @Nullable String context) {
+        return new RenderSecurityException(computeLabel(resource, context));
+    }
+
+    /**
+     * Creates a new {@linkplain RenderSecurityException}
+     *
+     * @param message the message for the exception
+     * @return a new exception
+     */
+    @NonNull
+    public static RenderSecurityException create(@NonNull String message) {
+        return new RenderSecurityException(message);
+    }
+
+    private static String computeLabel(@NonNull String resource, @Nullable String context) {
+        StringBuilder sb = new StringBuilder(40);
+        sb.append(resource);
+        sb.append(" access not allowed during rendering");
+        if (context != null) {
+            sb.append(" (").append(context).append(")");
+        }
+        return sb.toString();
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/rendering/RenderSecurityManager.java b/sdk-common/src/main/java/com/android/ide/common/rendering/RenderSecurityManager.java
new file mode 100644
index 0000000..1126777
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/rendering/RenderSecurityManager.java
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.common.rendering;
+
+import static com.android.SdkConstants.DOT_CLASS;
+import static com.android.SdkConstants.DOT_JAR;
+import static com.android.SdkConstants.VALUE_FALSE;
+
+import com.android.annotations.Nullable;
+import com.android.utils.ILogger;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FilePermission;
+import java.net.InetAddress;
+import java.security.Permission;
+
+/**
+ * A {@link java.lang.SecurityManager} which is used for layout lib rendering, to
+ * prevent custom views from accidentally exiting the whole IDE if they call
+ * {@code System.exit}, as well as unintentionally writing files etc.
+ * <p>
+ * The security manager only checks calls on the current thread for which it
+ * was made active with a call to {@link #setActive(boolean)}, as well as any
+ * threads constructed from the render thread.
+ */
+public class RenderSecurityManager extends SecurityManager {
+    /** Property used to disable sandbox */
+    public static final String ENABLED_PROPERTY = "android.render.sandbox";
+
+    /** Whether we should restrict reading to certain paths */
+    public static final boolean RESTRICT_READS = false;
+
+    /**
+     * Whether the security manager is enabled for this session (it might still
+     * be inactive, either because it's active for a different thread, or because
+     * it has been disabled via {@link #setActive(boolean)} (which sets the
+     * per-instance mEnabled flag)
+     */
+    public static boolean sEnabled =
+            !VALUE_FALSE.equals(System.getProperty(ENABLED_PROPERTY));
+
+    /**
+     * Thread local data which indicates whether the current thread is relevant for
+     * this security manager. This is an inheritable thread local such that any threads
+     * spawned from this thread will also apply the security manager; otherwise code
+     * could just create new threads and execute code separate from the security manager
+     * there.
+     */
+    private static ThreadLocal<Boolean> sIsRenderThread = new InheritableThreadLocal<Boolean>() {
+        @Override protected synchronized Boolean initialValue() {
+            return Boolean.FALSE;
+        }
+        @Override protected synchronized Boolean childValue(Boolean parentValue) {
+            return parentValue;
+        }
+    };
+
+    private boolean mAllowSetSecurityManager;
+    private boolean mDisabled;
+    private String mSdkPath;
+    private String mProjectPath;
+    private String mTempDir;
+    private SecurityManager myPreviousSecurityManager;
+    private ILogger mLogger;
+
+    /**
+     * Returns the current render security manager, if any. This will only return
+     * non-null if there is an active {@linkplain RenderSecurityManager} as the
+     * current global security manager.
+     */
+    @Nullable
+    public static RenderSecurityManager getCurrent() {
+        if (sIsRenderThread.get()) {
+            SecurityManager securityManager = System.getSecurityManager();
+            if (securityManager instanceof RenderSecurityManager) {
+                return (RenderSecurityManager) securityManager;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Creates a security manager suitable for controlling access to custom views
+     * being rendered by layoutlib, ensuring that they don't accidentally try to
+     * write files etc (which could corrupt data if they for example assume device
+     * paths that are not the same for the running IDE; for example, they could try
+     * to clear out their own local app storage, which in the IDE could be the
+     * user's home directory.)
+     * <p>
+     * Note: By default a security manager is not active. You need to call
+     * {@link #setActive(boolean)} with true to activate it, <b>instead</b> of just calling
+     * {@link System#setSecurityManager(SecurityManager)}.
+     *
+     * @param sdkPath an optional path to the SDK install being used by layoutlib;
+     *                this is used to white-list path prefixes for layoutlib resource
+     *                lookup
+     * @param projectPath a path to the project directory, used for similar purposes
+     */
+    public RenderSecurityManager(
+            @Nullable String sdkPath,
+            @Nullable String projectPath) {
+        mSdkPath = sdkPath;
+        mProjectPath = projectPath;
+        mTempDir = System.getProperty("java.io.tmpdir");
+    }
+
+    /** Sets an optional logger. Returns this for constructor chaining. */
+    public RenderSecurityManager setLogger(@Nullable ILogger logger) {
+        mLogger = logger;
+        return this;
+    }
+
+    public void setActive(boolean active) {
+        SecurityManager current = System.getSecurityManager();
+        boolean isActive = current == this;
+        if (active == isActive) {
+            return;
+        }
+
+        if (active) {
+            // Enable
+            assert !(current instanceof RenderSecurityManager);
+            myPreviousSecurityManager = current;
+            sIsRenderThread.set(true);
+            mDisabled = false;
+            System.setSecurityManager(this);
+        } else {
+            // Disable
+            mAllowSetSecurityManager = true;
+            // Don't set mDisabled and clear sInRenderThread yet: the call
+            // to revert to the previous security manager below will trigger
+            // a check permission, and in that code we need to distinguish between
+            // this call (isRelevant() should return true) and other threads calling
+            // it outside the scope of the security manager
+            try {
+                // Only reset the security manager if it hasn't already been set to
+                // something else. If other threads try to do the same thing we could have
+                // a problem; if they sampled the render security manager while it was globally
+                // active, replaced it with their own, and sometime in the future try to
+                // set it back, it will be active when we didn't intend for it to be. That's
+                // why there is also the {@code mDisabled} flag, used to ignore any requests
+                // later on.
+                if (current instanceof RenderSecurityManager) {
+                    System.setSecurityManager(myPreviousSecurityManager);
+                } else if (mLogger != null) {
+                    sIsRenderThread.set(false);
+                    mLogger.warning("Security manager was changed behind the scenes: ", current);
+                }
+            } finally {
+                mDisabled = true;
+                mAllowSetSecurityManager = false;
+                sIsRenderThread.set(false);
+            }
+        }
+    }
+
+    private boolean isRelevant() {
+        return sEnabled && !mDisabled && sIsRenderThread.get();
+    }
+
+    public void dispose() {
+        setActive(false);
+    }
+
+    // Permitted by custom views: access any package or member, read properties
+
+    @Override
+    public void checkPackageAccess(String pkg) {
+    }
+
+    @Override
+    public void checkMemberAccess(Class<?> clazz, int which) {
+    }
+
+    @Override
+    public void checkPropertyAccess(String property) {
+    }
+
+    @Override
+    public void checkLink(String lib) {
+        // Needed to for example load the "fontmanager" library from layout lib (from the
+        // BiDiRenderer's layoutGlyphVector call
+    }
+
+    @Override
+    public void checkCreateClassLoader() {
+        // TODO: Layoutlib makes heavy use of this, so we can't block it yet.
+        // To fix this we should make a local class loader, passed to layoutlib, which
+        // knows how to reset the security manager
+    }
+
+    //------------------------------------------------------------------------------------------
+    // Reading is permitted for certain files only
+    //------------------------------------------------------------------------------------------
+
+    @SuppressWarnings({"PointlessBooleanExpression", "ConstantConditions"})
+    @Override
+    public void checkRead(String file) {
+        if (RESTRICT_READS && isRelevant() && !isReadingAllowed(file)) {
+            throw RenderSecurityException.create("Read", file);
+        }
+    }
+
+    @SuppressWarnings({"PointlessBooleanExpression", "ConstantConditions"})
+    @Override
+    public void checkRead(String file, Object context) {
+        if (RESTRICT_READS && isRelevant() && !isReadingAllowed(file)) {
+            throw RenderSecurityException.create("Read", file);
+        }
+    }
+
+    private boolean isReadingAllowed(String path) {
+        if (RESTRICT_READS) {
+            // Allow reading files in the SDK install (fonts etc)
+            if (mSdkPath != null && path.startsWith(mSdkPath)) {
+                return true;
+            }
+
+            // Allowing reading resources in the project, such as icons
+            if (mProjectPath != null && path.startsWith(mProjectPath)) {
+                return true;
+            }
+
+            if (path.startsWith("#") && path.indexOf(File.separatorChar) == -1) {
+                // It's really layoutlib's ResourceHelper.getColorStateList which calls isFile()
+                // on values to see if it's a file or a color.
+                return true;
+            }
+
+            // Needed by layoutlib's class loader. Note that we've locked down the ability to
+            // create new class loaders.
+            if (path.endsWith(DOT_CLASS) || path.endsWith(DOT_JAR)) {
+                return true;
+            }
+
+            // Allow reading files in temp
+            if (path.startsWith(mTempDir)) {
+                return true;
+            }
+
+            String javaHome = System.getProperty("java.home");
+            if (path.startsWith(javaHome)) { // Allow JDK to load its own classes
+                return true;
+            } else if (javaHome.endsWith("/Contents/Home")) {
+                // On Mac, Home lives two directory levels down from the real home, and we
+                // sometimes need to read from sibling directories (e.g. ../Libraries/ etc)
+                if (path.regionMatches(0, javaHome, 0, javaHome.length() -
+                        "Contents/Home".length())) {
+                    return true;
+                }
+            }
+
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    private boolean isWritingAllowed(String path) {
+        //noinspection RedundantIfStatement
+        if (path.startsWith(mTempDir)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    //------------------------------------------------------------------------------------------
+    // Not permitted:
+    //------------------------------------------------------------------------------------------
+
+    @Override
+    public void checkExit(int status) {
+        // Probably not intentional in a custom view; would take down the whole IDE!
+        if (isRelevant()) {
+            throw RenderSecurityException.create("Exit", String.valueOf(status));
+        }
+
+        super.checkExit(status);
+    }
+
+    @Override
+    public void checkPropertiesAccess() {
+        if (isRelevant()) {
+            throw RenderSecurityException.create("Property", null);
+        }
+    }
+
+    // Prevent code execution/linking/loading
+
+    @Override
+    public void checkPackageDefinition(String pkg) {
+        if (isRelevant()) {
+            throw RenderSecurityException.create("Package", pkg);
+        }
+    }
+
+    @Override
+    public void checkExec(String cmd) {
+        if (isRelevant()) {
+            throw RenderSecurityException.create("Exec", cmd);
+        }
+    }
+
+    // Prevent network access
+
+    @Override
+    public void checkConnect(String host, int port) {
+        if (isRelevant()) {
+            throw RenderSecurityException.create("Socket", host + ":" + port);
+        }
+    }
+
+    @Override
+    public void checkConnect(String host, int port, Object context) {
+        if (isRelevant()) {
+            throw RenderSecurityException.create("Socket", host + ":" + port);
+        }
+    }
+
+    @Override
+    public void checkListen(int port) {
+        if (isRelevant()) {
+            throw RenderSecurityException.create("Socket", "port " + port);
+        }
+    }
+
+    @Override
+    public void checkAccept(String host, int port) {
+        if (isRelevant()) {
+            throw RenderSecurityException.create("Socket", host + ":" + port);
+        }
+    }
+
+    @Override
+    public void checkSetFactory() {
+        if (isRelevant()) {
+            throw RenderSecurityException.create("Socket", null);
+        }
+    }
+
+    @Override
+    public void checkMulticast(InetAddress inetAddress) {
+        if (isRelevant()) {
+            throw RenderSecurityException.create("Socket", inetAddress.getCanonicalHostName());
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public void checkMulticast(InetAddress inetAddress, byte ttl) {
+        if (isRelevant()) {
+            throw RenderSecurityException.create("Socket", inetAddress.getCanonicalHostName());
+        }
+    }
+
+    // Prevent file access
+
+    @Override
+    public void checkDelete(String file) {
+        if (isRelevant()) {
+            // Allow writing to temp
+            if (isWritingAllowed(file)) {
+                return;
+            }
+
+            throw RenderSecurityException.create("Delete", file);
+        }
+    }
+
+    @Override
+    public void checkAwtEventQueueAccess() {
+        if (isRelevant()) {
+            throw RenderSecurityException.create("Event", null);
+        }
+    }
+
+    // Prevent writes
+
+    @Override
+    public void checkWrite(FileDescriptor fileDescriptor) {
+        if (isRelevant()) {
+            throw RenderSecurityException.create("Write", fileDescriptor.toString());
+        }
+    }
+
+    @Override
+    public void checkWrite(String file) {
+        if (isRelevant()) {
+            if (isWritingAllowed(file)) {
+                return;
+            }
+
+            throw RenderSecurityException.create("Write", file);
+        }
+    }
+
+    // Misc
+
+    @Override
+    public void checkPrintJobAccess() {
+        if (isRelevant()) {
+            throw RenderSecurityException.create("Print", null);
+        }
+    }
+
+    @Override
+    public void checkSystemClipboardAccess() {
+        if (isRelevant()) {
+            throw RenderSecurityException.create("Clipboard", null);
+        }
+    }
+
+    @Override
+    public boolean checkTopLevelWindow(Object context) {
+        if (isRelevant()) {
+            throw RenderSecurityException.create("Window", null);
+        }
+        return false;
+    }
+
+    @Override
+    public void checkAccess(Thread thread) {
+        // Turns out layoutlib sometimes creates asynchronous calls, for example
+        //       java.lang.Thread.<init>(Thread.java:521)
+        //       at android.os.AsyncTask$1.newThread(AsyncTask.java:189)
+        //       at java.util.concurrent.ThreadPoolExecutor.addThread(ThreadPoolExecutor.java:670)
+        //       at java.util.concurrent.ThreadPoolExecutor.addIfUnderCorePoolSize(ThreadPoolExecutor.java:706)
+        //       at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:650)
+        //       at android.os.AsyncTask$SerialExecutor.scheduleNext(AsyncTask.java:244)
+        //       at android.os.AsyncTask$SerialExecutor.execute(AsyncTask.java:238)
+        //       at android.os.AsyncTask.execute(AsyncTask.java:604)
+        //       at android.widget.TextView.updateTextServicesLocaleAsync(TextView.java:8078)
+
+        // This may not work correctly for render sessions, which are treated as synchronous
+        // by callers. We should re-enable these checks to chase down these calls and
+        // eliminate them from layoutlib, but until we do, it's necessary to allow thread
+        // creation.
+    }
+
+    @Override
+    public void checkAccess(ThreadGroup threadGroup) {
+        // See checkAccess(Thread)
+    }
+
+    @Override
+    public void checkPermission(Permission permission) {
+        String name = permission.getName();
+        if ("setSecurityManager".equals(name)) {
+            if (isRelevant()) {
+                if (!mAllowSetSecurityManager) {
+                    throw RenderSecurityException.create("Security", null);
+                }
+            } else if (mLogger != null) {
+                mLogger.warning("RenderSecurityManager being replaced by another thread");
+            }
+        } else if (isRelevant()) {
+            String actions = permission.getActions();
+            //noinspection PointlessBooleanExpression,ConstantConditions
+            if (RESTRICT_READS && "read".equals(actions)) {
+                if (!isReadingAllowed(name)) {
+                    throw RenderSecurityException.create("Read", name);
+                }
+            } else if (!actions.isEmpty() && !actions.equals("read")) {
+                // write, execute, delete, readlink
+                if (!(permission instanceof FilePermission) || !isWritingAllowed(name)) {
+                    throw RenderSecurityException.create("Write", name);
+                }
+            }
+        }
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/NodeUtils.java b/sdk-common/src/main/java/com/android/ide/common/res2/NodeUtils.java
index e977bdf..f945cdb 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/NodeUtils.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/NodeUtils.java
@@ -80,7 +80,15 @@
         NamedNodeMap attributes = node.getAttributes();
         if (attributes != null) {
             for (int i = 0, n = attributes.getLength(); i < n; i++) {
-                processSingleNodeNamespace(attributes.item(i), document);
+                Node attribute = attributes.item(i);
+                if (!processSingleNodeNamespace(attribute, document)) {
+                    String nsUri = attribute.getNamespaceURI();
+                    if (nsUri != null) {
+                        attributes.removeNamedItemNS(nsUri, attribute.getLocalName());
+                    } else {
+                        attributes.removeNamedItem(attribute.getLocalName());
+                    }
+                }
             }
         }
 
@@ -98,10 +106,17 @@
 
     /**
      * Update the namespace of a given node to work with a given document.
+     *
      * @param node the node to update
      * @param document the new document
+     *
+     * @return false if the attribute is to be dropped
      */
-    private static void processSingleNodeNamespace(Node node, Document document) {
+    private static boolean processSingleNodeNamespace(Node node, Document document) {
+        if ("xmlns".equals(node.getLocalName())) {
+            return false;
+        }
+
         String ns = node.getNamespaceURI();
         if (ns != null) {
             NamedNodeMap docAttributes = document.getAttributes();
@@ -118,6 +133,8 @@
             prefix = prefix.substring(6);
             node.setPrefix(prefix);
         }
+
+        return true;
     }
 
     /**
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceResolver.java b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceResolver.java
index a8e39ee..c470aa4 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceResolver.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceResolver.java
@@ -16,6 +16,7 @@
 
 package com.android.ide.common.resources;
 
+import static com.android.SdkConstants.ANDROID_STYLE_RESOURCE_PREFIX;
 import static com.android.SdkConstants.PREFIX_ANDROID;
 import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
 import static com.android.SdkConstants.REFERENCE_STYLE;
@@ -30,8 +31,10 @@
 
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 public class ResourceResolver extends RenderResources {
     public static final String THEME_NAME = "Theme";
@@ -182,6 +185,11 @@
     @Override
     public ResourceValue findItemInStyle(StyleResourceValue style, String itemName,
             boolean isFrameworkAttr) {
+        return findItemInStyle(style, itemName, isFrameworkAttr, 0);
+    }
+
+    private ResourceValue findItemInStyle(StyleResourceValue style, String itemName,
+                                          boolean isFrameworkAttr, int depth) {
         ResourceValue item = style.findValue(itemName, isFrameworkAttr);
 
         // if we didn't find it, we look in the parent style (if applicable)
@@ -189,13 +197,63 @@
         if (item == null) {
             StyleResourceValue parentStyle = mStyleInheritanceMap.get(style);
             if (parentStyle != null) {
-                return findItemInStyle(parentStyle, itemName, isFrameworkAttr);
+                if (depth >= MAX_RESOURCE_INDIRECTION) {
+                    if (mLogger != null) {
+                        mLogger.error(LayoutLog.TAG_BROKEN,
+                                String.format("Cyclic style parent definitions: %1$s",
+                                        computeCyclicStyleChain(style)),
+                                null);
+                    }
+
+                    return null;
+                }
+
+                return findItemInStyle(parentStyle, itemName, isFrameworkAttr, depth + 1);
             }
         }
 
         return item;
     }
 
+    private String computeCyclicStyleChain(StyleResourceValue style) {
+        StringBuilder sb = new StringBuilder(100);
+        appendStyleParents(style, new HashSet<StyleResourceValue>(), 0, sb);
+        return sb.toString();
+    }
+
+    private void appendStyleParents(StyleResourceValue style, Set<StyleResourceValue> seen,
+            int depth, StringBuilder sb) {
+        if (depth >= MAX_RESOURCE_INDIRECTION) {
+            sb.append("...");
+            return;
+        }
+
+        boolean haveSeen = seen.contains(style);
+        seen.add(style);
+
+        sb.append('"');
+        if (style.isFramework()) {
+            sb.append(PREFIX_ANDROID);
+        }
+        sb.append(style.getName());
+        sb.append('"');
+
+        if (haveSeen) {
+            return;
+        }
+
+        StyleResourceValue parentStyle = mStyleInheritanceMap.get(style);
+        if (parentStyle != null) {
+            if (style.getParentStyle() != null) {
+                sb.append(" specifies parent ");
+            } else {
+                sb.append(" implies parent ");
+            }
+
+            appendStyleParents(parentStyle, seen, depth + 1, sb);
+        }
+    }
+
     @Override
     public ResourceValue findResValue(String reference, boolean forceFrameworkOnly) {
         if (reference == null) {
@@ -357,7 +415,6 @@
 
         // didn't find the resource anywhere.
         return null;
-
     }
 
     /**
@@ -547,6 +604,27 @@
     }
 
     /**
+     * Returns true if the given {@code themeStyle} extends the theme given by
+     * {@code parentStyle}
+     */
+    public boolean themeExtends(@NonNull String parentStyle, @NonNull String themeStyle) {
+        ResourceValue parentValue = findResValue(parentStyle, parentStyle.startsWith(ANDROID_STYLE_RESOURCE_PREFIX));
+        if (parentValue instanceof StyleResourceValue) {
+            ResourceValue themeValue = findResValue(themeStyle,
+                    themeStyle.startsWith(ANDROID_STYLE_RESOURCE_PREFIX));
+            if (themeValue == parentValue) {
+                return true;
+            }
+            if (themeValue instanceof StyleResourceValue) {
+                return themeIsParentOf((StyleResourceValue) parentValue,
+                        (StyleResourceValue) themeValue);
+            }
+        }
+
+        return false;
+    }
+
+    /**
      * Creates a new {@link ResourceResolver} which records all resource resolution
      * lookups into the given list. Note that it is the responsibility of the caller
      * to clear/reset the list between subsequent lookup operations.
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/FolderConfiguration.java b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/FolderConfiguration.java
index 96031fc..fced184 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/FolderConfiguration.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/FolderConfiguration.java
@@ -656,7 +656,26 @@
         return result.toString();
     }
 
-    /**
+  /**
+   * Returns the folder configuration as a unique key
+   */
+  public String getUniqueKey() {
+    StringBuilder result = new StringBuilder(100);
+
+    for (ResourceQualifier qualifier : mQualifiers) {
+      if (qualifier != null) {
+        String segment = qualifier.getFolderSegment();
+        if (segment != null && !segment.isEmpty()) {
+          result.append(SdkConstants.RES_QUALIFIER_SEP);
+          result.append(segment);
+        }
+      }
+    }
+
+    return result.toString();
+  }
+
+  /**
      * Returns {@link #toDisplayString()}.
      */
     @Override
@@ -730,7 +749,7 @@
             return "default";
         }
 
-        StringBuilder result = new StringBuilder();
+        StringBuilder result = new StringBuilder(100);
         int index = 0;
 
         // pre- language/region qualifiers
diff --git a/sdk-common/src/main/java/com/android/ide/common/sdk/SdkVersionInfo.java b/sdk-common/src/main/java/com/android/ide/common/sdk/SdkVersionInfo.java
index a55db2a..70b5749 100644
--- a/sdk-common/src/main/java/com/android/ide/common/sdk/SdkVersionInfo.java
+++ b/sdk-common/src/main/java/com/android/ide/common/sdk/SdkVersionInfo.java
@@ -28,7 +28,7 @@
      * release. This number is used as a baseline and any more recent platforms
      * found can be used to increase the highest known number.
      */
-    public static final int HIGHEST_KNOWN_API = 18;
+    public static final int HIGHEST_KNOWN_API = 19;
 
     /**
      * Returns the Android version and code name of the given API level, or null
@@ -60,6 +60,7 @@
             case 16: return "API 16: Android 4.1 (Jelly Bean)";
             case 17: return "API 17: Android 4.2 (Jelly Bean)";
             case 18: return "API 18: Android 4.3 (Jelly Bean)";
+            case 19: return "API 19: Android 4.4 (KitKat)";
             // If you add more versions here, also update #getBuildCodes and
             // #HIGHEST_KNOWN_API
 
@@ -98,6 +99,7 @@
             case 16: return "JELLY_BEAN"; //$NON-NLS-1$
             case 17: return "JELLY_BEAN_MR1"; //$NON-NLS-1$
             case 18: return "JELLY_BEAN_MR2"; //$NON-NLS-1$
+            case 19: return "KITKAT"; //$NON-NLS-1$
             // If you add more versions here, also update #getAndroidName and
             // #HIGHEST_KNOWN_API
         }
@@ -118,7 +120,7 @@
      */
     public static int getApiByBuildCode(String buildCode, boolean recognizeUnknowns) {
         for (int api = 1; api <= HIGHEST_KNOWN_API; api++) {
-            String code = SdkVersionInfo.getBuildCode(api);
+            String code = getBuildCode(api);
             if (code != null && code.equalsIgnoreCase(buildCode)) {
                 return api;
             }
diff --git a/sdk-common/src/test/java/com/android/ide/common/rendering/HardwareConfigHelperTest.java b/sdk-common/src/test/java/com/android/ide/common/rendering/HardwareConfigHelperTest.java
index 2c82a89..2c4b354 100644
--- a/sdk-common/src/test/java/com/android/ide/common/rendering/HardwareConfigHelperTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/rendering/HardwareConfigHelperTest.java
@@ -71,7 +71,7 @@
     public void testNexusRank() {
         List<Device> devices = Lists.newArrayList();
         DeviceManager deviceManager = getDeviceManager();
-        for (String id : new String[] { "Nexus 7 2013", "Nexus 10", "Nexus 4", "Nexus 7",
+        for (String id : new String[] { "Nexus 7 2013", "Nexus 5", "Nexus 10", "Nexus 4", "Nexus 7",
                                         "Galaxy Nexus", "Nexus S", "Nexus One"}) {
             Device device = deviceManager.getDevice(id, "Google");
             assertNotNull(device);
@@ -94,8 +94,8 @@
                 "Nexus 7",
                 "Nexus 10",
                 "Nexus 4",
-                "Nexus 7 2013"
-
+                "Nexus 7 2013",
+                "Nexus 5"
         ), ids);
     }
 }
diff --git a/sdk-common/src/test/java/com/android/ide/common/rendering/RenderSecurityManagerTest.java b/sdk-common/src/test/java/com/android/ide/common/rendering/RenderSecurityManagerTest.java
new file mode 100644
index 0000000..c1ea6ef
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/rendering/RenderSecurityManagerTest.java
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.common.rendering;
+
+import com.android.ide.common.res2.RecordingLogger;
+import com.google.common.io.Files;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.FilePermission;
+import java.security.Permission;
+import java.util.Collections;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class RenderSecurityManagerTest extends TestCase {
+
+    public void testExec() throws Exception {
+        assertNull(RenderSecurityManager.getCurrent());
+
+        RenderSecurityManager manager = new RenderSecurityManager(null, null);
+        RecordingLogger logger = new RecordingLogger();
+        manager.setLogger(logger);
+        try {
+            assertNull(RenderSecurityManager.getCurrent());
+            manager.setActive(true);
+            assertSame(manager, RenderSecurityManager.getCurrent());
+            if (new File("/bin/ls").exists()) {
+                Runtime.getRuntime().exec("/bin/ls");
+            } else {
+                manager.checkExec("/bin/ls");
+            }
+            fail("Should have thrown security exception");
+        } catch (SecurityException exception) {
+            //noinspection ConstantConditions
+            assertEquals(
+                    RenderSecurityManager.RESTRICT_READS ?
+                        "Read access not allowed during rendering (/bin/ls)" :
+                        "Exec access not allowed during rendering (/bin/ls)",
+                    exception.toString());
+            // pass
+        } finally {
+            manager.dispose();
+            assertNull(RenderSecurityManager.getCurrent());
+            assertNull(System.getSecurityManager());
+            assertEquals(Collections.<String>emptyList(), logger.getWarningMsgs());
+        }
+    }
+
+    public void testSetSecurityManager() throws Exception {
+        RenderSecurityManager manager = new RenderSecurityManager(null, null);
+        try {
+            manager.setActive(true);
+            System.setSecurityManager(null);
+            fail("Should have thrown security exception");
+        } catch (SecurityException exception) {
+            assertEquals("Security access not allowed during rendering", exception.toString());
+            // pass
+        } finally {
+            manager.dispose();
+        }
+    }
+
+    public void testReadWrite() throws Exception {
+        RenderSecurityManager manager = new RenderSecurityManager(null, null);
+        try {
+            manager.setActive(true);
+            manager.checkPermission(new FilePermission("/foo", "read,write"));
+            fail("Should have thrown security exception");
+        } catch (SecurityException exception) {
+            assertEquals("Write access not allowed during rendering (/foo)", exception.toString());
+            // pass
+        } finally {
+            manager.dispose();
+        }
+    }
+
+    public void testExecute() throws Exception {
+        RenderSecurityManager manager = new RenderSecurityManager(null, null);
+        try {
+            manager.setActive(true);
+            manager.checkPermission(new FilePermission("/foo", "execute"));
+            fail("Should have thrown security exception");
+        } catch (SecurityException exception) {
+            assertEquals("Write access not allowed during rendering (/foo)", exception.toString());
+            // pass
+        } finally {
+            manager.dispose();
+        }
+    }
+
+    public void testDelete() throws Exception {
+        RenderSecurityManager manager = new RenderSecurityManager(null, null);
+        try {
+            manager.setActive(true);
+            manager.checkPermission(new FilePermission("/foo", "delete"));
+            fail("Should have thrown security exception");
+        } catch (SecurityException exception) {
+            assertEquals("Write access not allowed during rendering (/foo)", exception.toString());
+            // pass
+        } finally {
+            manager.dispose();
+        }
+    }
+
+    public void testInvalidRead() throws Exception {
+        RenderSecurityManager manager = new RenderSecurityManager(null, null);
+        try {
+            manager.setActive(true);
+
+            if (RenderSecurityManager.RESTRICT_READS) {
+                try {
+                    File file = new File(System.getProperty("user.home"));
+                    //noinspection ResultOfMethodCallIgnored
+                    file.lastModified();
+
+                    fail("Should have thrown security exception");
+                } catch (SecurityException exception) {
+                    assertEquals("Read access not allowed during rendering (" +
+                            System.getProperty("user.home") + ")", exception.toString());
+                    // pass
+                }
+            } else {
+                try {
+                    File file = new File(System.getProperty("user.home"));
+                    //noinspection ResultOfMethodCallIgnored
+                    file.lastModified();
+                } catch (SecurityException exception) {
+                    fail("Reading should be allowed");
+                }
+            }
+        } finally {
+            manager.dispose();
+        }
+    }
+
+    public void testInvalidPropertyWrite() throws Exception {
+        RenderSecurityManager manager = new RenderSecurityManager(null, null);
+        try {
+            manager.setActive(true);
+
+            // Try to make java.io.tmpdir point to user.home to grant myself access:
+            String userHome = System.getProperty("user.home");
+            System.setProperty("java.io.tmpdir", userHome);
+
+            fail("Should have thrown security exception");
+        } catch (SecurityException exception) {
+            assertEquals("Write access not allowed during rendering (java.io.tmpdir)",
+                    exception.toString());
+            // pass
+        } finally {
+            manager.dispose();
+        }
+    }
+
+    public void testReadOk() throws Exception {
+        RenderSecurityManager manager = new RenderSecurityManager(null,  null);
+        try {
+            manager.setActive(true);
+
+            File jdkHome = new File(System.getProperty("java.home"));
+            assertTrue(jdkHome.exists());
+            //noinspection ResultOfMethodCallIgnored
+            File[] files = jdkHome.listFiles();
+            if (files != null) {
+                for (File file : files) {
+                    if (file.isFile()) {
+                        Files.toByteArray(file);
+                    }
+                }
+            }
+        } finally {
+            manager.dispose();
+        }
+    }
+
+    public void testProperties() throws Exception {
+        RenderSecurityManager manager = new RenderSecurityManager(null, null);
+        try {
+            manager.setActive(true);
+
+            System.getProperties();
+
+            fail("Should have thrown security exception");
+        } catch (SecurityException exception) {
+            assertEquals("Property access not allowed during rendering", exception.toString());
+            // pass
+        } finally {
+            manager.dispose();
+        }
+    }
+
+    public void testExit() throws Exception {
+        RenderSecurityManager manager = new RenderSecurityManager(null, null);
+        try {
+            manager.setActive(true);
+
+            System.exit(-1);
+
+            fail("Should have thrown security exception");
+        } catch (SecurityException exception) {
+            assertEquals("Exit access not allowed during rendering (-1)", exception.toString());
+            // pass
+        } finally {
+            manager.dispose();
+        }
+    }
+
+    public void testThread() throws Exception {
+        final AtomicBoolean failedUnexpectedly = new AtomicBoolean(false);
+        Thread otherThread = new Thread("other") {
+            @Override
+            public void run() {
+                try {
+                    assertNull(RenderSecurityManager.getCurrent());
+                    System.getProperties();
+                } catch (SecurityException e) {
+                    failedUnexpectedly.set(true);
+                }
+            }
+        };
+        RenderSecurityManager manager = new RenderSecurityManager(null, null);
+        try {
+            manager.setActive(true);
+
+            // Threads cloned from this one should inherit the same security constraints
+            final AtomicBoolean failedAsExpected = new AtomicBoolean(false);
+            final Thread renderThread = new Thread("render") {
+                @Override
+                public void run() {
+                    try {
+                        System.getProperties();
+                    } catch (SecurityException e) {
+                        failedAsExpected.set(true);
+                    }
+                }
+            };
+            renderThread.start();
+            renderThread.join();
+            assertTrue(failedAsExpected.get());
+            otherThread.start();
+            otherThread.join();
+            assertFalse(failedUnexpectedly.get());
+        } finally {
+            manager.dispose();
+        }
+    }
+
+    public void testActive() throws Exception {
+        RenderSecurityManager manager = new RenderSecurityManager(null, null);
+        try {
+            manager.setActive(true);
+
+            try {
+                System.getProperties();
+                fail("Should have thrown security exception");
+            } catch (SecurityException exception) {
+                // pass
+            }
+
+            manager.setActive(false);
+
+            try {
+                System.getProperties();
+            } catch (SecurityException exception) {
+                fail(exception.toString());
+            }
+
+            manager.setActive(true);
+
+            try {
+                System.getProperties();
+                fail("Should have thrown security exception");
+            } catch (SecurityException exception) {
+                // pass
+            }
+        } finally {
+            manager.dispose();
+        }
+    }
+
+    public void testThread2() throws Exception {
+        // Check that when a new thread is created simultaneously from an unrelated
+        // thread during rendering, that new thread does not pick up the security manager.
+        //
+        final CyclicBarrier barrier1 = new CyclicBarrier(2);
+        final CyclicBarrier barrier2 = new CyclicBarrier(2);
+        final CyclicBarrier barrier3 = new CyclicBarrier(4);
+        final CyclicBarrier barrier4 = new CyclicBarrier(4);
+        final CyclicBarrier barrier5 = new CyclicBarrier(4);
+        final CyclicBarrier barrier6 = new CyclicBarrier(4);
+
+        // First the threads reach barrier1. Then from barrier1 to barrier2, thread1
+        // installs the security manager. Then from barrier2 to barrier3, thread2
+        // checks that it does not have any security restrictions, and creates thread3.
+        // Thread1 will ensure that the security manager is working there, and it will
+        // create thread4. Then after barrier3 (where thread3 and thread4 are now also
+        // participating) thread3 will ensure that it too has no security restrictions,
+        // and thread4 will ensure that it does. At barrier4 the security manager gets
+        // uninstalled, and at barrier5 all threads will check that there are no more
+        // restrictions. At barrier6 all threads are done.
+
+        final Thread thread1 = new Thread("render") {
+            @Override
+            public void run() {
+                try {
+                    barrier1.await();
+                    assertNull(RenderSecurityManager.getCurrent());
+
+                    RenderSecurityManager manager = new RenderSecurityManager(null, null);
+                    manager.setActive(true);
+
+                    barrier2.await();
+
+                    Thread thread4 = new Thread() {
+                        @Override
+                        public void run() {
+                            try {
+                                barrier3.await();
+
+                                try {
+                                    System.getProperties();
+                                    fail("Should have thrown security exception");
+                                } catch (SecurityException e) {
+                                    // pass
+                                }
+
+                                barrier4.await();
+                                barrier5.await();
+                                assertNull(RenderSecurityManager.getCurrent());
+                                assertNull(System.getSecurityManager());
+                                barrier6.await();
+                            } catch (InterruptedException e) {
+                                fail(e.toString());
+                            } catch (BrokenBarrierException e) {
+                                fail(e.toString());
+                            }
+                        }
+                    };
+                    thread4.start();
+
+                    try {
+                        System.getProperties();
+                        fail("Should have thrown security exception");
+                    } catch (SecurityException e) {
+                        // expected
+                    }
+
+                    barrier3.await();
+                    barrier4.await();
+                    manager.dispose();
+
+                    assertNull(RenderSecurityManager.getCurrent());
+                    assertNull(System.getSecurityManager());
+
+                    barrier5.await();
+                    barrier6.await();
+
+                } catch (InterruptedException e) {
+                    fail(e.toString());
+                } catch (BrokenBarrierException e) {
+                    fail(e.toString());
+                }
+
+            }
+        };
+
+        final Thread thread2 = new Thread("unrelated") {
+            @Override
+            public void run() {
+                try {
+                    barrier1.await();
+                    assertNull(RenderSecurityManager.getCurrent());
+                    barrier2.await();
+                    assertNull(RenderSecurityManager.getCurrent());
+                    assertNotNull(System.getSecurityManager());
+
+                    try {
+                        System.getProperties();
+                    } catch (SecurityException e) {
+                        fail("Should not have been affected by security manager");
+                    }
+
+                    Thread thread3 = new Thread() {
+                        @Override
+                        public void run() {
+                            try {
+                                barrier3.await();
+
+                                try {
+                                    System.getProperties();
+                                } catch (SecurityException e) {
+                                    fail("Should not have been affected by security manager");
+                                }
+
+                                barrier4.await();
+                                barrier5.await();
+                                assertNull(RenderSecurityManager.getCurrent());
+                                assertNull(System.getSecurityManager());
+                                barrier6.await();
+
+                            } catch (InterruptedException e) {
+                                fail(e.toString());
+                            } catch (BrokenBarrierException e) {
+                                fail(e.toString());
+                            }
+                        }
+                    };
+                    thread3.start();
+
+                    barrier3.await();
+                    barrier4.await();
+                    barrier5.await();
+                    assertNull(RenderSecurityManager.getCurrent());
+                    assertNull(System.getSecurityManager());
+                    barrier6.await();
+
+                } catch (InterruptedException e) {
+                    fail(e.toString());
+                } catch (BrokenBarrierException e) {
+                    fail(e.toString());
+                }
+
+            }
+        };
+
+        thread1.start();
+        thread2.start();
+        thread1.join();
+        thread2.join();
+    }
+
+    public void testDisabled() throws Exception {
+        assertNull(RenderSecurityManager.getCurrent());
+
+        RenderSecurityManager manager = new RenderSecurityManager(null, null);
+        RenderSecurityManager.sEnabled = false;
+        try {
+            assertNull(RenderSecurityManager.getCurrent());
+            manager.setActive(true);
+            assertSame(manager, RenderSecurityManager.getCurrent());
+            if (new File("/bin/ls").exists()) {
+                Runtime.getRuntime().exec("/bin/ls");
+            } else {
+                manager.checkExec("/bin/ls");
+            }
+        } catch (SecurityException exception) {
+            fail("Should have been disabled");
+        } finally {
+            RenderSecurityManager.sEnabled = true;
+            manager.dispose();
+            assertNull(RenderSecurityManager.getCurrent());
+            assertNull(System.getSecurityManager());
+        }
+    }
+
+    public void testLogger() throws Exception {
+        assertNull(RenderSecurityManager.getCurrent());
+
+        final CyclicBarrier barrier1 = new CyclicBarrier(2);
+        final CyclicBarrier barrier2 = new CyclicBarrier(2);
+        final CyclicBarrier barrier3 = new CyclicBarrier(2);
+
+        Thread thread = new Thread() {
+            @Override
+            public void run() {
+                try {
+                    barrier1.await();
+                    barrier2.await();
+
+                    System.setSecurityManager(new SecurityManager() {
+                        @Override
+                        public String toString() {
+                            return "MyTestSecurityManager";
+                        }
+
+                        @Override
+                        public void checkPermission(Permission permission) {
+                        }
+                    });
+
+                    barrier3.await();
+                    assertNull(RenderSecurityManager.getCurrent());
+                    assertNotNull(System.getSecurityManager());
+                    assertEquals("MyTestSecurityManager", System.getSecurityManager().toString());
+                } catch (InterruptedException e) {
+                    fail(e.toString());
+                } catch (BrokenBarrierException e) {
+                    fail(e.toString());
+                }
+            }
+        };
+        thread.start();
+
+        RenderSecurityManager manager = new RenderSecurityManager(null, null);
+        RecordingLogger logger = new RecordingLogger();
+        manager.setLogger(logger);
+        try {
+            barrier1.await();
+            assertNull(RenderSecurityManager.getCurrent());
+            manager.setActive(true);
+            assertSame(manager, RenderSecurityManager.getCurrent());
+            barrier2.await();
+            barrier3.await();
+
+            assertNull(RenderSecurityManager.getCurrent());
+            manager.setActive(false);
+            assertNull(RenderSecurityManager.getCurrent());
+
+            assertEquals(Collections.singletonList(
+                    "RenderSecurityManager being replaced by another thread"),
+                    logger.getWarningMsgs());
+        } catch (InterruptedException e) {
+            fail(e.toString());
+        } catch (BrokenBarrierException e) {
+            fail(e.toString());
+        } finally {
+            manager.dispose();
+            assertNull(RenderSecurityManager.getCurrent());
+            assertNotNull(System.getSecurityManager());
+            assertEquals("MyTestSecurityManager", System.getSecurityManager().toString());
+            System.setSecurityManager(null);
+        }
+    }
+}
diff --git a/sdk-common/src/test/java/com/android/ide/common/res2/ResourceMergerTest.java b/sdk-common/src/test/java/com/android/ide/common/res2/ResourceMergerTest.java
index cb9bafc..8a64280 100755
--- a/sdk-common/src/test/java/com/android/ide/common/res2/ResourceMergerTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/res2/ResourceMergerTest.java
@@ -49,7 +49,7 @@
     public void testMergeByCount() throws Exception {
         ResourceMerger merger = getResourceMerger();
 
-        assertEquals(27, merger.size());
+        assertEquals(28, merger.size());
     }
 
     public void testMergedResourcesByName() throws Exception {
diff --git a/sdk-common/src/test/java/com/android/ide/common/res2/ResourceSetTest.java b/sdk-common/src/test/java/com/android/ide/common/res2/ResourceSetTest.java
index d7f60be..df38801 100644
--- a/sdk-common/src/test/java/com/android/ide/common/res2/ResourceSetTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/res2/ResourceSetTest.java
@@ -27,7 +27,7 @@
 
     public void testBaseResourceSetByCount() throws Exception {
         ResourceSet resourceSet = getBaseResourceSet();
-        assertEquals(25, resourceSet.size());
+        assertEquals(26, resourceSet.size());
     }
 
     public void testBaseResourceSetByName() throws Exception {
@@ -58,7 +58,8 @@
                 "declare-styleable/declare_styleable",
                 "dimen/dimen",
                 "id/item_id",
-                "integer/integer"
+                "integer/integer",
+                "plurals/plurals"
         );
     }
 
diff --git a/sdk-common/src/test/java/com/android/ide/common/res2/ValueResourceParser2Test.java b/sdk-common/src/test/java/com/android/ide/common/res2/ValueResourceParser2Test.java
index 0d26985..887c144 100644
--- a/sdk-common/src/test/java/com/android/ide/common/res2/ValueResourceParser2Test.java
+++ b/sdk-common/src/test/java/com/android/ide/common/res2/ValueResourceParser2Test.java
@@ -32,7 +32,7 @@
     public void testParsedResourcesByCount() throws Exception {
         List<ResourceItem> resources = getParsedResources();
 
-        assertEquals(20, resources.size());
+        assertEquals(21, resources.size());
     }
 
     public void testParsedResourcesByName() throws Exception {
@@ -61,7 +61,8 @@
                 "dimen/dimen",
                 "id/item_id",
                 "integer/integer",
-                "layout/layout_ref"
+                "layout/layout_ref",
+                "plurals/plurals"
         };
 
         for (String name : resourceNames) {
diff --git a/sdk-common/src/test/java/com/android/ide/common/resources/ResourceResolverTest.java b/sdk-common/src/test/java/com/android/ide/common/resources/ResourceResolverTest.java
index adfe1b7..29616a4 100644
--- a/sdk-common/src/test/java/com/android/ide/common/resources/ResourceResolverTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/resources/ResourceResolverTest.java
@@ -9,6 +9,7 @@
 
 import junit.framework.TestCase;
 
+import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -231,8 +232,17 @@
                 resolver.resolveValue(ResourceType.STRING, "bright_foreground_dark",
                         "@android:color/background_light", true).getValue());
 
+        // themeExtends
+        assertTrue(resolver.themeExtends("@android:style/Theme", "@android:style/Theme"));
+        assertTrue(resolver.themeExtends("@android:style/Theme", "@android:style/Theme.Light"));
+        assertFalse(resolver.themeExtends("@android:style/Theme.Light", "@android:style/Theme"));
+        assertTrue(resolver.themeExtends("@style/MyTheme.Dotted2", "@style/MyTheme.Dotted2"));
+        assertTrue(resolver.themeExtends("@style/MyTheme", "@style/MyTheme.Dotted2"));
+        assertTrue(resolver.themeExtends("@android:style/Theme.Light", "@style/MyTheme.Dotted2"));
+        assertTrue(resolver.themeExtends("@android:style/Theme", "@style/MyTheme.Dotted2"));
+        assertFalse(resolver.themeExtends("@style/MyTheme.Dotted1", "@style/MyTheme.Dotted2"));
 
-        // Switch to MyTheme.Dotted1 (to make sure the parent="" inheritence works properly.)
+        // Switch to MyTheme.Dotted1 (to make sure the parent="" inheritance works properly.)
         // To do that we need to create a new resource resolver.
         resolver = ResourceResolver.create(projectResources, frameworkResources,
                 "MyTheme.Dotted1", true);
@@ -358,4 +368,67 @@
 
         projectRepository.dispose();
     }
+
+    public void testParentCycle() throws IOException {
+        TestResourceRepository projectRepository = TestResourceRepository.create(false,
+                new Object[]{
+                        "values/styles.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<resources>\n"
+                        + "    <style name=\"ButtonStyle.Base\">\n"
+                        + "        <item name=\"android:textColor\">#ff0000</item>\n"
+                        + "    </style>\n"
+                        + "    <style name=\"ButtonStyle\" parent=\"ButtonStyle.Base\">\n"
+                        + "        <item name=\"android:layout_height\">40dp</item>\n"
+                        + "    </style>\n"
+                        + "</resources>\n",
+
+                        "layouts/layout.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                        + "    android:layout_width=\"match_parent\"\n"
+                        + "    android:layout_height=\"match_parent\">\n"
+                        + "\n"
+                        + "    <TextView\n"
+                        + "        style=\"@style/ButtonStyle\"\n"
+                        + "        android:layout_width=\"wrap_content\"\n"
+                        + "        android:layout_height=\"wrap_content\" />\n"
+                        + "\n"
+                        + "</RelativeLayout>\n",
+
+                });
+        assertFalse(projectRepository.isFrameworkRepository());
+        FolderConfiguration config = FolderConfiguration.getConfigForFolder("values-es-land");
+        assertNotNull(config);
+        Map<ResourceType, Map<String, ResourceValue>> projectResources =
+                projectRepository.getConfiguredResources(config);
+        assertNotNull(projectResources);
+        ResourceResolver resolver = ResourceResolver.create(projectResources, projectResources,
+                "ButtonStyle", true);
+        assertNotNull(resolver);
+
+        final AtomicBoolean wasWarned = new AtomicBoolean(false);
+        LayoutLog logger = new LayoutLog() {
+            @Override
+            public void error(String tag, String message, Object data) {
+                assertEquals("Cyclic style parent definitions: \"ButtonStyle\" specifies "
+                        + "parent \"ButtonStyle.Base\" implies parent \"ButtonStyle\"", message);
+                assertEquals(LayoutLog.TAG_BROKEN, tag);
+                wasWarned.set(true);
+            }
+        };
+        resolver.setLogger(logger);
+
+        StyleResourceValue buttonStyle = (StyleResourceValue) resolver.findResValue(
+                "@style/ButtonStyle", false);
+        ResourceValue textColor = resolver.findItemInStyle(buttonStyle, "textColor", true);
+        assertNotNull(textColor);
+        assertEquals("#ff0000", textColor.getValue());
+        assertFalse(wasWarned.get());
+        ResourceValue missing = resolver.findItemInStyle(buttonStyle, "missing", true);
+        assertNull(missing);
+        assertTrue(wasWarned.get());
+
+        projectRepository.dispose();
+    }
 }
diff --git a/sdk-common/src/test/java/com/android/ide/common/resources/configuration/FolderConfigurationTest.java b/sdk-common/src/test/java/com/android/ide/common/resources/configuration/FolderConfigurationTest.java
index 3f850ab..085f0ff 100644
--- a/sdk-common/src/test/java/com/android/ide/common/resources/configuration/FolderConfigurationTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/resources/configuration/FolderConfigurationTest.java
@@ -16,6 +16,7 @@
 
 package com.android.ide.common.resources.configuration;
 
+import com.android.resources.ResourceFolderType;
 import junit.framework.TestCase;
 
 import java.util.ArrayList;
@@ -99,6 +100,15 @@
         assertNull(configForFolder.getLayoutDirectionQualifier());
     }
 
+    public void testToStrings() {
+        FolderConfiguration configForFolder = FolderConfiguration.getConfigForFolder("values-en-rUS");
+        assertNotNull(configForFolder);
+        assertEquals("Locale Language en_Region US", configForFolder.toDisplayString());
+        assertEquals("en,US", configForFolder.toShortDisplayString());
+        assertEquals("layout-en-rUS", configForFolder.getFolderName(ResourceFolderType.LAYOUT));
+        assertEquals("-en-rUS", configForFolder.getUniqueKey());
+    }
+
     // --- helper methods
 
     private final static class MockConfigurable implements Configurable {
diff --git a/sdk-common/src/test/resources/testData/resources/baseSet/values/values.xml b/sdk-common/src/test/resources/testData/resources/baseSet/values/values.xml
index 168813e..c3cdf71 100644
--- a/sdk-common/src/test/resources/testData/resources/baseSet/values/values.xml
+++ b/sdk-common/src/test/resources/testData/resources/baseSet/values/values.xml
@@ -82,4 +82,7 @@
     <item type="layout" name="layout_ref">@layout/ref</item>
     <item type="layout" name="alias_replaced_by_file">@layout/ref</item>
 
+    <plurals name="plurals">
+        <item quantity="one">test2 <xliff:g xmlns="urn:oasis:names:tc:xliff:document:1.2" id="test3">%s</xliff:g> test4</item>
+    </plurals>
 </resources>
diff --git a/sdklib/src/main/java/com/android/sdklib/SdkManager.java b/sdklib/src/main/java/com/android/sdklib/SdkManager.java
index 5169deb..5dd9ece 100644
--- a/sdklib/src/main/java/com/android/sdklib/SdkManager.java
+++ b/sdklib/src/main/java/com/android/sdklib/SdkManager.java
@@ -170,7 +170,10 @@
                 LocalPkgInfo info = pkgsInfos[i];
                 assert info instanceof LocalPlatformPkgInfo;
                 if (info instanceof LocalPlatformPkgInfo) {
-                    targets.add(((LocalPlatformPkgInfo) info).getAndroidTarget());
+                    IAndroidTarget target = ((LocalPlatformPkgInfo) info).getAndroidTarget();
+                    if (target != null) {
+                        targets.add(target);
+                    }
                 }
             }
             mCachedTargets = targets.toArray(new IAndroidTarget[targets.size()]);
diff --git a/sdklib/src/main/java/com/android/sdklib/devices/nexus.xml b/sdklib/src/main/java/com/android/sdklib/devices/nexus.xml
index f3452cb..24fbbf4 100644
--- a/sdklib/src/main/java/com/android/sdklib/devices/nexus.xml
+++ b/sdklib/src/main/java/com/android/sdklib/devices/nexus.xml
@@ -693,4 +693,104 @@
         </d:state>
     </d:device>
 
+  <d:device>
+    <d:name>Nexus 5</d:name>
+    <d:id>Nexus 5</d:id>
+    <d:manufacturer>Google</d:manufacturer>
+    <d:hardware>
+      <d:screen>
+        <d:screen-size>normal</d:screen-size>
+        <d:diagonal-length>4.95</d:diagonal-length>
+        <d:pixel-density>xxhdpi</d:pixel-density>
+        <d:screen-ratio>notlong</d:screen-ratio>
+        <d:dimensions>
+          <d:x-dimension>1080</d:x-dimension>
+          <d:y-dimension>1920</d:y-dimension>
+        </d:dimensions>
+        <d:xdpi>445</d:xdpi>
+        <d:ydpi>445</d:ydpi>
+        <d:touch>
+          <d:multitouch>jazz-hands</d:multitouch>
+          <d:mechanism>finger</d:mechanism>
+          <d:screen-type>capacitive</d:screen-type>
+        </d:touch>
+      </d:screen>
+      <d:networking>
+        Wifi
+        Bluetooth
+        NFC
+      </d:networking>
+      <d:sensors>
+        Accelerometer
+        Barometer
+        Compass
+        GPS
+        Gyroscope
+        LightSensor
+        ProximitySensor
+      </d:sensors>
+      <d:mic>true</d:mic>
+      <d:camera>
+        <d:location>back</d:location>
+        <d:autofocus>true</d:autofocus>
+        <d:flash>true</d:flash>
+      </d:camera>
+      <d:camera>
+        <d:location>front</d:location>
+        <d:autofocus>false</d:autofocus>
+        <d:flash>false</d:flash>
+      </d:camera>
+      <d:keyboard>nokeys</d:keyboard>
+      <d:nav>nonav</d:nav>
+      <d:ram unit="GiB">2</d:ram>
+      <d:buttons>soft</d:buttons>
+      <d:internal-storage unit="GiB">16</d:internal-storage>
+      <d:removable-storage unit="MiB"></d:removable-storage>
+      <d:cpu>Snapdragon 800 (MSM8974)</d:cpu>
+      <d:gpu>Adreno 330</d:gpu>
+      <d:abi>
+        armeabi-v7a
+        armeabi
+      </d:abi>
+      <d:dock></d:dock>
+      <d:power-type>battery</d:power-type>
+    </d:hardware>
+    <d:software>
+      <d:api-level>19</d:api-level>
+      <d:live-wallpaper-support>true</d:live-wallpaper-support>
+      <d:bluetooth-profiles></d:bluetooth-profiles>
+      <d:gl-version>3.0</d:gl-version>
+      <d:gl-extensions>
+        GL_AMD_compressed_ATC_texture GL_AMD_performance_monitor GL_AMD_program_binary_Z400
+        GL_EXT_debug_label GL_EXT_debug_marker GL_EXT_discard_framebuffer
+        GL_EXT_robustness GL_EXT_texture_format_BGRA8888 GL_EXT_texture_type_2_10_10_10_REV
+        GL_NV_fence GL_OES_compressed_ETC1_RGB8_texture GL_OES_depth_texture GL_OES_depth24
+        GL_OES_EGL_image GL_OES_EGL_image_external GL_OES_element_index_uint
+        GL_OES_fbo_render_mipmap GL_OES_fragment_precision_high GL_OES_get_program_binary
+        GL_OES_packed_depth_stencil GL_OES_depth_texture_cube_map GL_OES_rgb8_rgba8
+        GL_OES_standard_derivatives GL_OES_texture_3D GL_OES_texture_float
+        GL_OES_texture_half_float GL_OES_texture_half_float_linear GL_OES_texture_npot
+        GL_OES_vertex_half_float GL_OES_vertex_type_10_10_10_2 GL_OES_vertex_array_object
+        GL_QCOM_alpha_test GL_QCOM_binning_control GL_QCOM_driver_control
+        GL_QCOM_perfmon_global_mode GL_QCOM_extended_get GL_QCOM_extended_get2
+        GL_QCOM_tiled_rendering GL_QCOM_writeonly_rendering GL_EXT_sRGB
+        GL_EXT_texture_filter_anisotropic GL_EXT_color_buffer_float
+        GL_EXT_color_buffer_half_float GL_EXT_disjoint_timer_query
+      </d:gl-extensions>
+      <d:status-bar>true</d:status-bar>
+    </d:software>
+    <d:state name="Portrait" default="true">
+      <d:description>The phone in portrait view</d:description>
+      <d:screen-orientation>port</d:screen-orientation>
+      <d:keyboard-state>keyssoft</d:keyboard-state>
+      <d:nav-state>nonav</d:nav-state>
+    </d:state>
+    <d:state name="Landscape">
+      <d:description>The phone in landscape view</d:description>
+      <d:screen-orientation>land</d:screen-orientation>
+      <d:keyboard-state>keyssoft</d:keyboard-state>
+      <d:nav-state>nonav</d:nav-state>
+    </d:state>
+  </d:device>
+
 </d:devices>
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/build/SignedJarBuilder.java b/sdklib/src/main/java/com/android/sdklib/internal/build/SignedJarBuilder.java
index 506eb49..2dd0a26 100644
--- a/sdklib/src/main/java/com/android/sdklib/internal/build/SignedJarBuilder.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/build/SignedJarBuilder.java
@@ -25,6 +25,7 @@
 import sun.security.x509.AlgorithmId;
 import sun.security.x509.X500Name;
 
+import java.io.BufferedOutputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
@@ -161,7 +162,7 @@
      */
     public SignedJarBuilder(OutputStream out, PrivateKey key, X509Certificate certificate)
             throws IOException, NoSuchAlgorithmException {
-        mOutputJar = new JarOutputStream(out);
+        mOutputJar = new JarOutputStream(new BufferedOutputStream(out));
         mOutputJar.setLevel(9);
         mKey = key;
         mCertificate = certificate;
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/project/ProjectProperties.java b/sdklib/src/main/java/com/android/sdklib/internal/project/ProjectProperties.java
index 5e7cad5..e578747 100644
--- a/sdklib/src/main/java/com/android/sdklib/internal/project/ProjectProperties.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/project/ProjectProperties.java
@@ -23,7 +23,6 @@
 import com.android.io.IAbstractFile;
 import com.android.io.IAbstractFolder;
 import com.android.io.StreamException;
-import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
 import com.android.utils.ILogger;
 import com.google.common.io.Closeables;
 
@@ -77,6 +76,7 @@
     public static final String PROPERTY_RULES_PATH = "layoutrules.jars";
 
     public static final String PROPERTY_SDK = "sdk.dir";
+    public static final String PROPERTY_NDK = "ndk.dir";
     // LEGACY - Kept so that we can actually remove it from local.properties.
     private static final String PROPERTY_SDK_LEGACY = "sdk-location";
 
diff --git a/templates/activities/BlankActivity/globals.xml.ftl b/templates/activities/BlankActivity/globals.xml.ftl
index a926954..0dca7d3 100644
--- a/templates/activities/BlankActivity/globals.xml.ftl
+++ b/templates/activities/BlankActivity/globals.xml.ftl
@@ -1,13 +1,13 @@
 <?xml version="1.0"?>
 <globals>
     <global id="projectOut" value="." />
-    <global id="manifestOut" value="src/main" />
-    <global id="appCompat" value="${(minApi lt 14)?string('1','')}" />
+    <global id="manifestOut" value="${manifestDir}" />
+    <global id="appCompat" value="${(minApiLevel lt 14)?string('1','')}" />
     <!-- e.g. getSupportActionBar vs. getActionBar -->
-    <global id="Support" value="${(minApi lt 14)?string('Support','')}" />
+    <global id="Support" value="${(minApiLevel lt 14)?string('Support','')}" />
     <global id="hasViewPager" value="${(navType == 'pager' || navType == 'tabs')?string('1','')}" />
     <global id="hasSections" value="${(navType == 'pager' || navType == 'tabs' || navType == 'spinner' || navType == 'drawer')?string('1','')}" />
-    <global id="srcOut" value="src/main/java/${slashedPackageName(packageName)}" />
-    <global id="resOut" value="src/main/res" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
+    <global id="resOut" value="${resDir}" />
     <global id="menuName" value="${classToResource(activityClass)}" />
 </globals>
diff --git a/templates/activities/BlankActivity/root/AndroidManifest.xml.ftl b/templates/activities/BlankActivity/root/AndroidManifest.xml.ftl
index b8ae72e..98d8d7b 100644
--- a/templates/activities/BlankActivity/root/AndroidManifest.xml.ftl
+++ b/templates/activities/BlankActivity/root/AndroidManifest.xml.ftl
@@ -1,7 +1,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <application>
-        <activity android:name=".${activityClass}"
+        <activity android:name="${packageName}.${activityClass}"
             <#if isNewProject>
             android:label="@string/app_name"
             <#else>
diff --git a/templates/activities/BlankActivity/root/res/layout/activity_drawer.xml.ftl b/templates/activities/BlankActivity/root/res/layout/activity_drawer.xml.ftl
index 6aed55f..c23b77d 100644
--- a/templates/activities/BlankActivity/root/res/layout/activity_drawer.xml.ftl
+++ b/templates/activities/BlankActivity/root/res/layout/activity_drawer.xml.ftl
@@ -5,7 +5,7 @@
     android:id="@+id/drawer_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    tools:context=".${activityClass}">
+    tools:context="${packageName}.${activityClass}">
 
     <!-- As the main content view, the view below consumes the entire
          space available using match_parent in both dimensions. -->
diff --git a/templates/activities/BlankActivity/root/res/layout/activity_fragment_container.xml.ftl b/templates/activities/BlankActivity/root/res/layout/activity_fragment_container.xml.ftl
index 935d379..da3a7e4 100644
--- a/templates/activities/BlankActivity/root/res/layout/activity_fragment_container.xml.ftl
+++ b/templates/activities/BlankActivity/root/res/layout/activity_fragment_container.xml.ftl
@@ -3,5 +3,5 @@
     android:id="@+id/container"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    tools:context=".${activityClass}"
+    tools:context="${packageName}.${activityClass}"
     tools:ignore="MergeRootFrame" />
diff --git a/templates/activities/BlankActivity/root/res/layout/activity_pager.xml.ftl b/templates/activities/BlankActivity/root/res/layout/activity_pager.xml.ftl
index 2d72cb9..bdaf98c 100644
--- a/templates/activities/BlankActivity/root/res/layout/activity_pager.xml.ftl
+++ b/templates/activities/BlankActivity/root/res/layout/activity_pager.xml.ftl
@@ -3,4 +3,4 @@
     android:id="@+id/pager"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    tools:context=".${activityClass}" />
+    tools:context="${packageName}.${activityClass}" />
diff --git a/templates/activities/BlankActivity/root/res/layout/fragment_simple.xml.ftl b/templates/activities/BlankActivity/root/res/layout/fragment_simple.xml.ftl
index 99bb4b4..db8cc12 100644
--- a/templates/activities/BlankActivity/root/res/layout/fragment_simple.xml.ftl
+++ b/templates/activities/BlankActivity/root/res/layout/fragment_simple.xml.ftl
@@ -6,7 +6,7 @@
     android:paddingRight="@dimen/activity_horizontal_margin"
     android:paddingTop="@dimen/activity_vertical_margin"
     android:paddingBottom="@dimen/activity_vertical_margin"
-    tools:context=".${activityClass}$PlaceholderFragment">
+    tools:context="${packageName}.${activityClass}$PlaceholderFragment">
 
     <TextView
         <#if hasSections?has_content>android:id="@+id/section_label"<#else>android:text="@string/hello_world"</#if>
diff --git a/templates/activities/BlankActivity/root/res/menu/main.xml.ftl b/templates/activities/BlankActivity/root/res/menu/main.xml.ftl
index 3f68e07..054a3dc 100644
--- a/templates/activities/BlankActivity/root/res/menu/main.xml.ftl
+++ b/templates/activities/BlankActivity/root/res/menu/main.xml.ftl
@@ -1,5 +1,7 @@
 <menu xmlns:android="http://schemas.android.com/apk/res/android"<#if appCompat?has_content>
-    xmlns:app="http://schemas.android.com/apk/res-auto"</#if>>
+    xmlns:app="http://schemas.android.com/apk/res-auto"</#if>
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:context="${packageName}.${activityClass}" >
     <#if navType == 'drawer'><item android:id="@+id/action_example"
         android:title="@string/action_example"
         ${(appCompat?has_content)?string('app','android')}:showAsAction="withText|ifRoom" /></#if>
diff --git a/templates/activities/BlankActivity/root/src/app_package/NavigationDrawerFragment.java.ftl b/templates/activities/BlankActivity/root/src/app_package/NavigationDrawerFragment.java.ftl
index 50cbd04..3880362 100644
--- a/templates/activities/BlankActivity/root/src/app_package/NavigationDrawerFragment.java.ftl
+++ b/templates/activities/BlankActivity/root/src/app_package/NavigationDrawerFragment.java.ftl
@@ -77,7 +77,11 @@
 
         // Select either the default item (0) or the last selected item.
         selectItem(mCurrentSelectedPosition);
+    }
 
+    @Override
+    public void onActivityCreated (Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
         // Indicate that this fragment would like to influence the set of actions in the action bar.
         setHasOptionsMenu(true);
     }
@@ -95,7 +99,7 @@
         });
         mDrawerListView.setAdapter(new ArrayAdapter<String>(
                 getActionBar().getThemedContext(),
-                android.R.layout.simple_list_item_<#if minApi gte 11>activated_</#if>1,
+                android.R.layout.simple_list_item_<#if minApiLevel gte 11>activated_</#if>1,
                 android.R.id.text1,
                 new String[]{
                         getString(R.string.title_section1),
@@ -160,7 +164,7 @@
                     mUserLearnedDrawer = true;
                     SharedPreferences sp = PreferenceManager
                             .getDefaultSharedPreferences(getActivity());
-                    sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).${(minApi gte 9)?string('apply','commit')}();
+                    sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).${(minApiLevel gte 9)?string('apply','commit')}();
                 }
 
                 getActivity().${(appCompat?has_content)?string('supportInvalidate','invalidate')}OptionsMenu(); // calls onPrepareOptionsMenu()
@@ -243,10 +247,9 @@
             return true;
         }
 
-        switch (item.getItemId()) {
-            case R.id.action_example:
-                Toast.makeText(getActivity(), "Example action.", Toast.LENGTH_SHORT).show();
-                return true;
+        if (item.getItemId() == R.id.action_example) {
+            Toast.makeText(getActivity(), "Example action.", Toast.LENGTH_SHORT).show();
+            return true;
         }
 
         return super.onOptionsItemSelected(item);
diff --git a/templates/activities/BlankActivity/root/src/app_package/include_options_menu.java.ftl b/templates/activities/BlankActivity/root/src/app_package/include_options_menu.java.ftl
index cf1470e..d99a3d2 100644
--- a/templates/activities/BlankActivity/root/src/app_package/include_options_menu.java.ftl
+++ b/templates/activities/BlankActivity/root/src/app_package/include_options_menu.java.ftl
@@ -20,9 +20,9 @@
         // Handle action bar item clicks here. The action bar will
         // automatically handle clicks on the Home/Up button, so long
         // as you specify a parent activity in AndroidManifest.xml.
-        switch (item.getItemId()) {
-            case R.id.action_settings:
-                return true;
+        int id = item.getItemId();
+        if (id == R.id.action_settings) {
+            return true;
         }
         return super.onOptionsItemSelected(item);
     }
diff --git a/templates/activities/FullscreenActivity/globals.xml.ftl b/templates/activities/FullscreenActivity/globals.xml.ftl
index 6d73e17..d566fee 100644
--- a/templates/activities/FullscreenActivity/globals.xml.ftl
+++ b/templates/activities/FullscreenActivity/globals.xml.ftl
@@ -1,8 +1,8 @@
 <?xml version="1.0"?>
 <globals>
     <global id="projectOut" value="." />
-    <global id="manifestOut" value="." />
-    <global id="srcOut" value="src/${slashedPackageName(packageName)}" />
-    <global id="resOut" value="res" />
+    <global id="manifestOut" value="${manifestDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
+    <global id="resOut" value="${resDir}" />
     <global id="simpleName" value="${activityToLayout(activityClass)}" />
 </globals>
diff --git a/templates/activities/FullscreenActivity/root/AndroidManifest.xml.ftl b/templates/activities/FullscreenActivity/root/AndroidManifest.xml.ftl
index b909732..266df2f 100644
--- a/templates/activities/FullscreenActivity/root/AndroidManifest.xml.ftl
+++ b/templates/activities/FullscreenActivity/root/AndroidManifest.xml.ftl
@@ -1,7 +1,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <application>
-        <activity android:name=".${activityClass}"
+        <activity android:name="${packageName}.${activityClass}"
             <#if isNewProject>
             android:label="@string/app_name"
             <#else>
diff --git a/templates/activities/FullscreenActivity/root/res/layout/activity_fullscreen.xml.ftl b/templates/activities/FullscreenActivity/root/res/layout/activity_fullscreen.xml.ftl
index 39f801a..000b639 100644
--- a/templates/activities/FullscreenActivity/root/res/layout/activity_fullscreen.xml.ftl
+++ b/templates/activities/FullscreenActivity/root/res/layout/activity_fullscreen.xml.ftl
@@ -3,7 +3,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="#0099cc"
-    tools:context=".${activityClass}">
+    tools:context="${packageName}.${activityClass}">
 
     <!-- The primary full-screen view. This can be replaced with whatever view
          is needed to present your content, e.g. VideoView, SurfaceView,
@@ -25,7 +25,7 @@
         android:fitsSystemWindows="true">
 
         <LinearLayout android:id="@+id/fullscreen_content_controls"
-            style="?buttonBarStyle"
+            style="?metaButtonBarStyle"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_gravity="bottom|center_horizontal"
@@ -34,7 +34,7 @@
             tools:ignore="UselessParent">
 
             <Button android:id="@+id/dummy_button"
-                style="?buttonBarButtonStyle"
+                style="?metaButtonBarButtonStyle"
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
                 android:layout_weight="1"
diff --git a/templates/activities/FullscreenActivity/root/res/values-v11/styles.xml b/templates/activities/FullscreenActivity/root/res/values-v11/styles.xml
index feaeb70..f72515d 100644
--- a/templates/activities/FullscreenActivity/root/res/values-v11/styles.xml
+++ b/templates/activities/FullscreenActivity/root/res/values-v11/styles.xml
@@ -4,8 +4,8 @@
         <item name="android:actionBarStyle">@style/FullscreenActionBarStyle</item>
         <item name="android:windowActionBarOverlay">true</item>
         <item name="android:windowBackground">@null</item>
-        <item name="buttonBarStyle">?android:attr/buttonBarStyle</item>
-        <item name="buttonBarButtonStyle">?android:attr/buttonBarButtonStyle</item>
+        <item name="metaButtonBarStyle">?android:attr/buttonBarStyle</item>
+        <item name="metaButtonBarButtonStyle">?android:attr/buttonBarButtonStyle</item>
     </style>
 
     <style name="FullscreenActionBarStyle" parent="android:Widget.Holo.ActionBar">
diff --git a/templates/activities/FullscreenActivity/root/res/values/attrs.xml b/templates/activities/FullscreenActivity/root/res/values/attrs.xml
index 2cf1a1a..7ce840e 100644
--- a/templates/activities/FullscreenActivity/root/res/values/attrs.xml
+++ b/templates/activities/FullscreenActivity/root/res/values/attrs.xml
@@ -5,8 +5,8 @@
          ?android:attr/buttonBarStyle is new as of API 11 so this is
          necessary to support previous API levels. -->
     <declare-styleable name="ButtonBarContainerTheme">
-        <attr name="buttonBarStyle" format="reference" />
-        <attr name="buttonBarButtonStyle" format="reference" />
+        <attr name="metaButtonBarStyle" format="reference" />
+        <attr name="metaButtonBarButtonStyle" format="reference" />
     </declare-styleable>
 
 </resources>
diff --git a/templates/activities/FullscreenActivity/root/res/values/styles.xml b/templates/activities/FullscreenActivity/root/res/values/styles.xml
index 48bb968..e95ba03 100644
--- a/templates/activities/FullscreenActivity/root/res/values/styles.xml
+++ b/templates/activities/FullscreenActivity/root/res/values/styles.xml
@@ -3,8 +3,8 @@
     <style name="FullscreenTheme" parent="android:Theme.NoTitleBar">
         <item name="android:windowContentOverlay">@null</item>
         <item name="android:windowBackground">@null</item>
-        <item name="buttonBarStyle">@style/ButtonBar</item>
-        <item name="buttonBarButtonStyle">@style/ButtonBarButton</item>
+        <item name="metaButtonBarStyle">@style/ButtonBar</item>
+        <item name="metaButtonBarButtonStyle">@style/ButtonBarButton</item>
     </style>
 
     <!-- Backward-compatible version of ?android:attr/buttonBarStyle -->
diff --git a/templates/activities/FullscreenActivity/root/src/app_package/FullscreenActivity.java.ftl b/templates/activities/FullscreenActivity/root/src/app_package/FullscreenActivity.java.ftl
index 4714244..9d6109b 100644
--- a/templates/activities/FullscreenActivity/root/src/app_package/FullscreenActivity.java.ftl
+++ b/templates/activities/FullscreenActivity/root/src/app_package/FullscreenActivity.java.ftl
@@ -145,19 +145,19 @@
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case android.R.id.home:
-                // This ID represents the Home or Up button. In the case of this
-                // activity, the Up button is shown. Use NavUtils to allow users
-                // to navigate up one level in the application structure. For
-                // more details, see the Navigation pattern on Android Design:
-                //
-                // http://developer.android.com/design/patterns/navigation.html#up-vs-back
-                //
-                // TODO: If Settings has multiple levels, Up should navigate up
-                // that hierarchy.
-                NavUtils.navigateUpFromSameTask(this);
-                return true;
+        int id = item.getItemId();
+        if (id == android.R.id.home) {
+            // This ID represents the Home or Up button. In the case of this
+            // activity, the Up button is shown. Use NavUtils to allow users
+            // to navigate up one level in the application structure. For
+            // more details, see the Navigation pattern on Android Design:
+            //
+            // http://developer.android.com/design/patterns/navigation.html#up-vs-back
+            //
+            // TODO: If Settings has multiple levels, Up should navigate up
+            // that hierarchy.
+            NavUtils.navigateUpFromSameTask(this);
+            return true;
         }
         return super.onOptionsItemSelected(item);
     }
diff --git a/templates/activities/FullscreenActivity/template.xml b/templates/activities/FullscreenActivity/template.xml
index d2617fb..cf568ea 100644
--- a/templates/activities/FullscreenActivity/template.xml
+++ b/templates/activities/FullscreenActivity/template.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <template
-    format="3"
-    revision="3"
+    format="4"
+    revision="4"
     name="Fullscreen Activity"
     description="Creates a new activity that toggles the visibility of the system UI (status and navigation bars) and action bar upon user interaction."
     minApi="4"
diff --git a/templates/activities/LoginActivity/globals.xml.ftl b/templates/activities/LoginActivity/globals.xml.ftl
index fbe8985..05c9aad 100644
--- a/templates/activities/LoginActivity/globals.xml.ftl
+++ b/templates/activities/LoginActivity/globals.xml.ftl
@@ -1,9 +1,9 @@
 <?xml version="1.0"?>
 <globals>
     <global id="projectOut" value="." />
-    <global id="manifestOut" value="." />
-    <global id="srcOut" value="src/${slashedPackageName(packageName)}" />
-    <global id="resOut" value="res" />
+    <global id="manifestOut" value="${manifestDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
+    <global id="resOut" value="${resDir}" />
     <global id="menuName" value="${classToResource(activityClass)}" />
     <global id="simpleName" value="${activityToLayout(activityClass)}" />
 </globals>
diff --git a/templates/activities/LoginActivity/root/AndroidManifest.xml.ftl b/templates/activities/LoginActivity/root/AndroidManifest.xml.ftl
index c5f02d2..4f35c79 100644
--- a/templates/activities/LoginActivity/root/AndroidManifest.xml.ftl
+++ b/templates/activities/LoginActivity/root/AndroidManifest.xml.ftl
@@ -1,7 +1,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <application>
-        <activity android:name=".${activityClass}"
+        <activity android:name="${packageName}.${activityClass}"
             <#if isNewProject>
             android:label="@string/app_name"
             <#else>
diff --git a/templates/activities/LoginActivity/root/res/layout/activity_login.xml.ftl b/templates/activities/LoginActivity/root/res/layout/activity_login.xml.ftl
index 9434e5b..9f0fb73 100644
--- a/templates/activities/LoginActivity/root/res/layout/activity_login.xml.ftl
+++ b/templates/activities/LoginActivity/root/res/layout/activity_login.xml.ftl
@@ -1,6 +1,6 @@
 <merge xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    tools:context=".${activityClass}">
+    tools:context="${packageName}.${activityClass}">
 
     <!-- Login progress -->
     <LinearLayout android:id="@+id/login_status"
diff --git a/templates/activities/LoginActivity/root/res/menu/activity_login.xml b/templates/activities/LoginActivity/root/res/menu/activity_login.xml
index 2965794..f39c9a3 100644
--- a/templates/activities/LoginActivity/root/res/menu/activity_login.xml
+++ b/templates/activities/LoginActivity/root/res/menu/activity_login.xml
@@ -1,4 +1,6 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:context="${packageName}.${activityClass}">
     <item android:id="@+id/action_forgot_password"
         android:title="@string/action_forgot_password"
         android:showAsAction="never" />
diff --git a/templates/activities/LoginActivity/root/src/app_package/LoginActivity.java.ftl b/templates/activities/LoginActivity/root/src/app_package/LoginActivity.java.ftl
index 8defdc7..4f28c21 100644
--- a/templates/activities/LoginActivity/root/src/app_package/LoginActivity.java.ftl
+++ b/templates/activities/LoginActivity/root/src/app_package/LoginActivity.java.ftl
@@ -106,19 +106,19 @@
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case android.R.id.home:
-                // This ID represents the Home or Up button. In the case of this
-                // activity, the Up button is shown. Use NavUtils to allow users
-                // to navigate up one level in the application structure. For
-                // more details, see the Navigation pattern on Android Design:
-                //
-                // http://developer.android.com/design/patterns/navigation.html#up-vs-back
-                //
-                // TODO: If Settings has multiple levels, Up should navigate up
-                // that hierarchy.
-                NavUtils.navigateUpFromSameTask(this);
-                return true;
+        int id = item.getItemId();
+        if (id == android.R.id.home) {
+            // This ID represents the Home or Up button. In the case of this
+            // activity, the Up button is shown. Use NavUtils to allow users
+            // to navigate up one level in the application structure. For
+            // more details, see the Navigation pattern on Android Design:
+            //
+            // http://developer.android.com/design/patterns/navigation.html#up-vs-back
+            //
+            // TODO: If Settings has multiple levels, Up should navigate up
+            // that hierarchy.
+            NavUtils.navigateUpFromSameTask(this);
+            return true;
         }
         return super.onOptionsItemSelected(item);
     }
diff --git a/templates/activities/LoginActivity/template.xml b/templates/activities/LoginActivity/template.xml
index 501f1ea..576c633 100644
--- a/templates/activities/LoginActivity/template.xml
+++ b/templates/activities/LoginActivity/template.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <template
-    format="3"
-    revision="3"
+    format="4"
+    revision="4"
     name="Login Activity"
     description="Creates a new login activity, allowing users to enter an email address and password to log in to or register with your application."
     minApi="3"
diff --git a/templates/activities/MasterDetailFlow/globals.xml.ftl b/templates/activities/MasterDetailFlow/globals.xml.ftl
index 415d60e..0d23f55 100644
--- a/templates/activities/MasterDetailFlow/globals.xml.ftl
+++ b/templates/activities/MasterDetailFlow/globals.xml.ftl
@@ -1,9 +1,9 @@
 <?xml version="1.0"?>
 <globals>
     <global id="projectOut" value="." />
-    <global id="manifestOut" value="." />
-    <global id="srcOut" value="src/${slashedPackageName(packageName)}" />
-    <global id="resOut" value="res" />
+    <global id="manifestOut" value="${manifestDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
+    <global id="resOut" value="${resDir}" />
     <global id="CollectionName" value="${extractLetters(objectKind)}List" />
     <global id="collection_name" value="${extractLetters(objectKind?lower_case)}_list" />
     <global id="DetailName" value="${extractLetters(objectKind)}Detail" />
diff --git a/templates/activities/MasterDetailFlow/root/AndroidManifest.xml.ftl b/templates/activities/MasterDetailFlow/root/AndroidManifest.xml.ftl
index 4707bd6..34d0402 100644
--- a/templates/activities/MasterDetailFlow/root/AndroidManifest.xml.ftl
+++ b/templates/activities/MasterDetailFlow/root/AndroidManifest.xml.ftl
@@ -1,7 +1,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
 
     <application>
-        <activity android:name=".${CollectionName}Activity"
+        <activity android:name="${packageName}.${CollectionName}Activity"
             <#if isNewProject>
             android:label="@string/app_name"
             <#else>
@@ -20,11 +20,11 @@
             </#if>
         </activity>
 
-        <activity android:name=".${DetailName}Activity"
+        <activity android:name="${packageName}.${DetailName}Activity"
             android:label="@string/title_${detail_name}"
             <#if buildApi gte 16>android:parentActivityName=".${CollectionName}Activity"</#if>>
             <meta-data android:name="android.support.PARENT_ACTIVITY"
-                android:value=".${CollectionName}Activity" />
+                android:value="${packageName}.${CollectionName}Activity" />
         </activity>
     </application>
 
diff --git a/templates/activities/MasterDetailFlow/root/res/layout/activity_content_detail.xml.ftl b/templates/activities/MasterDetailFlow/root/res/layout/activity_content_detail.xml.ftl
index ddc1ecc..02bf4f6 100644
--- a/templates/activities/MasterDetailFlow/root/res/layout/activity_content_detail.xml.ftl
+++ b/templates/activities/MasterDetailFlow/root/res/layout/activity_content_detail.xml.ftl
@@ -3,5 +3,5 @@
     android:id="@+id/${detail_name}_container"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    tools:context=".${DetailName}Activity"
+    tools:context="${packageName}.${DetailName}Activity"
     tools:ignore="MergeRootFrame" />
diff --git a/templates/activities/MasterDetailFlow/root/res/layout/activity_content_list.xml.ftl b/templates/activities/MasterDetailFlow/root/res/layout/activity_content_list.xml.ftl
index 065cd42..e51a98e 100644
--- a/templates/activities/MasterDetailFlow/root/res/layout/activity_content_list.xml.ftl
+++ b/templates/activities/MasterDetailFlow/root/res/layout/activity_content_list.xml.ftl
@@ -6,5 +6,5 @@
     android:layout_height="match_parent"
     android:layout_marginLeft="16dp"
     android:layout_marginRight="16dp"
-    tools:context=".${CollectionName}Activity"
+    tools:context="${packageName}.${CollectionName}Activity"
     tools:layout="@android:layout/list_content" />
diff --git a/templates/activities/MasterDetailFlow/root/res/layout/activity_content_twopane.xml.ftl b/templates/activities/MasterDetailFlow/root/res/layout/activity_content_twopane.xml.ftl
index 575e9e6..1f2bd19 100644
--- a/templates/activities/MasterDetailFlow/root/res/layout/activity_content_twopane.xml.ftl
+++ b/templates/activities/MasterDetailFlow/root/res/layout/activity_content_twopane.xml.ftl
@@ -8,7 +8,7 @@
     android:divider="?android:attr/dividerHorizontal"
     android:orientation="horizontal"
     android:showDividers="middle"
-    tools:context=".${CollectionName}Activity">
+    tools:context="${packageName}.${CollectionName}Activity">
 
     <!--
     This layout is a two-pane layout for the ${objectKindPlural}
diff --git a/templates/activities/MasterDetailFlow/root/res/layout/fragment_content_detail.xml.ftl b/templates/activities/MasterDetailFlow/root/res/layout/fragment_content_detail.xml.ftl
index 808fc31..f685145 100644
--- a/templates/activities/MasterDetailFlow/root/res/layout/fragment_content_detail.xml.ftl
+++ b/templates/activities/MasterDetailFlow/root/res/layout/fragment_content_detail.xml.ftl
@@ -6,4 +6,4 @@
     android:layout_height="match_parent"
     android:padding="16dp"
     android:textIsSelectable="true"
-    tools:context=".${DetailName}Fragment" />
+    tools:context="${packageName}.${DetailName}Fragment" />
diff --git a/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailActivity.java.ftl b/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailActivity.java.ftl
index 2cc6054..79f0e90 100644
--- a/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailActivity.java.ftl
+++ b/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailActivity.java.ftl
@@ -50,17 +50,17 @@
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case android.R.id.home:
-                // This ID represents the Home or Up button. In the case of this
-                // activity, the Up button is shown. Use NavUtils to allow users
-                // to navigate up one level in the application structure. For
-                // more details, see the Navigation pattern on Android Design:
-                //
-                // http://developer.android.com/design/patterns/navigation.html#up-vs-back
-                //
-                NavUtils.navigateUpTo(this, new Intent(this, ${CollectionName}Activity.class));
-                return true;
+        int id = item.getItemId();
+        if (id == android.R.id.home) {
+            // This ID represents the Home or Up button. In the case of this
+            // activity, the Up button is shown. Use NavUtils to allow users
+            // to navigate up one level in the application structure. For
+            // more details, see the Navigation pattern on Android Design:
+            //
+            // http://developer.android.com/design/patterns/navigation.html#up-vs-back
+            //
+            NavUtils.navigateUpTo(this, new Intent(this, ${CollectionName}Activity.class));
+            return true;
         }
         return super.onOptionsItemSelected(item);
     }
diff --git a/templates/activities/MasterDetailFlow/root/src/app_package/ContentListActivity.java.ftl b/templates/activities/MasterDetailFlow/root/src/app_package/ContentListActivity.java.ftl
index ae73f7d3..fe02fe9 100644
--- a/templates/activities/MasterDetailFlow/root/src/app_package/ContentListActivity.java.ftl
+++ b/templates/activities/MasterDetailFlow/root/src/app_package/ContentListActivity.java.ftl
@@ -60,17 +60,17 @@
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case android.R.id.home:
-                // This ID represents the Home or Up button. In the case of this
-                // activity, the Up button is shown. Use NavUtils to allow users
-                // to navigate up one level in the application structure. For
-                // more details, see the Navigation pattern on Android Design:
-                //
-                // http://developer.android.com/design/patterns/navigation.html#up-vs-back
-                //
-                NavUtils.navigateUpFromSameTask(this);
-                return true;
+        int id = item.getItemId();
+        if (id == android.R.id.home) {
+            // This ID represents the Home or Up button. In the case of this
+            // activity, the Up button is shown. Use NavUtils to allow users
+            // to navigate up one level in the application structure. For
+            // more details, see the Navigation pattern on Android Design:
+            //
+            // http://developer.android.com/design/patterns/navigation.html#up-vs-back
+            //
+            NavUtils.navigateUpFromSameTask(this);
+            return true;
         }
         return super.onOptionsItemSelected(item);
     }
diff --git a/templates/activities/MasterDetailFlow/template.xml b/templates/activities/MasterDetailFlow/template.xml
index 6762321..c2e2b6e 100644
--- a/templates/activities/MasterDetailFlow/template.xml
+++ b/templates/activities/MasterDetailFlow/template.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <template
-    format="3"
-    revision="3"
+    format="4"
+    revision="4"
     name="Master/Detail Flow"
     minApi="11"
     description="Creates a new master/detail flow, allowing users to view a collection of objects as well as details for each object. This flow is presented using two columns on tablet-size screens and one column on handsets and smaller screens. This template creates two activities, a master fragment, and a detail fragment.">
diff --git a/templates/activities/SettingsActivity/globals.xml.ftl b/templates/activities/SettingsActivity/globals.xml.ftl
index 6d73e17..d566fee 100644
--- a/templates/activities/SettingsActivity/globals.xml.ftl
+++ b/templates/activities/SettingsActivity/globals.xml.ftl
@@ -1,8 +1,8 @@
 <?xml version="1.0"?>
 <globals>
     <global id="projectOut" value="." />
-    <global id="manifestOut" value="." />
-    <global id="srcOut" value="src/${slashedPackageName(packageName)}" />
-    <global id="resOut" value="res" />
+    <global id="manifestOut" value="${manifestDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
+    <global id="resOut" value="${resDir}" />
     <global id="simpleName" value="${activityToLayout(activityClass)}" />
 </globals>
diff --git a/templates/activities/SettingsActivity/root/AndroidManifest.xml.ftl b/templates/activities/SettingsActivity/root/AndroidManifest.xml.ftl
index 9f78fcf..a638dd6 100644
--- a/templates/activities/SettingsActivity/root/AndroidManifest.xml.ftl
+++ b/templates/activities/SettingsActivity/root/AndroidManifest.xml.ftl
@@ -1,7 +1,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <application>
-        <activity android:name=".${activityClass}"
+        <activity android:name="${packageName}.${activityClass}"
             <#if isNewProject>
             android:label="@string/app_name"
             <#else>
diff --git a/templates/activities/SettingsActivity/root/src/app_package/SettingsActivity.java.ftl b/templates/activities/SettingsActivity/root/src/app_package/SettingsActivity.java.ftl
index bf67aca..bf4610f 100644
--- a/templates/activities/SettingsActivity/root/src/app_package/SettingsActivity.java.ftl
+++ b/templates/activities/SettingsActivity/root/src/app_package/SettingsActivity.java.ftl
@@ -63,19 +63,19 @@
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case android.R.id.home:
-                // This ID represents the Home or Up button. In the case of this
-                // activity, the Up button is shown. Use NavUtils to allow users
-                // to navigate up one level in the application structure. For
-                // more details, see the Navigation pattern on Android Design:
-                //
-                // http://developer.android.com/design/patterns/navigation.html#up-vs-back
-                //
-                // TODO: If Settings has multiple levels, Up should navigate up
-                // that hierarchy.
-                NavUtils.navigateUpFromSameTask(this);
-                return true;
+        int id = item.getItemId();
+        if (id == android.R.id.home) {
+            // This ID represents the Home or Up button. In the case of this
+            // activity, the Up button is shown. Use NavUtils to allow users
+            // to navigate up one level in the application structure. For
+            // more details, see the Navigation pattern on Android Design:
+            //
+            // http://developer.android.com/design/patterns/navigation.html#up-vs-back
+            //
+            // TODO: If Settings has multiple levels, Up should navigate up
+            // that hierarchy.
+            NavUtils.navigateUpFromSameTask(this);
+            return true;
         }
         return super.onOptionsItemSelected(item);
     }
diff --git a/templates/activities/SettingsActivity/template.xml b/templates/activities/SettingsActivity/template.xml
index 959dc34..33f9413 100644
--- a/templates/activities/SettingsActivity/template.xml
+++ b/templates/activities/SettingsActivity/template.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <template
-    format="3"
-    revision="3"
+    format="4"
+    revision="4"
     name="Settings Activity"
     description="Creates a new application settings activity that presents alternative layouts on handset and tablet-size screens."
     minApi="4"
diff --git a/templates/gradle-projects/NewAndroidApplication/globals.xml.ftl b/templates/gradle-projects/NewAndroidApplication/globals.xml.ftl
deleted file mode 100644
index b7cc47a..0000000
--- a/templates/gradle-projects/NewAndroidApplication/globals.xml.ftl
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0"?>
-<globals>
-    <global id="topOut" value="." />
-    <global id="projectOut" value="." />
-    <global id="appCompat" value="${(minApi lt 14)?string('1','')}" />
-    <global id="manifestOut" value="src/main" />
-    <global id="srcOut" value="src/main/java/${slashedPackageName(packageName)}" />
-    <global id="resOut" value="src/main/res" />
-    <global id="mavenUrl" value="mavenCentral" />
-    <global id="buildToolsVersion" value="18.0.1" />
-    <global id="gradlePluginVersion" value="0.6.+" />
-    <global id="v4SupportLibraryVersion" value="13.0.+" />
-</globals>
diff --git a/templates/gradle-projects/NewAndroidApplication/root/build.gradle.ftl b/templates/gradle-projects/NewAndroidApplication/root/build.gradle.ftl
deleted file mode 100644
index 4e249bd..0000000
--- a/templates/gradle-projects/NewAndroidApplication/root/build.gradle.ftl
+++ /dev/null
@@ -1,39 +0,0 @@
-buildscript {
-    repositories {
-<#if mavenUrl == "mavenCentral">
-        mavenCentral()
-<#else>
-        maven { url '${mavenUrl}' }
-</#if>
-    }
-    dependencies {
-        classpath 'com.android.tools.build:gradle:${gradlePluginVersion}'
-    }
-}
-apply plugin: 'android'
-
-repositories {
-<#if mavenUrl == "mavenCentral">
-    mavenCentral()
-<#else>
-    maven { url '${mavenUrl}' }
-</#if>
-}
-
-android {
-    compileSdkVersion ${buildApi}
-    buildToolsVersion "${buildToolsVersion}"
-
-    defaultConfig {
-        minSdkVersion ${minApi}
-        targetSdkVersion ${targetApi}
-    }
-}
-
-dependencies {
-    <#if dependencyList?? >
-    <#list dependencyList as dependency>
-    compile '${dependency}'
-    </#list>
-    </#if>
-}
diff --git a/templates/gradle-projects/NewAndroidApplication/root/res/drawable-hdpi/ic_launcher.png b/templates/gradle-projects/NewAndroidApplication/root/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 96a442e..0000000
--- a/templates/gradle-projects/NewAndroidApplication/root/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/templates/gradle-projects/NewAndroidApplication/root/res/drawable-mdpi/ic_launcher.png b/templates/gradle-projects/NewAndroidApplication/root/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 359047d..0000000
--- a/templates/gradle-projects/NewAndroidApplication/root/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/templates/gradle-projects/NewAndroidApplication/root/res/drawable-xhdpi/ic_launcher.png b/templates/gradle-projects/NewAndroidApplication/root/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 71c6d76..0000000
--- a/templates/gradle-projects/NewAndroidApplication/root/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/templates/gradle-projects/NewAndroidApplication/root/res/values/strings.xml.ftl b/templates/gradle-projects/NewAndroidApplication/root/res/values/strings.xml.ftl
deleted file mode 100644
index ee03444..0000000
--- a/templates/gradle-projects/NewAndroidApplication/root/res/values/strings.xml.ftl
+++ /dev/null
@@ -1,3 +0,0 @@
-<resources>
-    <string name="app_name">${escapeXmlString(appTitle)}</string>
-</resources>
diff --git a/templates/gradle-projects/NewAndroidApplication/root/settings.gradle.ftl b/templates/gradle-projects/NewAndroidApplication/root/settings.gradle.ftl
deleted file mode 100644
index b12004b..0000000
--- a/templates/gradle-projects/NewAndroidApplication/root/settings.gradle.ftl
+++ /dev/null
@@ -1 +0,0 @@
-include ':${projectName}'
diff --git a/templates/gradle-projects/NewAndroidApplication/template_new_project.png b/templates/gradle-projects/NewAndroidApplication/template_new_project.png
deleted file mode 100644
index 92e8556..0000000
--- a/templates/gradle-projects/NewAndroidApplication/template_new_project.png
+++ /dev/null
Binary files differ
diff --git a/templates/gradle-projects/NewAndroidLibrary/globals.xml.ftl b/templates/gradle-projects/NewAndroidLibrary/globals.xml.ftl
deleted file mode 100644
index 9870768..0000000
--- a/templates/gradle-projects/NewAndroidLibrary/globals.xml.ftl
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<globals>
-    <global id="topOut" value="." />
-    <global id="projectOut" value="." />
-    <global id="manifestOut" value="." />
-    <global id="srcOut" value="src/${slashedPackageName(packageName)}" />
-    <global id="resOut" value="res" />
-    <global id="mavenUrl" value="mavenCentral" />
-    <global id="buildToolsVersion" value="${buildApi}" />
-    <global id="gradlePluginVersion" value="1.0.+" />
-    <global id="v4SupportLibraryVersion" value="13.0.+" />
-</globals>
diff --git a/templates/gradle-projects/NewAndroidLibrary/recipe.xml.ftl b/templates/gradle-projects/NewAndroidLibrary/recipe.xml.ftl
deleted file mode 100644
index 5333bda..0000000
--- a/templates/gradle-projects/NewAndroidLibrary/recipe.xml.ftl
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0"?>
-<recipe>
-    <merge from="settings.gradle.ftl"
-             to="${escapeXmlAttribute(topOut)}/settings.gradle" />
-    <instantiate from="build.gradle.ftl"
-                   to="${escapeXmlAttribute(projectOut)}/build.gradle" />
-    <instantiate from="AndroidManifest.xml.ftl"
-                   to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
-
-<#if copyIcons>
-    <copy from="res/drawable-hdpi"
-            to="${escapeXmlAttribute(resOut)}/drawable-hdpi" />
-    <copy from="res/drawable-mdpi"
-            to="${escapeXmlAttribute(resOut)}/drawable-mdpi" />
-    <copy from="res/drawable-xhdpi"
-            to="${escapeXmlAttribute(resOut)}/drawable-xhdpi" />
-</#if>
-<#if makeIgnore>
-    <copy from="gitignore"
-            to="${escapeXmlAttribute(projectOut)}/.gitignore" />
-</#if>
-    <instantiate from="res/values/styles.xml.ftl"
-                   to="${escapeXmlAttribute(resOut)}/values/styles.xml" />
-<#if buildApi gte 11 && baseTheme != "none">
-    <instantiate from="res/values-v11/styles_hc.xml.ftl"
-                   to="${escapeXmlAttribute(resOut)}/values-v11/styles.xml" />
-</#if>
-<#if buildApi gte 14 && baseTheme?contains("darkactionbar")>
-    <copy from="res/values-v14/styles_ics.xml"
-            to="${escapeXmlAttribute(resOut)}/values-v14/styles.xml" />
-</#if>
-
-    <instantiate from="res/values/strings.xml.ftl"
-                   to="${escapeXmlAttribute(resOut)}/values/strings.xml" />
-</recipe>
diff --git a/templates/gradle-projects/NewAndroidLibrary/root/AndroidManifest.xml.ftl b/templates/gradle-projects/NewAndroidLibrary/root/AndroidManifest.xml.ftl
deleted file mode 100644
index 390a9da..0000000
--- a/templates/gradle-projects/NewAndroidLibrary/root/AndroidManifest.xml.ftl
+++ /dev/null
@@ -1,15 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="${packageName}"
-    android:versionCode="1"
-    android:versionName="1.0">
-
-    <uses-sdk android:minSdkVersion="${minApi}" <#if buildApi gte 4>android:targetSdkVersion="${targetApi}" </#if>/>
-
-    <application <#if minApiLevel gte 4 && buildApi gte 4>android:allowBackup="true"</#if>
-        android:label="@string/app_name"
-        android:icon="@drawable/ic_launcher"<#if baseTheme != "none">
-        android:theme="@style/AppTheme"</#if>>
-
-    </application>
-
-</manifest>
diff --git a/templates/gradle-projects/NewAndroidLibrary/root/build.gradle.ftl b/templates/gradle-projects/NewAndroidLibrary/root/build.gradle.ftl
deleted file mode 100644
index 03161bc..0000000
--- a/templates/gradle-projects/NewAndroidLibrary/root/build.gradle.ftl
+++ /dev/null
@@ -1,34 +0,0 @@
-buildscript {
-    repositories {
-<#if mavenUrl == "mavenCentral">
-        mavenCentral()
-<#else>
-        maven { url '${mavenUrl}' }
-</#if>
-    }
-    dependencies {
-        classpath 'com.android.tools.build:gradle:${gradlePluginVersion}'
-    }
-}
-apply plugin: 'android-library'
-
-repositories {
-<#if mavenUrl == "mavenCentral">
-    mavenCentral()
-<#else>
-    maven { url '${mavenUrl}' }
-</#if>
-}
-
-android {
-    compileSdkVersion ${buildApi}
-    buildToolsVersion "${buildToolsVersion}"
-
-    defaultConfig {
-        minSdkVersion ${minApi}
-        targetSdkVersion ${targetApi}
-    }
-}
-
-dependencies {
-}
diff --git a/templates/gradle-projects/NewAndroidLibrary/root/gitignore b/templates/gradle-projects/NewAndroidLibrary/root/gitignore
deleted file mode 100644
index 796b96d..0000000
--- a/templates/gradle-projects/NewAndroidLibrary/root/gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/templates/gradle-projects/NewAndroidLibrary/root/res/values-v11/styles_hc.xml.ftl b/templates/gradle-projects/NewAndroidLibrary/root/res/values-v11/styles_hc.xml.ftl
deleted file mode 100644
index f8993c3..0000000
--- a/templates/gradle-projects/NewAndroidLibrary/root/res/values-v11/styles_hc.xml.ftl
+++ /dev/null
@@ -1,11 +0,0 @@
-<resources>
-
-    <!--
-        Base application theme for API 11+. This theme completely replaces
-        AppBaseTheme from res/values/styles.xml on API 11+ devices.
-    -->
-    <style name="AppBaseTheme" parent="android:Theme.Holo<#if baseTheme?contains("light")>.Light</#if>">
-        <!-- API 11 theme customizations can go here. -->
-    </style>
-
-</resources>
diff --git a/templates/gradle-projects/NewAndroidLibrary/root/res/values-v14/styles_ics.xml b/templates/gradle-projects/NewAndroidLibrary/root/res/values-v14/styles_ics.xml
deleted file mode 100644
index a91fd03..0000000
--- a/templates/gradle-projects/NewAndroidLibrary/root/res/values-v14/styles_ics.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<resources>
-
-    <!--
-        Base application theme for API 14+. This theme completely replaces
-        AppBaseTheme from BOTH res/values/styles.xml and
-        res/values-v11/styles.xml on API 14+ devices.
-    -->
-    <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
-        <!-- API 14 theme customizations can go here. -->
-    </style>
-
-</resources>
diff --git a/templates/gradle-projects/NewAndroidLibrary/root/res/values/styles.xml.ftl b/templates/gradle-projects/NewAndroidLibrary/root/res/values/styles.xml.ftl
deleted file mode 100644
index 30fe5b5..0000000
--- a/templates/gradle-projects/NewAndroidLibrary/root/res/values/styles.xml.ftl
+++ /dev/null
@@ -1,20 +0,0 @@
-<resources>
-
-    <!--
-        Base application theme, dependent on API level. This theme is replaced
-        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
-    -->
-    <style name="AppBaseTheme" parent="android:Theme<#if baseTheme?contains("light")>.Light</#if>">
-        <!--
-            Theme customizations available in newer API levels can go in
-            res/values-vXX/styles.xml, while customizations related to
-            backward-compatibility can go here.
-        -->
-    </style>
-
-    <!-- Application theme. -->
-    <style name="AppTheme" parent="AppBaseTheme">
-        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
-    </style>
-
-</resources>
diff --git a/templates/gradle-projects/NewAndroidLibrary/template.xml b/templates/gradle-projects/NewAndroidLibrary/template.xml
deleted file mode 100644
index d6b0014..0000000
--- a/templates/gradle-projects/NewAndroidLibrary/template.xml
+++ /dev/null
@@ -1,87 +0,0 @@
-<?xml version="1.0"?>
-<template
-    format="1"
-    revision="3"
-    name="Android Library"
-    description="Creates a new Android library.">
-
-    <thumbs>
-        <thumb>template_new_project.png</thumb>
-    </thumbs>
-
-    <category value="Applications" />
-
-    <parameter
-        id="packageName"
-        name="Package name"
-        type="string"
-        constraints="package|nonempty"
-        default="com.mycompany.myapp" />
-
-    <parameter
-        id="appTitle"
-        name="Library title"
-        type="string"
-        constraints="nonempty"
-        default="My Library"/>
-
-    <parameter
-        id="baseTheme"
-        name="Base Theme"
-        type="enum"
-        default="holo_light_darkactionbar"
-        help="The base user interface theme for the library">
-        <option id="none">None</option>
-        <option id="holo_dark" minBuildApi="11">Holo Dark</option>
-        <option id="holo_light" minBuildApi="11">Holo Light</option>
-        <option id="holo_light_darkactionbar" minBuildApi="14" default="true">Holo Light with Dark Action Bar</option>
-    </parameter>
-
-    <parameter
-        id="minApi"
-        name="Minimum API level"
-        type="string"
-        constraints="apilevel"
-        default="7" />
-
-    <!--
-      Usually the same as minApi, but when minApi is a code name this will be the corresponding
-      API level
-    -->
-    <parameter
-        id="minApiLevel"
-        name="Minimum API level"
-        type="string"
-        constraints="apilevel"
-        default="7" />
-
-    <parameter
-        id="targetApi"
-        name="Target API level"
-        type="string"
-        constraints="apilevel"
-        default="16" />
-
-    <parameter
-        id="buildApi"
-        name="Build API level"
-        type="string"
-        constraints="apilevel"
-        default="16" />
-
-    <parameter
-        id="copyIcons"
-        name="Include launcher icons"
-        type="boolean"
-        default="true" />
-
-    <parameter
-        id="makeIgnore"
-        name="Create .gitignore file"
-        type="boolean"
-        default="true" />
-
-    <globals file="globals.xml.ftl" />
-    <execute file="recipe.xml.ftl" />
-
-</template>
diff --git a/templates/gradle-projects/NewAndroidModule/globals.xml.ftl b/templates/gradle-projects/NewAndroidModule/globals.xml.ftl
new file mode 100644
index 0000000..1843c0d
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidModule/globals.xml.ftl
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<globals>
+    <global id="topOut" value="." />
+    <global id="projectOut" value="." />
+    <global id="appCompat" value="${(minApiLevel lt 14)?string('1','')}" />
+    <global id="manifestOut" value="${manifestDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
+    <global id="resOut" value="${resDir}" />
+    <global id="mavenUrl" value="mavenCentral" />
+    <global id="buildToolsVersion" value="18.0.1" />
+    <global id="gradlePluginVersion" value="0.6.+" />
+    <global id="v4SupportLibraryVersion" value="13.0.+" />
+</globals>
diff --git a/templates/gradle-projects/NewAndroidApplication/recipe.xml.ftl b/templates/gradle-projects/NewAndroidModule/recipe.xml.ftl
similarity index 82%
rename from templates/gradle-projects/NewAndroidApplication/recipe.xml.ftl
rename to templates/gradle-projects/NewAndroidModule/recipe.xml.ftl
index f3bd847..ade222a 100644
--- a/templates/gradle-projects/NewAndroidApplication/recipe.xml.ftl
+++ b/templates/gradle-projects/NewAndroidModule/recipe.xml.ftl
@@ -4,6 +4,10 @@
 
     <#if appCompat?has_content><dependency mavenUrl="com.android.support:appcompat-v7:+"/></#if>
 
+<#if !createActivity>
+    <mkdir at="${srcOut}" />
+</#if>
+
     <merge from="settings.gradle.ftl"
              to="${escapeXmlAttribute(topOut)}/settings.gradle" />
     <instantiate from="build.gradle.ftl"
@@ -22,13 +26,17 @@
             to="${escapeXmlAttribute(resOut)}/drawable-xxhdpi" />
 </#if>
 <#if makeIgnore>
-    <copy from="project_ignore"
-            to="${escapeXmlAttribute(topOut)}/.gitignore" />
     <copy from="module_ignore"
             to="${escapeXmlAttribute(projectOut)}/.gitignore" />
 </#if>
+<#if enableProGuard>
+    <instantiate from="proguard-rules.txt.ftl"
+                   to="${escapeXmlAttribute(projectOut)}/proguard-rules.txt" />
+</#if>
+<#if !(isLibraryProject??) || !isLibraryProject>
     <instantiate from="res/values/styles.xml.ftl"
                    to="${escapeXmlAttribute(resOut)}/values/styles.xml" />
+</#if>
 
     <instantiate from="res/values/strings.xml.ftl"
                    to="${escapeXmlAttribute(resOut)}/values/strings.xml" />
diff --git a/templates/gradle-projects/NewAndroidApplication/root/AndroidManifest.xml.ftl b/templates/gradle-projects/NewAndroidModule/root/AndroidManifest.xml.ftl
similarity index 95%
rename from templates/gradle-projects/NewAndroidApplication/root/AndroidManifest.xml.ftl
rename to templates/gradle-projects/NewAndroidModule/root/AndroidManifest.xml.ftl
index 390a9da..b086306 100644
--- a/templates/gradle-projects/NewAndroidApplication/root/AndroidManifest.xml.ftl
+++ b/templates/gradle-projects/NewAndroidModule/root/AndroidManifest.xml.ftl
@@ -7,7 +7,7 @@
 
     <application <#if minApiLevel gte 4 && buildApi gte 4>android:allowBackup="true"</#if>
         android:label="@string/app_name"
-        android:icon="@drawable/ic_launcher"<#if baseTheme != "none">
+        android:icon="@drawable/ic_launcher"<#if baseTheme != "none" && !isLibraryProject>
         android:theme="@style/AppTheme"</#if>>
 
     </application>
diff --git a/templates/gradle-projects/NewAndroidModule/root/build.gradle.ftl b/templates/gradle-projects/NewAndroidModule/root/build.gradle.ftl
new file mode 100644
index 0000000..951fde0
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidModule/root/build.gradle.ftl
@@ -0,0 +1,71 @@
+buildscript {
+    repositories {
+<#if mavenUrl == "mavenCentral">
+        mavenCentral()
+<#else>
+        maven { url '${mavenUrl}' }
+</#if>
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:${gradlePluginVersion}'
+    }
+}
+<#if isLibraryProject?? && isLibraryProject>
+apply plugin: 'android-library'
+<#else>
+apply plugin: 'android'
+</#if>
+
+repositories {
+<#if mavenUrl == "mavenCentral">
+    mavenCentral()
+<#else>
+    maven { url '${mavenUrl}' }
+</#if>
+}
+
+android {
+    compileSdkVersion ${buildApi}
+    buildToolsVersion "${buildToolsVersion}"
+
+    defaultConfig {
+        minSdkVersion ${minApi}
+        targetSdkVersion ${targetApi}
+    }
+<#if javaVersion?? && javaVersion != "1.6">
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_${javaVersion?replace('.','_','i')}
+        targetCompatibility JavaVersion.VERSION_${javaVersion?replace('.','_','i')}
+    }
+</#if>
+<#if enableProGuard>
+    <#if isLibraryProject>
+    release {
+        runProguard false
+        proguardFile 'proguard-rules.txt'
+        proguardFile getDefaultProguardFile('proguard-android.txt')
+    }
+    <#else>
+    buildTypes {
+        release {
+            runProguard false
+            proguardFile getDefaultProguardFile('proguard-android.txt')
+        }
+    }
+    productFlavors {
+        defaultFlavor {
+            proguardFile 'proguard-rules.txt'
+        }
+    }
+    </#if>
+</#if>
+}
+
+dependencies {
+    <#if dependencyList?? >
+    <#list dependencyList as dependency>
+    compile '${dependency}'
+    </#list>
+    </#if>
+}
diff --git a/templates/gradle-projects/NewAndroidApplication/root/module_ignore b/templates/gradle-projects/NewAndroidModule/root/module_ignore
similarity index 100%
rename from templates/gradle-projects/NewAndroidApplication/root/module_ignore
rename to templates/gradle-projects/NewAndroidModule/root/module_ignore
diff --git a/templates/gradle-projects/NewAndroidModule/root/proguard-rules.txt.ftl b/templates/gradle-projects/NewAndroidModule/root/proguard-rules.txt.ftl
new file mode 100644
index 0000000..f766622
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidModule/root/proguard-rules.txt.ftl
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdkDir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
\ No newline at end of file
diff --git a/templates/gradle-projects/NewAndroidLibrary/root/res/drawable-hdpi/ic_launcher.png b/templates/gradle-projects/NewAndroidModule/root/res/drawable-hdpi/ic_launcher.png
old mode 100755
new mode 100644
similarity index 100%
rename from templates/gradle-projects/NewAndroidLibrary/root/res/drawable-hdpi/ic_launcher.png
rename to templates/gradle-projects/NewAndroidModule/root/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/templates/gradle-projects/NewAndroidLibrary/root/res/drawable-mdpi/ic_launcher.png b/templates/gradle-projects/NewAndroidModule/root/res/drawable-mdpi/ic_launcher.png
old mode 100755
new mode 100644
similarity index 100%
rename from templates/gradle-projects/NewAndroidLibrary/root/res/drawable-mdpi/ic_launcher.png
rename to templates/gradle-projects/NewAndroidModule/root/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/templates/gradle-projects/NewAndroidLibrary/root/res/drawable-xhdpi/ic_launcher.png b/templates/gradle-projects/NewAndroidModule/root/res/drawable-xhdpi/ic_launcher.png
old mode 100755
new mode 100644
similarity index 100%
rename from templates/gradle-projects/NewAndroidLibrary/root/res/drawable-xhdpi/ic_launcher.png
rename to templates/gradle-projects/NewAndroidModule/root/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/templates/gradle-projects/NewAndroidApplication/root/res/drawable-xxhdpi/ic_launcher.png b/templates/gradle-projects/NewAndroidModule/root/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from templates/gradle-projects/NewAndroidApplication/root/res/drawable-xxhdpi/ic_launcher.png
rename to templates/gradle-projects/NewAndroidModule/root/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/templates/gradle-projects/NewAndroidLibrary/root/res/values/strings.xml.ftl b/templates/gradle-projects/NewAndroidModule/root/res/values/strings.xml.ftl
similarity index 100%
rename from templates/gradle-projects/NewAndroidLibrary/root/res/values/strings.xml.ftl
rename to templates/gradle-projects/NewAndroidModule/root/res/values/strings.xml.ftl
diff --git a/templates/gradle-projects/NewAndroidApplication/root/res/values/styles.xml.ftl b/templates/gradle-projects/NewAndroidModule/root/res/values/styles.xml.ftl
similarity index 100%
rename from templates/gradle-projects/NewAndroidApplication/root/res/values/styles.xml.ftl
rename to templates/gradle-projects/NewAndroidModule/root/res/values/styles.xml.ftl
diff --git a/templates/gradle-projects/NewAndroidLibrary/root/settings.gradle.ftl b/templates/gradle-projects/NewAndroidModule/root/settings.gradle.ftl
similarity index 100%
rename from templates/gradle-projects/NewAndroidLibrary/root/settings.gradle.ftl
rename to templates/gradle-projects/NewAndroidModule/root/settings.gradle.ftl
diff --git a/templates/gradle-projects/NewAndroidApplication/template.xml b/templates/gradle-projects/NewAndroidModule/template.xml
similarity index 83%
rename from templates/gradle-projects/NewAndroidApplication/template.xml
rename to templates/gradle-projects/NewAndroidModule/template.xml
index 68e397b..dc0ecfa 100644
--- a/templates/gradle-projects/NewAndroidApplication/template.xml
+++ b/templates/gradle-projects/NewAndroidModule/template.xml
@@ -2,8 +2,8 @@
 <template
     format="4"
     revision="4"
-    name="Android Application"
-    description="Creates a new Android application.">
+    name="Android Module"
+    description="Creates a new Android module.">
 
     <thumbs>
         <thumb>template_new_project.png</thumb>
@@ -20,17 +20,18 @@
 
     <parameter
         id="appTitle"
-        name="Application title"
+        name="Module title"
         type="string"
         constraints="nonempty"
-        default="My Application" />
+        default="My Module" />
 
     <parameter
         id="baseTheme"
         name="Base Theme"
         type="enum"
         default="holo_light_darkactionbar"
-        help="The base user interface theme for the application">
+        help="The base user interface theme for the module">
+        <option id="none">None</option>
         <option id="holo_dark" minBuildApi="11">Holo Dark</option>
         <option id="holo_light" minBuildApi="11">Holo Light</option>
         <option id="holo_light_darkactionbar" minBuildApi="14" default="true">Holo Light with Dark Action Bar</option>
@@ -80,6 +81,12 @@
         type="boolean"
         default="true" />
 
+    <parameter
+        id="enableProGuard"
+        name="Enable ProGuard"
+        type="boolean"
+        default="true" />
+
     <globals file="globals.xml.ftl" />
     <execute file="recipe.xml.ftl" />
 
diff --git a/templates/gradle-projects/NewAndroidLibrary/template_new_project.png b/templates/gradle-projects/NewAndroidModule/template_new_project.png
similarity index 100%
rename from templates/gradle-projects/NewAndroidLibrary/template_new_project.png
rename to templates/gradle-projects/NewAndroidModule/template_new_project.png
Binary files differ
diff --git a/templates/gradle-projects/NewAndroidProject/globals.xml.ftl b/templates/gradle-projects/NewAndroidProject/globals.xml.ftl
new file mode 100644
index 0000000..26e600e
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidProject/globals.xml.ftl
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<globals>
+    <global id="topOut" value="." />
+</globals>
diff --git a/templates/gradle-projects/NewAndroidProject/recipe.xml.ftl b/templates/gradle-projects/NewAndroidProject/recipe.xml.ftl
new file mode 100644
index 0000000..5321458
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidProject/recipe.xml.ftl
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<recipe>
+    <instantiate from="build.gradle.ftl"
+                   to="${escapeXmlAttribute(topOut)}/build.gradle" />
+
+<#if makeIgnore>
+    <copy from="project_ignore"
+            to="${escapeXmlAttribute(topOut)}/.gitignore" />
+</#if>
+
+    <instantiate from="settings.gradle.ftl"
+                   to="${escapeXmlAttribute(topOut)}/settings.gradle" />
+
+    <instantiate from="gradle.properties.ftl"
+                   to="${escapeXmlAttribute(topOut)}/gradle.properties" />
+
+    <copy from="$TEMPLATEDIR/gradle/wrapper"
+        to="${escapeXmlAttribute(topOut)}/" />
+
+<#if sdkDir??>
+  <instantiate from="local.properties.ftl"
+           to="${escapeXmlAttribute(topOut)}/local.properties" />
+</#if>
+</recipe>
diff --git a/templates/gradle-projects/NewAndroidProject/root/build.gradle.ftl b/templates/gradle-projects/NewAndroidProject/root/build.gradle.ftl
new file mode 100644
index 0000000..f7a7ae7
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidProject/root/build.gradle.ftl
@@ -0,0 +1 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
\ No newline at end of file
diff --git a/templates/gradle-projects/NewAndroidProject/root/gradle.properties.ftl b/templates/gradle-projects/NewAndroidProject/root/gradle.properties.ftl
new file mode 100644
index 0000000..5d08ba7
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidProject/root/gradle.properties.ftl
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Settings specified in this file will override any Gradle settings
+# configured through the IDE.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/templates/gradle-projects/NewAndroidProject/root/local.properties.ftl b/templates/gradle-projects/NewAndroidProject/root/local.properties.ftl
new file mode 100644
index 0000000..9dcbf9b
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidProject/root/local.properties.ftl
@@ -0,0 +1,10 @@
+## This file is automatically generated by Android Studio.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file should *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+#
+# Location of the SDK. This is only used by Gradle.
+# For customization when using a Version Control System, please read the
+# header note.
+sdk.dir=${escapePropertyValue(sdkDir)}
\ No newline at end of file
diff --git a/templates/gradle-projects/NewAndroidApplication/root/project_ignore b/templates/gradle-projects/NewAndroidProject/root/project_ignore
similarity index 100%
rename from templates/gradle-projects/NewAndroidApplication/root/project_ignore
rename to templates/gradle-projects/NewAndroidProject/root/project_ignore
diff --git a/templates/gradle-projects/NewAndroidProject/root/settings.gradle.ftl b/templates/gradle-projects/NewAndroidProject/root/settings.gradle.ftl
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidProject/root/settings.gradle.ftl
diff --git a/templates/gradle-projects/NewAndroidProject/template.xml b/templates/gradle-projects/NewAndroidProject/template.xml
new file mode 100644
index 0000000..6ae554a
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidProject/template.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<template
+    format="4"
+    revision="1"
+    name="Android Project"
+    description="Creates a new Android project.">
+
+    <thumbs>
+        <thumb>template_new_project.png</thumb>
+    </thumbs>
+
+    <category value="Projects" />
+
+    <parameter
+        id="makeIgnore"
+        name="Create .gitignore file"
+        type="boolean"
+        default="true" />
+
+    <globals file="globals.xml.ftl" />
+    <execute file="recipe.xml.ftl" />
+
+</template>
diff --git a/templates/gradle-projects/NewAndroidLibrary/template_new_project.png b/templates/gradle-projects/NewAndroidProject/template_new_project.png
similarity index 100%
copy from templates/gradle-projects/NewAndroidLibrary/template_new_project.png
copy to templates/gradle-projects/NewAndroidProject/template_new_project.png
Binary files differ
diff --git a/templates/gradle-projects/NewJavaLibrary/globals.xml.ftl b/templates/gradle-projects/NewJavaLibrary/globals.xml.ftl
index b731454..8d19d26 100644
--- a/templates/gradle-projects/NewJavaLibrary/globals.xml.ftl
+++ b/templates/gradle-projects/NewJavaLibrary/globals.xml.ftl
@@ -2,5 +2,5 @@
 <globals>
     <global id="topOut" value="." />
     <global id="projectOut" value="." />
-    <global id="srcOut" value="src/${slashedPackageName(packageName)}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
 </globals>
diff --git a/templates/gradle-projects/NewJavaLibrary/template.xml b/templates/gradle-projects/NewJavaLibrary/template.xml
index 5d6c526..a9f35c1 100644
--- a/templates/gradle-projects/NewJavaLibrary/template.xml
+++ b/templates/gradle-projects/NewJavaLibrary/template.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <template
-    format="1"
-    revision="3"
+    format="4"
+    revision="4"
     name="Java Library"
     description="Creates a new Java library.">
 
diff --git a/templates/gradle/wrapper/gradle/wrapper/gradle-wrapper.properties b/templates/gradle/wrapper/gradle/wrapper/gradle-wrapper.properties
index 5c22dec..861eddc 100644
--- a/templates/gradle/wrapper/gradle/wrapper/gradle-wrapper.properties
+++ b/templates/gradle/wrapper/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.6-bin.zip
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.8-bin.zip
diff --git a/templates/other/AppWidget/globals.xml.ftl b/templates/other/AppWidget/globals.xml.ftl
index ac85374..65e6905 100644
--- a/templates/other/AppWidget/globals.xml.ftl
+++ b/templates/other/AppWidget/globals.xml.ftl
@@ -1,5 +1,7 @@
 <?xml version="1.0"?>
 <globals>
-    <global id="srcOut" value="src/${slashedPackageName(packageName)}" />
+    <global id="manifestOut" value="${manifestDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
+    <global id="resOut" value="${resDir}" />
     <global id="class_name" value="${camelCaseToUnderscore(className)}" />
 </globals>
diff --git a/templates/other/AppWidget/recipe.xml.ftl b/templates/other/AppWidget/recipe.xml.ftl
index 876b7b0..9db22d2 100644
--- a/templates/other/AppWidget/recipe.xml.ftl
+++ b/templates/other/AppWidget/recipe.xml.ftl
@@ -1,31 +1,36 @@
 <?xml version="1.0"?>
 <recipe>
 
-    <merge from="AndroidManifest.xml.ftl" />
+    <merge from="AndroidManifest.xml.ftl"
+             to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
 
-    <copy from="res/drawable-nodpi/example_appwidget_preview.png" />
+    <copy from="res/drawable-nodpi/example_appwidget_preview.png"
+            to="${escapeXmlAttribute(resOut)}/drawable-nodpi/example_appwidget_preview.png" />
     <instantiate from="res/layout/appwidget.xml"
-                   to="res/layout/${class_name}.xml" />
+                   to="${escapeXmlAttribute(resOut)}/layout/${class_name}.xml" />
 
 
     <#if configurable>
     <instantiate from="res/layout/appwidget_configure.xml"
-                   to="res/layout/${class_name}_configure.xml" />
+                   to="${escapeXmlAttribute(resOut)}/layout/${class_name}_configure.xml" />
     </#if>
 
     <instantiate from="res/xml/appwidget_info.xml.ftl"
-                   to="res/xml/${class_name}_info.xml" />
-    <merge from="res/values/strings.xml.ftl" />
-    <merge from="res/values-v14/dimens.xml" />
-    <merge from="res/values/dimens.xml" />
+                   to="${escapeXmlAttribute(resOut)}/xml/${class_name}_info.xml" />
+    <merge from="res/values/strings.xml.ftl"
+           to="${escapeXmlAttribute(resOut)}/values/strings.xml" />
+    <merge from="res/values-v14/dimens.xml"
+           to="${escapeXmlAttribute(resOut)}/values-v14/dimens.xml" />
+    <merge from="res/values/dimens.xml"
+           to="${escapeXmlAttribute(resOut)}/values/dimens.xml" />
 
     <instantiate from="src/app_package/AppWidget.java.ftl"
-                   to="${srcOut}/${className}.java" />
+                   to="${escapeXmlAttribute(srcOut)}/${className}.java" />
 
     <#if configurable>
     <instantiate from="src/app_package/AppWidgetConfigureActivity.java.ftl"
-                   to="${srcOut}/${className}ConfigureActivity.java" />
+                   to="${escapeXmlAttribute(srcOut)}/${className}ConfigureActivity.java" />
     </#if>
 
-    <open file="${srcOut}/${className}.java" />
+    <open file="${escapeXmlAttribute(srcOut)}/${className}.java" />
 </recipe>
diff --git a/templates/other/AppWidget/root/AndroidManifest.xml.ftl b/templates/other/AppWidget/root/AndroidManifest.xml.ftl
index 8b96d56..bf8f820 100644
--- a/templates/other/AppWidget/root/AndroidManifest.xml.ftl
+++ b/templates/other/AppWidget/root/AndroidManifest.xml.ftl
@@ -3,7 +3,7 @@
 
     <application>
 
-        <receiver android:name=".${className}" >
+        <receiver android:name="${packageName}.${className}" >
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
             </intent-filter>
@@ -14,7 +14,7 @@
         </receiver>
 
     <#if configurable>
-        <activity android:name=".${className}ConfigureActivity" >
+        <activity android:name="${packageName}.${className}ConfigureActivity" >
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
             </intent-filter>
diff --git a/templates/other/AppWidget/template.xml b/templates/other/AppWidget/template.xml
index f071363..d79ef0c 100644
--- a/templates/other/AppWidget/template.xml
+++ b/templates/other/AppWidget/template.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <template
-    format="3"
-    revision="1"
+    format="4"
+    revision="2"
     name="New App Widget"
     description="Creates a new App Widget"
     minApi="4"
diff --git a/templates/other/BlankFragment/globals.xml.ftl b/templates/other/BlankFragment/globals.xml.ftl
index bfc27eb..6e28120 100644
--- a/templates/other/BlankFragment/globals.xml.ftl
+++ b/templates/other/BlankFragment/globals.xml.ftl
@@ -1,4 +1,5 @@
 <?xml version="1.0"?>
 <globals>
-    <global id="srcOut" value="src/${slashedPackageName(packageName)}" />
+    <global id="resOut" value="${resDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
 </globals>
diff --git a/templates/other/BlankFragment/recipe.xml.ftl b/templates/other/BlankFragment/recipe.xml.ftl
index add6d27..0008b9b 100644
--- a/templates/other/BlankFragment/recipe.xml.ftl
+++ b/templates/other/BlankFragment/recipe.xml.ftl
@@ -1,18 +1,19 @@
 <?xml version="1.0"?>
 <recipe>
 
-    <merge from="res/values/strings.xml" />
+    <dependency mavenUrl="com.android.support:support-v4:+"/>
+    <merge from="res/values/strings.xml" to="${escapeXmlAttribute(resOut)}/values/strings.xml" />
 
     <#if includeLayout>
         <instantiate from="res/layout/fragment_blank.xml.ftl"
-                       to="res/layout/fragment_${classToResource(className)}.xml" />
+                       to="${escapeXmlAttribute(resOut)}/layout/fragment_${classToResource(className)}.xml" />
 
-        <open file="res/layout/fragment_${classToResource(className)}.xml" />
+        <open file="${escapeXmlAttribute(resOut)}/layout/fragment_${classToResource(className)}.xml" />
     </#if>
 
-    <open file="${srcOut}/${className}.java" />
+    <open file="${escapeXmlAttribute(srcOut)}/${className}.java" />
 
     <instantiate from="src/app_package/BlankFragment.java.ftl"
-                   to="${srcOut}/${className}.java" />
+                   to="${escapeXmlAttribute(srcOut)}/${className}.java" />
 
 </recipe>
diff --git a/templates/other/BlankFragment/root/res/layout/fragment_blank.xml.ftl b/templates/other/BlankFragment/root/res/layout/fragment_blank.xml.ftl
index 3e04f05..5712aae 100644
--- a/templates/other/BlankFragment/root/res/layout/fragment_blank.xml.ftl
+++ b/templates/other/BlankFragment/root/res/layout/fragment_blank.xml.ftl
@@ -2,7 +2,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    tools:context=".${className}">
+    tools:context="${packageName}.${className}">
 
     <!-- TODO: Update blank fragment layout -->
     <TextView
diff --git a/templates/other/BlankFragment/template.xml b/templates/other/BlankFragment/template.xml
index 9434c18..5ed7c07 100644
--- a/templates/other/BlankFragment/template.xml
+++ b/templates/other/BlankFragment/template.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <template
-    format="3"
-    revision="1"
+    format="4"
+    revision="2"
     name="New Blank Fragment"
     description="Creates a blank fragment that is compatible back to API level 4."
     minApi="7"
diff --git a/templates/other/BroadcastReceiver/globals.xml.ftl b/templates/other/BroadcastReceiver/globals.xml.ftl
index bfc27eb..7beccc1 100644
--- a/templates/other/BroadcastReceiver/globals.xml.ftl
+++ b/templates/other/BroadcastReceiver/globals.xml.ftl
@@ -1,4 +1,5 @@
 <?xml version="1.0"?>
 <globals>
-    <global id="srcOut" value="src/${slashedPackageName(packageName)}" />
+    <global id="manifestOut" value="${manifestDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
 </globals>
diff --git a/templates/other/BroadcastReceiver/recipe.xml.ftl b/templates/other/BroadcastReceiver/recipe.xml.ftl
index a9d2623..d889414 100644
--- a/templates/other/BroadcastReceiver/recipe.xml.ftl
+++ b/templates/other/BroadcastReceiver/recipe.xml.ftl
@@ -1,7 +1,8 @@
 <?xml version="1.0"?>
 <recipe>
-    <merge from="AndroidManifest.xml.ftl" />
+    <merge from="AndroidManifest.xml.ftl"
+             to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
     <instantiate from="src/app_package/BroadcastReceiver.java.ftl"
-                   to="${srcOut}/${className}.java" />
-    <open file="${srcOut}/${className}.java" />
+                   to="${escapeXmlAttribute(srcOut)}/${className}.java" />
+    <open file="${escapeXmlAttribute(srcOut)}/${className}.java" />
 </recipe>
diff --git a/templates/other/BroadcastReceiver/root/AndroidManifest.xml.ftl b/templates/other/BroadcastReceiver/root/AndroidManifest.xml.ftl
index 27b60b0..6849a3b 100644
--- a/templates/other/BroadcastReceiver/root/AndroidManifest.xml.ftl
+++ b/templates/other/BroadcastReceiver/root/AndroidManifest.xml.ftl
@@ -1,7 +1,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <application>
-        <receiver android:name=".${className}"
+        <receiver android:name="${packageName}.${className}"
             android:exported="${isExported?string}"
             android:enabled="${isEnabled?string}" >
         </receiver>
diff --git a/templates/other/BroadcastReceiver/template.xml b/templates/other/BroadcastReceiver/template.xml
index b17851e..159ff75 100644
--- a/templates/other/BroadcastReceiver/template.xml
+++ b/templates/other/BroadcastReceiver/template.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <template
-    format="1"
-    revision="1"
+    format="4"
+    revision="2"
     name="Broadcast Receiver"
     description="Creates a new broadcast receiver component and adds it to your Android manifest.">
 
diff --git a/templates/other/ContentProvider/globals.xml.ftl b/templates/other/ContentProvider/globals.xml.ftl
index bfc27eb..7beccc1 100644
--- a/templates/other/ContentProvider/globals.xml.ftl
+++ b/templates/other/ContentProvider/globals.xml.ftl
@@ -1,4 +1,5 @@
 <?xml version="1.0"?>
 <globals>
-    <global id="srcOut" value="src/${slashedPackageName(packageName)}" />
+    <global id="manifestOut" value="${manifestDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
 </globals>
diff --git a/templates/other/ContentProvider/recipe.xml.ftl b/templates/other/ContentProvider/recipe.xml.ftl
index 6cdad82..9bc3e61 100644
--- a/templates/other/ContentProvider/recipe.xml.ftl
+++ b/templates/other/ContentProvider/recipe.xml.ftl
@@ -1,7 +1,8 @@
 <?xml version="1.0"?>
 <recipe>
-    <merge from="AndroidManifest.xml.ftl" />
+    <merge from="AndroidManifest.xml.ftl"
+             to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
     <instantiate from="src/app_package/ContentProvider.java.ftl"
-                   to="${srcOut}/${className}.java" />
-    <open file="${srcOut}/${className}.java" />
+                   to="${escapeXmlAttribute(srcOut)}/${className}.java" />
+    <open file="${escapeXmlAttribute(srcOut)}/${className}.java" />
 </recipe>
diff --git a/templates/other/ContentProvider/root/AndroidManifest.xml.ftl b/templates/other/ContentProvider/root/AndroidManifest.xml.ftl
index 6fa4afc..819925d 100644
--- a/templates/other/ContentProvider/root/AndroidManifest.xml.ftl
+++ b/templates/other/ContentProvider/root/AndroidManifest.xml.ftl
@@ -1,7 +1,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <application>
-        <provider android:name=".${className}"
+        <provider android:name="${packageName}.${className}"
             android:authorities="${authorities}"
             android:exported="${isExported?string}"
             android:enabled="${isEnabled?string}" >
diff --git a/templates/other/ContentProvider/template.xml b/templates/other/ContentProvider/template.xml
index 21ed1be..dc7e1ea 100644
--- a/templates/other/ContentProvider/template.xml
+++ b/templates/other/ContentProvider/template.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <template
-    format="1"
-    revision="1"
+    format="4"
+    revision="2"
     name="Content Provider"
     description="Creates a new content provider component and adds it to your Android manifest.">
 
diff --git a/templates/other/CustomView/globals.xml.ftl b/templates/other/CustomView/globals.xml.ftl
index d2eeb40..58fe1d0 100644
--- a/templates/other/CustomView/globals.xml.ftl
+++ b/templates/other/CustomView/globals.xml.ftl
@@ -1,5 +1,6 @@
 <?xml version="1.0"?>
 <globals>
-    <global id="srcOut" value="src/${slashedPackageName(packageName)}" />
+    <global id="resOut" value="${resDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
     <global id="view_class" value="${camelCaseToUnderscore(viewClass)}" />
 </globals>
diff --git a/templates/other/CustomView/recipe.xml.ftl b/templates/other/CustomView/recipe.xml.ftl
index d152df0..ce605b2 100644
--- a/templates/other/CustomView/recipe.xml.ftl
+++ b/templates/other/CustomView/recipe.xml.ftl
@@ -1,13 +1,13 @@
 <?xml version="1.0"?>
 <recipe>
     <merge from="res/values/attrs.xml.ftl"
-                   to="res/values/attrs_${view_class}.xml" />
+             to="${escapeXmlAttribute(resOut)}/values/attrs_${view_class}.xml" />
     <instantiate from="res/layout/sample.xml.ftl"
-                   to="res/layout/sample_${view_class}.xml" />
+                   to="${escapeXmlAttribute(resOut)}/layout/sample_${view_class}.xml" />
 
     <instantiate from="src/app_package/CustomView.java.ftl"
-                   to="${srcOut}/${viewClass}.java" />
+                   to="${escapeXmlAttribute(srcOut)}/${viewClass}.java" />
 
-    <open file="${srcOut}/${viewClass}.java" />
-    <open file="res/layout/sample_${view_class}.xml" />
+    <open file="${escapeXmlAttribute(srcOut)}/${viewClass}.java" />
+    <open file="${escapeXmlAttribute(resOut)}/layout/sample_${view_class}.xml" />
 </recipe>
diff --git a/templates/other/CustomView/root/res/layout/sample.xml.ftl b/templates/other/CustomView/root/res/layout/sample.xml.ftl
index 03125b2..7dc4232 100755
--- a/templates/other/CustomView/root/res/layout/sample.xml.ftl
+++ b/templates/other/CustomView/root/res/layout/sample.xml.ftl
@@ -1,6 +1,6 @@
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-<#if !isLibraryProject>
+<#if !isLibraryProject && (!(isGradle??) || !isGradle) >
     xmlns:app="http://schemas.android.com/apk/res/${packageName}"
 <#else>
     xmlns:app="http://schemas.android.com/apk/res-auto"
diff --git a/templates/other/CustomView/template.xml b/templates/other/CustomView/template.xml
index 55f6f3d..5373030 100644
--- a/templates/other/CustomView/template.xml
+++ b/templates/other/CustomView/template.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <template
-    format="1"
-    revision="1"
+    format="4"
+    revision="2"
     name="Custom View"
     description="Creates a new custom view that extends android.view.View and exposes custom attributes.">
 
diff --git a/templates/other/Daydream/globals.xml.ftl b/templates/other/Daydream/globals.xml.ftl
index 04537f9..4d64b36 100644
--- a/templates/other/Daydream/globals.xml.ftl
+++ b/templates/other/Daydream/globals.xml.ftl
@@ -1,6 +1,8 @@
 <?xml version="1.0"?>
 <globals>
-    <global id="srcOut" value="src/${slashedPackageName(packageName)}" />
+    <global id="manifestOut" value="${manifestDir}" />
+    <global id="resOut" value="${resDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
     <global id="class_name" value="${classToResource(className)}" />
     <global id="info_name" value="${classToResource(className)}_info" />
     <global id="settingsClassName"  value="${className}SettingsActivity" />
diff --git a/templates/other/Daydream/recipe.xml.ftl b/templates/other/Daydream/recipe.xml.ftl
index 8db9811..5b99917 100644
--- a/templates/other/Daydream/recipe.xml.ftl
+++ b/templates/other/Daydream/recipe.xml.ftl
@@ -1,26 +1,28 @@
 <?xml version="1.0"?>
 <recipe>
 
-    <merge from="AndroidManifest.xml.ftl" />
-    <merge from="res/values/strings.xml.ftl" />
+    <merge from="AndroidManifest.xml.ftl"
+             to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
+    <merge from="res/values/strings.xml.ftl"
+             to="${escapeXmlAttribute(resOut)}/values/strings.xml" />
 
     <copy from="res/layout-v17/dream.xml"
-          to="res/layout-v17/${class_name}.xml" />
+          to="${escapeXmlAttribute(resOut)}/layout-v17/${class_name}.xml" />
 
     <instantiate from="src/app_package/DreamService.java.ftl"
-                 to="${srcOut}/${className}.java" />
+                 to="${escapeXmlAttribute(srcOut)}/${className}.java" />
 
 <#if configurable>
     <copy from="res/xml/dream_prefs.xml"
-          to="res/xml/${prefs_name}.xml" />
+          to="${escapeXmlAttribute(resOut)}/xml/${prefs_name}.xml" />
 
     <instantiate from="src/app_package/SettingsActivity.java.ftl"
-                 to="${srcOut}/${settingsClassName}.java" />
+                 to="${escapeXmlAttribute(srcOut)}/${settingsClassName}.java" />
 
     <instantiate from="res/xml/xml_dream.xml.ftl"
-                 to="res/xml/${info_name}.xml" />
+                 to="${escapeXmlAttribute(resOut)}/xml/${info_name}.xml" />
 </#if>
 
-    <open file="${srcOut}/${className}.java" />
+    <open file="${escapeXmlAttribute(srcOut)}/${className}.java" />
 
 </recipe>
diff --git a/templates/other/Daydream/root/AndroidManifest.xml.ftl b/templates/other/Daydream/root/AndroidManifest.xml.ftl
index c23bc6e..01beaec 100644
--- a/templates/other/Daydream/root/AndroidManifest.xml.ftl
+++ b/templates/other/Daydream/root/AndroidManifest.xml.ftl
@@ -4,12 +4,12 @@
 
 <#if configurable>
         <activity
-            android:name=".${settingsClassName}" />
+            android:name="${packageName}.${settingsClassName}" />
 </#if>
 
         <!-- This service is only used on devices with API v17+ -->
         <service
-            android:name=".${className}"
+            android:name="${packageName}.${className}"
             android:exported="true" >
             <intent-filter>
                 <action android:name="android.service.dreams.DreamService" />
diff --git a/templates/other/Daydream/template.xml b/templates/other/Daydream/template.xml
index 2014eee..cd292a4 100644
--- a/templates/other/Daydream/template.xml
+++ b/templates/other/Daydream/template.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <template
-    format="3"
-    revision="1"
+    format="4"
+    revision="2"
     name="New Daydream"
     description="Creates a new Daydream service component, for use on devices running Android 4.2 and later."
     minBuildApi="17">
diff --git a/templates/other/IntentService/globals.xml.ftl b/templates/other/IntentService/globals.xml.ftl
index bfc27eb..b44b9f0 100644
--- a/templates/other/IntentService/globals.xml.ftl
+++ b/templates/other/IntentService/globals.xml.ftl
@@ -1,4 +1,4 @@
 <?xml version="1.0"?>
 <globals>
-    <global id="srcOut" value="src/${slashedPackageName(packageName)}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
 </globals>
diff --git a/templates/other/IntentService/recipe.xml.ftl b/templates/other/IntentService/recipe.xml.ftl
index 958f6fd..197fc1f 100644
--- a/templates/other/IntentService/recipe.xml.ftl
+++ b/templates/other/IntentService/recipe.xml.ftl
@@ -1,7 +1,8 @@
 <?xml version="1.0"?>
 <recipe>
-    <merge from="AndroidManifest.xml.ftl" />
+    <merge from="AndroidManifest.xml.ftl"
+             to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
     <instantiate from="src/app_package/IntentService.java.ftl"
-                   to="${srcOut}/${className}.java" />
-    <open file="${srcOut}/${className}.java" />
+                   to="${escapeXmlAttribute(srcOut)}/${className}.java" />
+    <open file="${escapeXmlAttribute(srcOut)}/${className}.java" />
 </recipe>
diff --git a/templates/other/IntentService/root/AndroidManifest.xml.ftl b/templates/other/IntentService/root/AndroidManifest.xml.ftl
index fcc0b66..61b66bb 100644
--- a/templates/other/IntentService/root/AndroidManifest.xml.ftl
+++ b/templates/other/IntentService/root/AndroidManifest.xml.ftl
@@ -1,7 +1,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <application>
-        <service android:name=".${className}"
+        <service android:name="${packageName}.${className}"
             android:exported="false" >
         </service>
     </application>
diff --git a/templates/other/IntentService/template.xml b/templates/other/IntentService/template.xml
index ffe6cd5..3d86c9c 100644
--- a/templates/other/IntentService/template.xml
+++ b/templates/other/IntentService/template.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <template
-    format="3"
-    revision="1"
+    format="4"
+    revision="2"
     name="New IntentService"
     description="Creates a new intent service class."
     minApi="3"
diff --git a/templates/other/ListFragment/globals.xml.ftl b/templates/other/ListFragment/globals.xml.ftl
index 577250d..29dbb98 100644
--- a/templates/other/ListFragment/globals.xml.ftl
+++ b/templates/other/ListFragment/globals.xml.ftl
@@ -1,6 +1,7 @@
 <?xml version="1.0"?>
 <globals>
-    <global id="srcOut" value="src/${slashedPackageName(packageName)}" />
+    <global id="resOut" value="${resDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
     <global id="collection_name" value="${extractLetters(objectKind?lower_case)}" />
     <global id="className" value="${extractLetters(objectKind)}Fragment" />
     <global id="fragment_layout" value="fragment_${extractLetters(objectKind?lower_case)}" />
diff --git a/templates/other/ListFragment/recipe.xml.ftl b/templates/other/ListFragment/recipe.xml.ftl
index 5db4fdb..7be60e9 100644
--- a/templates/other/ListFragment/recipe.xml.ftl
+++ b/templates/other/ListFragment/recipe.xml.ftl
@@ -1,26 +1,28 @@
 <?xml version="1.0"?>
 <recipe>
 
+    <dependency mavenUrl="com.android.support:support-v4:+"/>
 <#if switchGrid == true>
-    <merge from="res/values/refs.xml.ftl" />
+    <merge from="res/values/refs.xml.ftl"
+             to="${escapeXmlAttribute(resOut)}/values/refs.xml" />
     <merge from="res/values/refs_lrg.xml.ftl"
-           to="res/values-large/refs.xml" />
+           to="${escapeXmlAttribute(resOut)}/values-large/refs.xml" />
     <merge from="res/values/refs_lrg.xml.ftl"
-           to="res/values-sw600dp/refs.xml" />
+           to="${escapeXmlAttribute(resOut)}/values-sw600dp/refs.xml" />
 
     <instantiate from="res/layout/fragment_grid.xml"
-                 to="res/layout/${fragment_layout}_grid.xml" />
+                 to="${escapeXmlAttribute(resOut)}/layout/${fragment_layout}_grid.xml" />
 
     <instantiate from="res/layout/fragment_list.xml"
-                 to="res/layout/${fragment_layout}_list.xml" />
+                 to="${escapeXmlAttribute(resOut)}/layout/${fragment_layout}_list.xml" />
 </#if>
 
     <instantiate from="src/app_package/ListFragment.java.ftl"
-                 to="${srcOut}/${className}.java" />
+                 to="${escapeXmlAttribute(srcOut)}/${className}.java" />
 
     <instantiate from="src/app_package/dummy/DummyContent.java.ftl"
-                 to="${srcOut}/dummy/DummyContent.java" />
+                 to="${escapeXmlAttribute(srcOut)}/dummy/DummyContent.java" />
 
-    <open file="${srcOut}/${className}.java" />
+    <open file="${escapeXmlAttribute(srcOut)}/${className}.java" />
 
 </recipe>
diff --git a/templates/other/ListFragment/root/res/layout/fragment_grid.xml b/templates/other/ListFragment/root/res/layout/fragment_grid.xml
index 87ae969..55c0044 100644
--- a/templates/other/ListFragment/root/res/layout/fragment_grid.xml
+++ b/templates/other/ListFragment/root/res/layout/fragment_grid.xml
@@ -3,7 +3,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    tools:context=".${className}">
+    tools:context="${packageName}.${className}">
 
     <GridView
         android:id="@android:id/list"
diff --git a/templates/other/ListFragment/root/res/layout/fragment_list.xml b/templates/other/ListFragment/root/res/layout/fragment_list.xml
index 5b0e178..e37a0a6 100644
--- a/templates/other/ListFragment/root/res/layout/fragment_list.xml
+++ b/templates/other/ListFragment/root/res/layout/fragment_list.xml
@@ -3,7 +3,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    tools:context=".${className}">
+    tools:context="${packageName}.${className}">
 
     <ListView
         android:id="@android:id/list"
diff --git a/templates/other/ListFragment/template.xml b/templates/other/ListFragment/template.xml
index e00257c..81eabad 100644
--- a/templates/other/ListFragment/template.xml
+++ b/templates/other/ListFragment/template.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <template
-    format="3"
-    revision="1"
+    format="4"
+    revision="2"
     name="New List Fragment"
     description="Creates a new empty fragment containing a list that can optionally change to a grid when on large screens. Compatible back to API level 4."
     minApi="7"
diff --git a/templates/other/Notification/globals.xml.ftl b/templates/other/Notification/globals.xml.ftl
index b302aa9..93d7472 100644
--- a/templates/other/Notification/globals.xml.ftl
+++ b/templates/other/Notification/globals.xml.ftl
@@ -1,9 +1,10 @@
 <?xml version="1.0"?>
 <globals>
-    <global id="srcOut" value="src/${slashedPackageName(packageName)}" />
+    <global id="manifestOut" value="${manifestDir}" />
+    <global id="resOut" value="${resDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
     <global id="notificationName" value="${className?replace('notification','','i')}" />
     <global id="notification_name" value="${camelCaseToUnderscore(className?replace('notification','','i'))}" />
     <global id="display_title" value="${camelCaseToUnderscore(className?replace('notification','','i'))?replace('_',' ')?cap_first}" />
-
     <global id="icon_resource" value="ic_stat_${camelCaseToUnderscore(className?replace('notification','','i'))}" />
 </globals>
diff --git a/templates/other/Notification/recipe.xml.ftl b/templates/other/Notification/recipe.xml.ftl
index bd1c265..1f1afec 100644
--- a/templates/other/Notification/recipe.xml.ftl
+++ b/templates/other/Notification/recipe.xml.ftl
@@ -1,25 +1,31 @@
 <?xml version="1.0"?>
 <recipe>
 
-    <merge from="AndroidManifest.xml.ftl" />
+    <dependency mavenUrl="com.android.support:support-v4:+"/>
+    <merge from="AndroidManifest.xml.ftl"
+             to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
 
     <#if expandedStyle == "picture">
     <copy from="res/drawable-nodpi/example_picture_large.png"
-            to="res/drawable-nodpi/example_picture.png" />
+            to="${escapeXmlAttribute(resOut)}/drawable-nodpi/example_picture.png" />
     <#else>
     <copy from="res/drawable-nodpi/example_picture_small.png"
-            to="res/drawable-nodpi/example_picture.png" />
+            to="${escapeXmlAttribute(resOut)}/drawable-nodpi/example_picture.png" />
     </#if>
 
     <#if moreActions>
-    <copy from="res/drawable-hdpi" />
-    <copy from="res/drawable-mdpi" />
-    <copy from="res/drawable-xhdpi" />
+    <copy from="res/drawable-hdpi"
+            to="${escapeXmlAttribute(resOut)}/drawable-hdpi" />
+    <copy from="res/drawable-mdpi"
+            to="${escapeXmlAttribute(resOut)}/drawable-mdpi" />
+    <copy from="res/drawable-xhdpi"
+            to="${escapeXmlAttribute(resOut)}/drawable-xhdpi" />
     </#if>
 
-    <merge from="res/values/strings.xml.ftl" />
+    <merge from="res/values/strings.xml.ftl"
+             to="${escapeXmlAttribute(resOut)}/values/strings.xml" />
 
     <instantiate from="src/app_package/NotificationHelper.java.ftl"
-                   to="${srcOut}/${className}.java" />
-    <open file="${srcOut}/${className}.java" />
+                   to="${escapeXmlAttribute(srcOut)}/${className}.java" />
+    <open file="${escapeXmlAttribute(srcOut)}/${className}.java" />
 </recipe>
diff --git a/templates/other/Notification/template.xml b/templates/other/Notification/template.xml
index 61fbc59..b9479b2 100644
--- a/templates/other/Notification/template.xml
+++ b/templates/other/Notification/template.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <template
-    format="3"
-    revision="1"
+    format="4"
+    revision="2"
     name="New Notification"
     description="Creates a new helper class that can show or cancel a status bar notification."
     minApi="4">
diff --git a/templates/other/Service/globals.xml.ftl b/templates/other/Service/globals.xml.ftl
index bfc27eb..7beccc1 100644
--- a/templates/other/Service/globals.xml.ftl
+++ b/templates/other/Service/globals.xml.ftl
@@ -1,4 +1,5 @@
 <?xml version="1.0"?>
 <globals>
-    <global id="srcOut" value="src/${slashedPackageName(packageName)}" />
+    <global id="manifestOut" value="${manifestDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
 </globals>
diff --git a/templates/other/Service/recipe.xml.ftl b/templates/other/Service/recipe.xml.ftl
index 6e4bd57..9e66da5 100644
--- a/templates/other/Service/recipe.xml.ftl
+++ b/templates/other/Service/recipe.xml.ftl
@@ -1,7 +1,8 @@
 <?xml version="1.0"?>
 <recipe>
-    <merge from="AndroidManifest.xml.ftl" />
+    <merge from="AndroidManifest.xml.ftl"
+             to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
     <instantiate from="src/app_package/Service.java.ftl"
-                   to="${srcOut}/${className}.java" />
-    <open file="${srcOut}/${className}.java" />
+                   to="${escapeXmlAttribute(srcOut)}/${className}.java" />
+    <open file="${escapeXmlAttribute(srcOut)}/${className}.java" />
 </recipe>
diff --git a/templates/other/Service/root/AndroidManifest.xml.ftl b/templates/other/Service/root/AndroidManifest.xml.ftl
index 14b0bce..0f747ea 100644
--- a/templates/other/Service/root/AndroidManifest.xml.ftl
+++ b/templates/other/Service/root/AndroidManifest.xml.ftl
@@ -1,7 +1,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <application>
-        <service android:name=".${className}"
+        <service android:name="${packageName}.${className}"
             android:exported="${isExported?string}"
             android:enabled="${isEnabled?string}" >
         </service>
diff --git a/templates/other/Service/template.xml b/templates/other/Service/template.xml
index 481fe74..de5b36c 100644
--- a/templates/other/Service/template.xml
+++ b/templates/other/Service/template.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <template
-    format="1"
-    revision="1"
+    format="4"
+    revision="2"
     name="Service"
     description="Creates a new service component and adds it to your Android manifest.">